// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: LGPL-3.0-or-later use super::{AllsortsFont, Rect, Vec2}; use allsorts::binary::read::ReadScope; use allsorts::cff::CFF; use allsorts::font::GlyphTableFlags; use allsorts::font_data::DynamicFontTableProvider; use allsorts::outline::OutlineBuilder; use allsorts::pathfinder_geometry::{line_segment::LineSegment2F, vector::Vector2F}; use allsorts::tables::{glyf::GlyfTable, loca::LocaTable, FontTableProvider, SfntVersion}; use allsorts::tag; use lyon::path::Path; use parking_lot::RwLock; pub struct GlyphCache { pub units_per_em: f32, pub glyphs: RwLock>, } impl GlyphCache { pub fn new(font: &AllsortsFont) -> Self { let glyph_num = font.num_glyphs(); let mut glyphs = Vec::with_capacity(glyph_num as usize); let units_per_em = font.head_table().unwrap().unwrap().units_per_em as f32; 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(units_per_em, &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(units_per_em, &mut glyf, index)); } } else { panic!("no glyf or CFF table"); } Self { units_per_em, glyphs: RwLock::new(glyphs), } } } pub struct CachedGlyph { pub bounding_box: Rect, path: Path, mesh: Option, } impl CachedGlyph { pub fn new(units_per_em: f32, builder: &mut impl OutlineBuilder, index: u16) -> Self { let mut sink = OutlineSink::new(units_per_em); builder.visit(index, &mut sink).unwrap(); let bounding_box = sink.bounding_box; let path = sink.builder.build(); Self { bounding_box, 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 fill_options = FillOptions::default().with_tolerance(0.0005); let mut tessellator = FillTessellator::new(); tessellator .tessellate_path( &self.path.clone(), &fill_options, &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { let position = vertex.position(); Vec2::new(position.x, position.y) }), ) .unwrap(); GlyphMesh { vertices: geometry.vertices, indices: geometry.indices, } }) } } pub struct OutlineSink { units_per_em: f32, bounding_box: Rect, builder: lyon::path::path::Builder, } impl OutlineSink { pub fn new(units_per_em: f32) -> Self { Self { units_per_em, bounding_box: Rect::NEG_INFINITY, builder: lyon::path::Path::builder(), } } fn pf_line_to_lyon( &mut self, 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(&mut self, v: &Vector2F) -> lyon::geom::Point { let point = lyon::geom::Point::::new(v.x(), -v.y()) / self.units_per_em; let glam_point = Vec2::new(point.x, point.y); self.bounding_box = self.bounding_box.union_point(glam_point); point } } 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(); } } pub struct GlyphMesh { pub vertices: Vec, pub indices: Vec, }