Skip to content

Commit

Permalink
Add support for atlas cached images
Browse files Browse the repository at this point in the history
  • Loading branch information
dzhou121 authored and Zoxc committed Jan 7, 2024
1 parent f12f4b6 commit 1c6d766
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 62 deletions.
46 changes: 40 additions & 6 deletions src/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ struct ImageData {
data: Vec<u8>,
}

pub enum AtlasContent {
Mask,
Color,
}

pub struct Atlas {
packer: Packer,
new_data: Vec<ImageData>,
pub atlas_texture: wgpu::Texture,
area_used: i32,
did_clear: bool,
content: AtlasContent,
}

impl Atlas {
Expand Down Expand Up @@ -50,15 +56,37 @@ impl Atlas {
}
}

pub fn new(device: &wgpu::Device) -> Self {
let atlas_texture = device.create_texture(&Self::get_texture_desc());
pub fn new(device: &wgpu::Device, content: AtlasContent) -> Self {
let texture_size = wgpu::Extent3d {
width: Atlas::ATLAS_SIZE,
height: Atlas::ATLAS_SIZE,
depth_or_array_layers: 1,
};
let format = match content {
AtlasContent::Mask => wgpu::TextureFormat::R8Unorm,
AtlasContent::Color => wgpu::TextureFormat::Rgba8Unorm,
};
let desc = wgpu::TextureDescriptor {
size: texture_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::TEXTURE_BINDING,
label: Some("atlas_texture"),
view_formats: &[format],
};
let atlas_texture = device.create_texture(&desc);

Self {
packer: Packer::new(Atlas::get_packer_config()),
new_data: vec![],
atlas_texture,
area_used: 0,
did_clear: false,
content,
}
}

Expand All @@ -78,10 +106,15 @@ impl Atlas {
}

pub fn update(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
let pixels = match self.content {
AtlasContent::Mask => 1,
AtlasContent::Color => 4,
};

if self.did_clear {
// encoder.clear_texture(&self.atlas_texture, &wgpu::ImageSubresourceRange::default());

let sz = Atlas::ATLAS_SIZE as usize;
let sz = Atlas::ATLAS_SIZE as usize * pixels;

let data = vec![0_u8; sz * sz];

Expand Down Expand Up @@ -121,14 +154,15 @@ impl Atlas {
for data in &self.new_data {
// Pad data to wgpu::COPY_BYTES_PER_ROW_ALIGNMENT
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as i32;
let padding = (align - data.rect.width % align) % align;
let padded_width = data.rect.width + padding;
let width = data.rect.width * pixels as i32;
let padding = (align - width % align) % align;
let padded_width = width + padding;
let mut padded_data = vec![];
padded_data.reserve((padded_width * data.rect.height) as usize);

let mut i = 0;
for _ in 0..data.rect.height {
for _ in 0..data.rect.width {
for _ in 0..width {
padded_data.push(data.data[i]);
i += 1;
}
Expand Down
115 changes: 92 additions & 23 deletions src/glyphs.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,110 @@
use crate::atlas::Atlas;
use crate::atlas::{Atlas, AtlasContent};
use rect_packer::Rect;
use std::collections::HashMap;
use std::{
collections::HashMap,
hash::{Hash, Hasher},
sync::Arc,
};

#[derive(Copy, Clone, Debug)]
pub struct GlyphInfo {
pub rect: Option<Rect>,
pub metrics: fontdue::Metrics,
}

pub struct GlyphCache {
pub atlas: Atlas,
#[derive(Clone, Eq)]
pub struct ImageId {
id: Arc<bool>,
}

impl ImageId {
pub fn new() -> Self {
Self {
id: Arc::new(false),
}
}
}

impl PartialEq for ImageId {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.id, &other.id)
}
}

impl Hash for ImageId {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
(Arc::as_ptr(&self.id) as usize).hash(state)
}
}

#[derive(Copy, Clone, Debug)]
pub struct AtlasImage {
pub rect: Option<Rect>,
}

pub struct AtlasCache {
pub mask_atlas: Atlas,
pub color_atlas: Atlas,
pub font: fontdue::Font,
info: HashMap<(char, u32), GlyphInfo>,
glyphs: HashMap<(char, u32), GlyphInfo>,
mask_images: HashMap<ImageId, AtlasImage>,
color_images: HashMap<ImageId, AtlasImage>,
}

impl GlyphCache {
impl AtlasCache {
pub fn new(device: &wgpu::Device) -> Self {
let mut settings = fontdue::FontSettings::default();
settings.collection_index = 0;
settings.scale = 100.0;
let font = include_bytes!("fonts/Anodina-Regular.ttf") as &[u8];

Self {
atlas: Atlas::new(device),
mask_atlas: Atlas::new(device, AtlasContent::Mask),
color_atlas: Atlas::new(device, AtlasContent::Color),
font: fontdue::Font::from_bytes(font, settings).unwrap(),
info: HashMap::new(),
glyphs: HashMap::new(),
mask_images: HashMap::new(),
color_images: HashMap::new(),
}
}

pub fn get_mask_image(
&mut self,
id: ImageId,
width: u32,
height: u32,
image: impl FnOnce() -> Vec<u8>,
) -> AtlasImage {
let mask_atlas = &mut self.mask_atlas;
*self.mask_images.entry(id).or_insert_with(|| AtlasImage {
rect: mask_atlas.add_region(&image(), width, height),
})
}

pub fn get_color_image(
&mut self,
id: ImageId,
width: u32,
height: u32,
image: impl FnOnce() -> Vec<u8>,
) -> AtlasImage {
let color_atlas = &mut self.color_atlas;
*self.color_images.entry(id).or_insert_with(|| AtlasImage {
rect: color_atlas.add_region(&image(), width, height),
})
}

pub fn get_glyph(&mut self, c: char, size: f32) -> GlyphInfo {
let factor = 65536.0;

// Convert size to fixed point so we can hash it.
let size_fixed_point = (size * factor) as u32;

// Do we already have a glyph?
match self.info.get(&(c, size_fixed_point)) {
match self.glyphs.get(&(c, size_fixed_point)) {
Some(info) => *info,
None => {
let (metrics, data) = self.font.rasterize(c, size_fixed_point as f32 / factor);
Expand All @@ -52,31 +121,31 @@ impl GlyphCache {
*/

let rect =
self.atlas
self.mask_atlas
.add_region(&data, metrics.width as u32, metrics.height as u32);

let info = GlyphInfo { rect, metrics };

self.info.insert((c, size_fixed_point), info);
self.glyphs.insert((c, size_fixed_point), info);
info
}
}
}

pub fn update(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
self.atlas.update(device, encoder);
self.mask_atlas.update(device, encoder);
self.color_atlas.update(device, encoder);
}

pub fn create_view(&self) -> wgpu::TextureView {
self.atlas.create_view()
}

pub fn usage(&self) -> f32 {
self.atlas.usage()
}

pub fn clear(&mut self) {
self.info.clear();
self.atlas.clear();
pub fn check_usage(&mut self) {
if self.mask_atlas.usage() > 0.7 {
self.glyphs.clear();
self.mask_atlas.clear();
self.mask_images.clear();
}
if self.color_atlas.usage() > 0.7 {
self.color_atlas.clear();
self.color_images.clear();
}
}
}
Loading

0 comments on commit 1c6d766

Please sign in to comment.