use std::collections::HashMap;
use cosmic_text::CacheKey;
use etagere::{AtlasAllocator, size2};
pub struct GlyphAtlas {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
allocator: AtlasAllocator,
cached: HashMap<CacheKey, crate::types::glyph::AtlasEntry>,
size: u32,
}
impl GlyphAtlas {
pub fn new(device: &wgpu::Device, size: u32) -> Self {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Glyph Atlas"),
size: wgpu::Extent3d {
width: size,
height: size,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
Self {
texture,
view,
sampler,
allocator: AtlasAllocator::new(size2(size as i32, size as i32)),
cached: HashMap::new(),
size,
}
}
pub fn get(&self, key: &CacheKey) -> Option<&crate::types::glyph::AtlasEntry> {
self.cached.get(key)
}
pub fn insert(&mut self, queue: &wgpu::Queue, key: CacheKey, glyph: &crate::types::glyph::RasterizedGlyph) -> crate::types::glyph::AtlasEntry {
let width = glyph.width;
let height = glyph.height;
if width == 0 || height == 0 {
return crate::types::glyph::AtlasEntry {
uv_pos: [0.0, 0.0],
uv_size: [0.0, 0.0],
pixel_size: [0.0, 0.0],
pixel_offset: [0.0, 0.0],
is_color: false,
};
}
let pad = 1;
let alloc_width = width as i32 + (pad * 2);
let alloc_height = height as i32 + (pad * 2);
let allocation = self.allocator.allocate(size2(alloc_width, alloc_height))
.expect("Atlas out of space");
let rect = allocation.rectangle;
let x = rect.min.x as u32 + pad as u32;
let y = rect.min.y as u32 + pad as u32;
let rgba_data = if glyph.is_color {
glyph.data.clone()
} else {
let mut data = Vec::with_capacity(glyph.data.len() * 4);
for &alpha in &glyph.data {
data.push(255);
data.push(255);
data.push(255);
data.push(alpha);
}
data
};
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d { x, y, z: 0 },
aspect: wgpu::TextureAspect::All,
},
&rgba_data,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(width * 4),
rows_per_image: Some(height),
},
wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
);
let entry = crate::types::glyph::AtlasEntry {
uv_pos: [
x as f32 / self.size as f32,
y as f32 / self.size as f32,
],
uv_size: [
width as f32 / self.size as f32,
height as f32 / self.size as f32,
],
pixel_size: [width as f32, height as f32],
pixel_offset: [glyph.left as f32, glyph.top as f32],
is_color: glyph.is_color,
};
self.cached.insert(key, entry);
entry
}
pub fn clear(&mut self) {
self.allocator.clear();
self.cached.clear();
}
}