use super::{DrawCommand, MeshIndex, MeshVertex}; use allsorts::binary::read::ReadScope; use allsorts::cff::CFF; use allsorts::font::{GlyphTableFlags, MatchingPresentation}; use allsorts::font_data::{DynamicFontTableProvider, FontData}; use allsorts::glyph_position::{GlyphLayout, TextDirection}; use allsorts::outline::OutlineBuilder; use allsorts::pathfinder_geometry::{line_segment::LineSegment2F, vector::Vector2F}; use allsorts::tables::{glyf::GlyfTable, loca::LocaTable}; use allsorts::tables::{FontTableProvider, SfntVersion}; use allsorts::{tag, Font}; use lyon::path::Path; const TEXT_SCALE: f32 = 0.0001; pub struct GlyphCache { glyphs: Vec, } impl GlyphCache { pub fn new(font: &mut Font) -> Self { let glyph_num = font.num_glyphs(); let mut glyphs = Vec::with_capacity(glyph_num as usize); if font.glyph_table_flags.contains(GlyphTableFlags::CFF) && font.font_table_provider.sfnt_version() == tag::OTTO { let cff_data = font.font_table_provider.read_table_data(tag::CFF).unwrap(); let mut cff = ReadScope::new(&cff_data).read::>().unwrap(); for index in 0..glyph_num { glyphs.push(CachedGlyph::new(&mut cff, index)); } } else if font.glyph_table_flags.contains(GlyphTableFlags::GLYF) { let loca_data = font.font_table_provider.read_table_data(tag::LOCA).unwrap(); let loca = ReadScope::new(&loca_data) .read_dep::>(( usize::from(font.maxp_table.num_glyphs), font.head_table().unwrap().unwrap().index_to_loc_format, )) .unwrap(); let glyf_data = font.font_table_provider.read_table_data(tag::GLYF).unwrap(); let mut glyf = ReadScope::new(&glyf_data) .read_dep::>(&loca) .unwrap(); for index in 0..glyph_num { glyphs.push(CachedGlyph::new(&mut glyf, index)); } } else { panic!("no glyf or CFF table"); } Self { glyphs } } pub fn draw(&mut self, mesh: &mut DummyText, index: u16, xpos: i32, ypos: i32) { let glyph = self.glyphs.get_mut(index as usize).unwrap().get_mesh(); let voff = mesh.vertices.len() as MeshIndex; let xoff = (xpos as f32) * TEXT_SCALE; let yoff = (ypos as f32) * TEXT_SCALE; for v in glyph.vertices.iter() { let x = v.position.x + xoff - 0.9; let y = v.position.y + yoff + 0.5; mesh.vertices.push(MeshVertex { position: canary_types::Vec2 { x, y }, color: v.color, }); } for i in glyph.indices.iter() { mesh.indices.push(i + voff); } } } pub struct CachedGlyph { path: Path, mesh: Option, } impl CachedGlyph { pub fn new(builder: &mut impl OutlineBuilder, index: u16) -> Self { let mut sink = OutlineSink::new(); builder.visit(index, &mut sink).unwrap(); let path = sink.builder.build(); Self { path, mesh: None } } pub fn get_mesh(&mut self) -> &GlyphMesh { self.mesh.get_or_insert_with(|| { use lyon::tessellation::*; let mut geometry: VertexBuffers = VertexBuffers::new(); let mut tessellator = FillTessellator::new(); tessellator .tessellate_path( &self.path.clone(), &FillOptions::default(), &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { let position = vertex.position() * TEXT_SCALE; let position = canary_types::Vec2 { x: position.x, y: position.y, }; MeshVertex { position, color: canary_types::Color::WHITE, } }), ) .unwrap(); GlyphMesh { vertices: geometry.vertices, indices: geometry.indices, } }) } } pub struct GlyphMesh { vertices: Vec, indices: Vec, } pub struct DummyText { vertices: Vec, indices: Vec, } impl Default for DummyText { fn default() -> Self { let script = tag::LATN; let buffer = include_bytes!("linja-pona-4.9.otf"); let buffer = buffer.as_slice(); let scope = ReadScope::new(&buffer); let font_file = scope.read::>().unwrap(); let provider = font_file.table_provider(0).unwrap(); let mut font = Font::new(provider).unwrap().unwrap(); let mut cache = GlyphCache::new(&mut font); let text = "toki o! nimi mi li [_mun_alasa_sona]. toki+pona li pona."; let glyphs = font.map_glyphs(&text, script, MatchingPresentation::Required); let lang_tag = None; let features = allsorts::gsub::Features::default(); let kerning = true; let infos = font .shape(glyphs, script, lang_tag, &features, kerning) .unwrap(); let direction = TextDirection::LeftToRight; let vertical = false; let mut layout = GlyphLayout::new(&mut font, &infos, direction, vertical); let positions = layout.glyph_positions().unwrap(); let mut mesh = Self { vertices: Vec::new(), indices: Vec::new(), }; let mut xcur = 0; let mut ycur = 0; for (glyph, position) in infos.iter().zip(&positions) { let xpos = xcur + position.x_offset; let ypos = ycur + position.y_offset; xcur += position.hori_advance; ycur += position.vert_advance; cache.draw(&mut mesh, glyph.glyph.glyph_index, xpos, ypos); } mesh } } impl DummyText { pub fn draw(&self, cmds: &mut Vec) { cmds.push(DrawCommand::Mesh { vertices: self.vertices.clone(), indices: self.indices.clone(), }); } } pub struct OutlineSink { builder: lyon::path::path::Builder, } impl OutlineSink { pub fn new() -> Self { Self { builder: lyon::path::Path::builder(), } } fn pf_line_to_lyon(line: &LineSegment2F) -> (lyon::geom::Point, lyon::geom::Point) { ( Self::pf_vector_to_lyon(&line.from()), Self::pf_vector_to_lyon(&line.to()), ) } fn pf_vector_to_lyon(v: &Vector2F) -> lyon::geom::Point { lyon::geom::Point::::new(v.x(), v.y()) } } impl allsorts::outline::OutlineSink for OutlineSink { fn move_to(&mut self, to: Vector2F) { let to = Self::pf_vector_to_lyon(&to); self.builder.begin(to); } fn line_to(&mut self, to: Vector2F) { let to = Self::pf_vector_to_lyon(&to); self.builder.line_to(to); } fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) { let control = Self::pf_vector_to_lyon(&control); let to = Self::pf_vector_to_lyon(&to); self.builder.quadratic_bezier_to(control, to); } fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) { let (ctrl1, ctrl2) = Self::pf_line_to_lyon(&control); let to = Self::pf_vector_to_lyon(&to); self.builder.cubic_bezier_to(ctrl1, ctrl2, to); } fn close(&mut self) { self.builder.close(); } }