Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for atlas cached images #15

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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