use crate::font::text_run_cache::{
create_cached_text_run, create_shaping_key, create_text_run_key, CacheHitType,
ShapedGlyph, TextRunCache,
};
use std::sync::Arc;
use tracing::debug;
pub struct TextRunManager {
unified_cache: TextRunCache,
}
impl TextRunManager {
pub fn new() -> Self {
Self {
unified_cache: TextRunCache::new(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn get_cached_data(
&mut self,
text: &str,
font_id: usize,
font_size: f32,
color: Option<[f32; 4]>,
) -> CacheResult {
let key = create_text_run_key(text, font_id, font_size, color);
match self.unified_cache.get(&key) {
Some(CacheHitType::FullRender(cached_run)) => CacheResult::FullRender {
glyphs: cached_run.glyphs.clone(),
vertices: cached_run.vertices.clone().unwrap(),
base_position: cached_run.base_position.unwrap(),
advance_width: cached_run.advance_width,
has_emoji: cached_run.has_emoji,
font_id: cached_run.font_id,
},
Some(CacheHitType::ShapingOnly(cached_run)) => CacheResult::ShapingOnly {
glyphs: cached_run.glyphs.clone(),
shaping_features: cached_run.shaping_features.clone(),
advance_width: cached_run.advance_width,
has_emoji: cached_run.has_emoji,
font_id: cached_run.font_id,
},
Some(CacheHitType::GlyphsOnly(cached_run)) => CacheResult::GlyphsOnly {
glyphs: cached_run.glyphs.clone(),
advance_width: cached_run.advance_width,
has_emoji: cached_run.has_emoji,
font_id: cached_run.font_id,
},
None => CacheResult::Miss,
}
}
#[allow(clippy::too_many_arguments)]
pub fn cache_shaping_data(
&mut self,
text: &str,
font_id: usize,
font_size: f32,
glyphs: Vec<ShapedGlyph>,
has_emoji: bool,
shaping_features: Option<Vec<u8>>,
) {
let key = create_shaping_key(text, font_id, font_size);
let cached_run = create_cached_text_run(
glyphs,
font_id,
font_size,
has_emoji,
shaping_features,
None, None, None, );
self.unified_cache.insert(key, cached_run);
}
pub fn apply_cached_vertices(
vertices_data: &[u8],
base_position: (f32, f32),
new_position: (f32, f32),
output_vertices: &mut Vec<u8>,
) {
let dx = new_position.0 - base_position.0;
let dy = new_position.1 - base_position.1;
if dx == 0.0 && dy == 0.0 {
output_vertices.extend_from_slice(vertices_data);
return;
}
const VERTEX_SIZE: usize = 44;
if vertices_data.len() % VERTEX_SIZE != 0 {
debug!("Invalid vertex data size: {}", vertices_data.len());
output_vertices.extend_from_slice(vertices_data);
return;
}
output_vertices.reserve(vertices_data.len());
for chunk in vertices_data.chunks_exact(VERTEX_SIZE) {
let x_bytes = [chunk[0], chunk[1], chunk[2], chunk[3]];
let y_bytes = [chunk[4], chunk[5], chunk[6], chunk[7]];
let z_bytes = [chunk[8], chunk[9], chunk[10], chunk[11]];
let mut x = f32::from_le_bytes(x_bytes);
let mut y = f32::from_le_bytes(y_bytes);
let z = f32::from_le_bytes(z_bytes);
x += dx;
y += dy;
output_vertices.extend_from_slice(&x.to_le_bytes());
output_vertices.extend_from_slice(&y.to_le_bytes());
output_vertices.extend_from_slice(&z.to_le_bytes());
output_vertices.extend_from_slice(&chunk[12..]);
}
debug!("Applied cached vertices with offset ({}, {})", dx, dy);
}
pub fn clear_all(&mut self) {
self.unified_cache.clear();
debug!("TextRunManager: Cleared unified cache due to font change");
}
}
#[derive(Debug)]
#[allow(unused)]
pub enum CacheResult {
FullRender {
glyphs: Arc<Vec<ShapedGlyph>>,
vertices: Arc<Vec<u8>>,
base_position: (f32, f32),
advance_width: f32,
has_emoji: bool,
font_id: usize,
},
ShapingOnly {
glyphs: Arc<Vec<ShapedGlyph>>,
shaping_features: Option<Arc<Vec<u8>>>,
advance_width: f32,
has_emoji: bool,
font_id: usize,
},
GlyphsOnly {
glyphs: Arc<Vec<ShapedGlyph>>,
advance_width: f32,
has_emoji: bool,
font_id: usize,
},
Miss,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vertex_positioning() {
let mut vertices = Vec::new();
vertices.extend_from_slice(&10.0f32.to_le_bytes()); vertices.extend_from_slice(&20.0f32.to_le_bytes()); vertices.extend_from_slice(&0.0f32.to_le_bytes());
vertices.extend_from_slice(&1.0f32.to_le_bytes());
vertices.extend_from_slice(&0.5f32.to_le_bytes());
vertices.extend_from_slice(&0.0f32.to_le_bytes());
vertices.extend_from_slice(&1.0f32.to_le_bytes());
vertices.extend_from_slice(&0.5f32.to_le_bytes());
vertices.extend_from_slice(&0.7f32.to_le_bytes());
vertices.extend_from_slice(&1i32.to_le_bytes());
vertices.extend_from_slice(&2i32.to_le_bytes());
let mut output_vertices = Vec::new();
TextRunManager::apply_cached_vertices(
&vertices,
(100.0, 200.0), (150.0, 250.0), &mut output_vertices,
);
assert_eq!(output_vertices.len(), 44);
let x = f32::from_le_bytes([
output_vertices[0],
output_vertices[1],
output_vertices[2],
output_vertices[3],
]);
let y = f32::from_le_bytes([
output_vertices[4],
output_vertices[5],
output_vertices[6],
output_vertices[7],
]);
let z = f32::from_le_bytes([
output_vertices[8],
output_vertices[9],
output_vertices[10],
output_vertices[11],
]);
assert_eq!(x, 60.0);
assert_eq!(y, 70.0);
assert_eq!(z, 0.0);
let color_r = f32::from_le_bytes([
output_vertices[12],
output_vertices[13],
output_vertices[14],
output_vertices[15],
]);
let uv_u = f32::from_le_bytes([
output_vertices[28],
output_vertices[29],
output_vertices[30],
output_vertices[31],
]);
let layer_0 = i32::from_le_bytes([
output_vertices[36],
output_vertices[37],
output_vertices[38],
output_vertices[39],
]);
assert_eq!(color_r, 1.0);
assert_eq!(uv_u, 0.5);
assert_eq!(layer_0, 1);
}
#[test]
fn test_vertex_positioning_no_offset() {
let vertices = vec![0u8; 44]; let mut output_vertices = Vec::new();
TextRunManager::apply_cached_vertices(
&vertices,
(100.0, 200.0),
(100.0, 200.0), &mut output_vertices,
);
assert_eq!(output_vertices, vertices);
}
}