174 lines
5.5 KiB
Rust
174 lines
5.5 KiB
Rust
// 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<Vec<CachedGlyph>>,
|
|
}
|
|
|
|
impl GlyphCache {
|
|
pub fn new(font: &AllsortsFont<DynamicFontTableProvider>) -> 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::<CFF<'_>>().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::<LocaTable<'_>>((
|
|
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::<GlyfTable<'_>>(&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<GlyphMesh>,
|
|
}
|
|
|
|
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<Vec2, u32> = 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<f32>, lyon::geom::Point<f32>) {
|
|
(
|
|
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<f32> {
|
|
let point = lyon::geom::Point::<f32>::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<Vec2>,
|
|
pub indices: Vec<u32>,
|
|
}
|