use ahash::AHashMap;
#[cfg(feature = "nostd")]
use alloc::{string::String, vec, vec::Vec};
#[cfg(not(feature = "nostd"))]
use std::{string::String, vec::Vec};
pub struct GlyphCache {
cache: AHashMap<GlyphKey, CachedGlyph>,
max_entries: usize,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct GlyphKey {
pub font_family: String,
pub glyph_id: u16,
pub size: u32,
pub style_flags: u32,
}
pub struct CachedGlyph {
pub bitmap: Vec<u8>,
pub width: u32,
pub height: u32,
pub advance: f32,
pub bearing_x: f32,
pub bearing_y: f32,
}
impl GlyphCache {
pub fn new(max_entries: usize) -> Self {
Self {
cache: AHashMap::new(),
max_entries,
}
}
pub fn get(&self, key: &GlyphKey) -> Option<&CachedGlyph> {
self.cache.get(key)
}
pub fn insert(&mut self, key: GlyphKey, glyph: CachedGlyph) {
if self.cache.len() >= self.max_entries {
if let Some(first_key) = self.cache.keys().next().cloned() {
self.cache.remove(&first_key);
}
}
self.cache.insert(key, glyph);
}
pub fn clear(&mut self) {
self.cache.clear();
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
}
pub struct TextureAtlas {
width: u32,
height: u32,
data: Vec<u8>,
allocations: Vec<AtlasRegion>,
next_x: u32,
next_y: u32,
row_height: u32,
}
#[derive(Debug, Clone)]
pub struct AtlasRegion {
pub id: u32,
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
impl TextureAtlas {
pub fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
data: vec![0; (width * height * 4) as usize],
allocations: Vec::new(),
next_x: 0,
next_y: 0,
row_height: 0,
}
}
pub fn allocate(&mut self, width: u32, height: u32) -> Option<AtlasRegion> {
if self.next_x + width > self.width {
self.next_x = 0;
self.next_y += self.row_height;
self.row_height = 0;
}
if self.next_y + height > self.height {
return None;
}
let region = AtlasRegion {
id: self.allocations.len() as u32,
x: self.next_x,
y: self.next_y,
width,
height,
};
self.next_x += width;
self.row_height = self.row_height.max(height);
self.allocations.push(region.clone());
Some(region)
}
pub fn write_region(&mut self, region: &AtlasRegion, data: &[u8]) {
let stride = self.width * 4;
for y in 0..region.height {
let src_offset = (y * region.width * 4) as usize;
let dst_offset = ((region.y + y) * stride + region.x * 4) as usize;
let src_end = src_offset + (region.width * 4) as usize;
let dst_end = dst_offset + (region.width * 4) as usize;
if src_end <= data.len() && dst_end <= self.data.len() {
self.data[dst_offset..dst_end].copy_from_slice(&data[src_offset..src_end]);
}
}
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn clear(&mut self) {
self.data.fill(0);
self.allocations.clear();
self.next_x = 0;
self.next_y = 0;
self.row_height = 0;
}
}