System font loading + proper scaling + FontStore

This commit is contained in:
mars 2022-07-16 21:57:45 -06:00
parent feadf8fa15
commit dc71971c39
2 changed files with 126 additions and 53 deletions

View File

@ -20,3 +20,6 @@ lyon = "1"
ouroboros = "^0.15" ouroboros = "^0.15"
parking_lot = "0.12" parking_lot = "0.12"
wasmtime = "0.38" wasmtime = "0.38"
[dependencies.font-kit]
version = "0.11"

View File

@ -14,16 +14,27 @@ use canary_types::GlyphPosition;
use lyon::path::Path; use lyon::path::Path;
use ouroboros::self_referencing; use ouroboros::self_referencing;
const TEXT_SCALE: f32 = 0.0001; const TEXT_SCALE: f32 = 0.075;
pub struct GlyphCache { pub struct GlyphCache {
units_per_em: f32,
glyphs: Vec<CachedGlyph>, glyphs: Vec<CachedGlyph>,
} }
impl GlyphCache { impl GlyphCache {
pub fn new(font: &AllsortsFont<DynamicFontTableProvider>) -> Self { pub fn empty() -> Self {
Self {
units_per_em: 0.0,
glyphs: Vec::new(),
}
}
pub fn load_paths(&mut self, font: &AllsortsFont<DynamicFontTableProvider>) {
let glyph_num = font.num_glyphs(); let glyph_num = font.num_glyphs();
let mut glyphs = Vec::with_capacity(glyph_num as usize); self.glyphs.clear();
self.glyphs.reserve(glyph_num as usize);
self.units_per_em = font.head_table().unwrap().unwrap().units_per_em as f32;
if font.glyph_table_flags.contains(GlyphTableFlags::CFF) if font.glyph_table_flags.contains(GlyphTableFlags::CFF)
&& font.font_table_provider.sfnt_version() == tag::OTTO && font.font_table_provider.sfnt_version() == tag::OTTO
@ -31,7 +42,8 @@ impl GlyphCache {
let cff_data = font.font_table_provider.read_table_data(tag::CFF).unwrap(); let cff_data = font.font_table_provider.read_table_data(tag::CFF).unwrap();
let mut cff = ReadScope::new(&cff_data).read::<CFF<'_>>().unwrap(); let mut cff = ReadScope::new(&cff_data).read::<CFF<'_>>().unwrap();
for index in 0..glyph_num { for index in 0..glyph_num {
glyphs.push(CachedGlyph::new(&mut cff, index)); self.glyphs
.push(CachedGlyph::new(self.units_per_em, &mut cff, index));
} }
} else if font.glyph_table_flags.contains(GlyphTableFlags::GLYF) { } else if font.glyph_table_flags.contains(GlyphTableFlags::GLYF) {
let loca_data = font.font_table_provider.read_table_data(tag::LOCA).unwrap(); let loca_data = font.font_table_provider.read_table_data(tag::LOCA).unwrap();
@ -47,24 +59,24 @@ impl GlyphCache {
.unwrap(); .unwrap();
for index in 0..glyph_num { for index in 0..glyph_num {
glyphs.push(CachedGlyph::new(&mut glyf, index)); self.glyphs
.push(CachedGlyph::new(self.units_per_em, &mut glyf, index));
} }
} else { } else {
panic!("no glyf or CFF table"); panic!("no glyf or CFF table {:?}");
} }
Self { glyphs }
} }
pub fn draw(&mut self, mesh: &mut DummyText, index: u16, xpos: i32, ypos: i32) { pub fn draw(&mut self, mesh: &mut DummyText, index: u16, xpos: i32, ypos: i32, yoff: f32) {
let glyph = self.glyphs.get_mut(index as usize).unwrap().get_mesh(); let glyph = self.glyphs.get_mut(index as usize).unwrap().get_mesh();
let voff = mesh.vertices.len() as MeshIndex; let voff = mesh.vertices.len() as MeshIndex;
let xoff = (xpos as f32) * TEXT_SCALE; let scale = TEXT_SCALE / self.units_per_em;
let yoff = (ypos as f32) * TEXT_SCALE; let xpos = xpos as f32 * scale;
let ypos = ypos as f32 * scale;
for v in glyph.vertices.iter() { for v in glyph.vertices.iter() {
let x = v.position.x + xoff - 0.9; let x = v.position.x + xpos - 0.9;
let y = v.position.y + yoff + 0.5; let y = v.position.y + ypos + yoff;
mesh.vertices.push(MeshVertex { mesh.vertices.push(MeshVertex {
position: canary_types::Vec2 { x, y }, position: canary_types::Vec2 { x, y },
color: v.color, color: v.color,
@ -83,8 +95,8 @@ pub struct CachedGlyph {
} }
impl CachedGlyph { impl CachedGlyph {
pub fn new(builder: &mut impl OutlineBuilder, index: u16) -> Self { pub fn new(units_per_em: f32, builder: &mut impl OutlineBuilder, index: u16) -> Self {
let mut sink = OutlineSink::new(); let mut sink = OutlineSink::new(units_per_em);
builder.visit(index, &mut sink).unwrap(); builder.visit(index, &mut sink).unwrap();
let path = sink.builder.build(); let path = sink.builder.build();
@ -96,11 +108,12 @@ impl CachedGlyph {
use lyon::tessellation::*; use lyon::tessellation::*;
let mut geometry: VertexBuffers<MeshVertex, MeshIndex> = VertexBuffers::new(); let mut geometry: VertexBuffers<MeshVertex, MeshIndex> = VertexBuffers::new();
let fill_options = FillOptions::default().with_tolerance(0.0005);
let mut tessellator = FillTessellator::new(); let mut tessellator = FillTessellator::new();
tessellator tessellator
.tessellate_path( .tessellate_path(
&self.path.clone(), &self.path.clone(),
&FillOptions::default(), &fill_options,
&mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
let position = vertex.position() * TEXT_SCALE; let position = vertex.position() * TEXT_SCALE;
let position = canary_types::Vec2 { let position = canary_types::Vec2 {
@ -136,6 +149,7 @@ pub struct Font {
script: u32, script: u32,
direction: TextDirection, direction: TextDirection,
vertical: bool, vertical: bool,
glyph_cache: GlyphCache,
#[borrows(file_buffer)] #[borrows(file_buffer)]
#[covariant] #[covariant]
@ -158,12 +172,13 @@ impl Font {
vertical: bool, vertical: bool,
file_buffer: Vec<u8>, file_buffer: Vec<u8>,
) -> Self { ) -> Self {
FontBuilder { let mut built = FontBuilder {
file_buffer, file_buffer,
face_index, face_index,
script, script,
direction, direction,
vertical, vertical,
glyph_cache: GlyphCache::empty(),
read_scope_builder: |buffer| ReadScope::new(buffer), read_scope_builder: |buffer| ReadScope::new(buffer),
font_data_builder: |scope| scope.read::<FontData<'_>>().unwrap(), font_data_builder: |scope| scope.read::<FontData<'_>>().unwrap(),
inner_builder: |font_data| { inner_builder: |font_data| {
@ -172,11 +187,16 @@ impl Font {
.unwrap() .unwrap()
}, },
} }
.build() .build();
}
pub fn create_glyph_cache(&self) -> GlyphCache { let mut glyph_cache = GlyphCache::empty();
GlyphCache::new(self.borrow_inner()) built.with_inner_mut(|font| {
glyph_cache.load_paths(font);
});
built.with_glyph_cache_mut(|old| std::mem::replace(old, glyph_cache));
built
} }
pub fn shape(&mut self, text: &str) -> Vec<GlyphPosition> { pub fn shape(&mut self, text: &str) -> Vec<GlyphPosition> {
@ -213,6 +233,49 @@ impl Font {
} }
} }
pub struct FontStore {
source: Box<dyn font_kit::source::Source>,
}
impl FontStore {
pub fn new() -> Self {
let source = font_kit::source::SystemSource::new();
let source = Box::new(source);
Self { source }
}
pub fn load_font(&self, title: &str) -> Font {
use font_kit::family_name::FamilyName;
use font_kit::handle::Handle;
use font_kit::properties::{Properties, Style};
println!("loading font family {}", title);
let font_handle = self
.source
.select_best_match(&[FamilyName::Title(title.to_string())], &Properties::new())
.unwrap();
println!("loading font file: {:?}", font_handle);
let font_data = if let Handle::Path {
path,
font_index: _,
} = font_handle
{
std::fs::read(path).unwrap()
} else {
panic!("font is not a file for some reason");
};
let script = tag::LATN;
let face_index = 0;
let direction = TextDirection::LeftToRight;
let vertical = false;
Font::load(face_index, script, direction, vertical, font_data.clone())
}
}
pub struct DummyText { pub struct DummyText {
vertices: Vec<MeshVertex>, vertices: Vec<MeshVertex>,
indices: Vec<MeshIndex>, indices: Vec<MeshIndex>,
@ -220,32 +283,34 @@ pub struct DummyText {
impl Default for DummyText { impl Default for DummyText {
fn default() -> Self { fn default() -> Self {
let script = tag::LATN; let store = FontStore::new();
let face_index = 0; let text = "The quick brown fox jumps over the lazy dog. 🍑";
let direction = TextDirection::LeftToRight; let font_families = ["mononoki", "Liberation Sans", "Ubuntu", "Noto Color Emoji"];
let vertical = false;
let buffer = include_bytes!("linja-pona-4.9.otf");
let mut font = Font::load(face_index, script, direction, vertical, buffer.to_vec());
let mut cache = font.create_glyph_cache();
let text = "toki o! nimi mi li [_mun_alasa_sona]. toki+pona li pona.";
let glyphs = font.shape(text);
let mut mesh = Self { let mut mesh = Self {
vertices: Vec::new(), vertices: Vec::new(),
indices: Vec::new(), indices: Vec::new(),
}; };
let mut xcur = 0; let line_height = -0.1;
let mut ycur = 0; let mut line_offset = 0.9;
for glyph in glyphs.iter() { for family in font_families.iter() {
let xpos = xcur + glyph.xoff; let mut font = store.load_font(family);
let ypos = ycur + glyph.yoff; let glyphs = font.shape(text);
xcur += glyph.hori_advance;
ycur += glyph.vert_advance; font.with_glyph_cache_mut(|cache| {
cache.draw(&mut mesh, glyph.index, xpos, ypos); let mut xcur = 0;
let mut ycur = 0;
for glyph in glyphs.iter() {
let xpos = xcur + glyph.xoff;
let ypos = ycur + glyph.yoff;
xcur += glyph.hori_advance;
ycur += glyph.vert_advance;
cache.draw(&mut mesh, glyph.index, xpos, ypos, line_offset);
}
});
line_offset += line_height;
} }
mesh mesh
@ -262,48 +327,53 @@ impl DummyText {
} }
pub struct OutlineSink { pub struct OutlineSink {
units_per_em: f32,
builder: lyon::path::path::Builder, builder: lyon::path::path::Builder,
} }
impl OutlineSink { impl OutlineSink {
pub fn new() -> Self { pub fn new(units_per_em: f32) -> Self {
Self { Self {
units_per_em,
builder: lyon::path::Path::builder(), builder: lyon::path::Path::builder(),
} }
} }
fn pf_line_to_lyon(line: &LineSegment2F) -> (lyon::geom::Point<f32>, lyon::geom::Point<f32>) { fn pf_line_to_lyon(
&self,
line: &LineSegment2F,
) -> (lyon::geom::Point<f32>, lyon::geom::Point<f32>) {
( (
Self::pf_vector_to_lyon(&line.from()), self.pf_vector_to_lyon(&line.from()),
Self::pf_vector_to_lyon(&line.to()), self.pf_vector_to_lyon(&line.to()),
) )
} }
fn pf_vector_to_lyon(v: &Vector2F) -> lyon::geom::Point<f32> { fn pf_vector_to_lyon(&self, v: &Vector2F) -> lyon::geom::Point<f32> {
lyon::geom::Point::<f32>::new(v.x(), v.y()) lyon::geom::Point::<f32>::new(v.x(), v.y()) / self.units_per_em
} }
} }
impl allsorts::outline::OutlineSink for OutlineSink { impl allsorts::outline::OutlineSink for OutlineSink {
fn move_to(&mut self, to: Vector2F) { fn move_to(&mut self, to: Vector2F) {
let to = Self::pf_vector_to_lyon(&to); let to = self.pf_vector_to_lyon(&to);
self.builder.begin(to); self.builder.begin(to);
} }
fn line_to(&mut self, to: Vector2F) { fn line_to(&mut self, to: Vector2F) {
let to = Self::pf_vector_to_lyon(&to); let to = self.pf_vector_to_lyon(&to);
self.builder.line_to(to); self.builder.line_to(to);
} }
fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) { fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) {
let control = Self::pf_vector_to_lyon(&control); let control = self.pf_vector_to_lyon(&control);
let to = Self::pf_vector_to_lyon(&to); let to = self.pf_vector_to_lyon(&to);
self.builder.quadratic_bezier_to(control, to); self.builder.quadratic_bezier_to(control, to);
} }
fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) { fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) {
let (ctrl1, ctrl2) = Self::pf_line_to_lyon(&control); let (ctrl1, ctrl2) = self.pf_line_to_lyon(&control);
let to = Self::pf_vector_to_lyon(&to); let to = self.pf_vector_to_lyon(&to);
self.builder.cubic_bezier_to(ctrl1, ctrl2, to); self.builder.cubic_bezier_to(ctrl1, ctrl2, to);
} }