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"
parking_lot = "0.12"
wasmtime = "0.38"
[dependencies.font-kit]
version = "0.11"

View File

@ -14,16 +14,27 @@ use canary_types::GlyphPosition;
use lyon::path::Path;
use ouroboros::self_referencing;
const TEXT_SCALE: f32 = 0.0001;
const TEXT_SCALE: f32 = 0.075;
pub struct GlyphCache {
units_per_em: f32,
glyphs: Vec<CachedGlyph>,
}
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 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)
&& 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 mut cff = ReadScope::new(&cff_data).read::<CFF<'_>>().unwrap();
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) {
let loca_data = font.font_table_provider.read_table_data(tag::LOCA).unwrap();
@ -47,24 +59,24 @@ impl GlyphCache {
.unwrap();
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 {
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 voff = mesh.vertices.len() as MeshIndex;
let xoff = (xpos as f32) * TEXT_SCALE;
let yoff = (ypos as f32) * TEXT_SCALE;
let scale = TEXT_SCALE / self.units_per_em;
let xpos = xpos as f32 * scale;
let ypos = ypos as f32 * scale;
for v in glyph.vertices.iter() {
let x = v.position.x + xoff - 0.9;
let y = v.position.y + yoff + 0.5;
let x = v.position.x + xpos - 0.9;
let y = v.position.y + ypos + yoff;
mesh.vertices.push(MeshVertex {
position: canary_types::Vec2 { x, y },
color: v.color,
@ -83,8 +95,8 @@ pub struct CachedGlyph {
}
impl CachedGlyph {
pub fn new(builder: &mut impl OutlineBuilder, index: u16) -> Self {
let mut sink = OutlineSink::new();
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 path = sink.builder.build();
@ -96,11 +108,12 @@ impl CachedGlyph {
use lyon::tessellation::*;
let mut geometry: VertexBuffers<MeshVertex, MeshIndex> = VertexBuffers::new();
let fill_options = FillOptions::default().with_tolerance(0.0005);
let mut tessellator = FillTessellator::new();
tessellator
.tessellate_path(
&self.path.clone(),
&FillOptions::default(),
&fill_options,
&mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
let position = vertex.position() * TEXT_SCALE;
let position = canary_types::Vec2 {
@ -136,6 +149,7 @@ pub struct Font {
script: u32,
direction: TextDirection,
vertical: bool,
glyph_cache: GlyphCache,
#[borrows(file_buffer)]
#[covariant]
@ -158,12 +172,13 @@ impl Font {
vertical: bool,
file_buffer: Vec<u8>,
) -> Self {
FontBuilder {
let mut built = FontBuilder {
file_buffer,
face_index,
script,
direction,
vertical,
glyph_cache: GlyphCache::empty(),
read_scope_builder: |buffer| ReadScope::new(buffer),
font_data_builder: |scope| scope.read::<FontData<'_>>().unwrap(),
inner_builder: |font_data| {
@ -172,11 +187,16 @@ impl Font {
.unwrap()
},
}
.build()
}
.build();
pub fn create_glyph_cache(&self) -> GlyphCache {
GlyphCache::new(self.borrow_inner())
let mut glyph_cache = GlyphCache::empty();
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> {
@ -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 {
vertices: Vec<MeshVertex>,
indices: Vec<MeshIndex>,
@ -220,32 +283,34 @@ pub struct DummyText {
impl Default for DummyText {
fn default() -> Self {
let script = tag::LATN;
let face_index = 0;
let direction = TextDirection::LeftToRight;
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 store = FontStore::new();
let text = "The quick brown fox jumps over the lazy dog. 🍑";
let font_families = ["mononoki", "Liberation Sans", "Ubuntu", "Noto Color Emoji"];
let mut mesh = Self {
vertices: Vec::new(),
indices: Vec::new(),
};
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);
let line_height = -0.1;
let mut line_offset = 0.9;
for family in font_families.iter() {
let mut font = store.load_font(family);
let glyphs = font.shape(text);
font.with_glyph_cache_mut(|cache| {
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
@ -262,48 +327,53 @@ impl DummyText {
}
pub struct OutlineSink {
units_per_em: f32,
builder: lyon::path::path::Builder,
}
impl OutlineSink {
pub fn new() -> Self {
pub fn new(units_per_em: f32) -> Self {
Self {
units_per_em,
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.to()),
self.pf_vector_to_lyon(&line.from()),
self.pf_vector_to_lyon(&line.to()),
)
}
fn pf_vector_to_lyon(v: &Vector2F) -> lyon::geom::Point<f32> {
lyon::geom::Point::<f32>::new(v.x(), v.y())
fn pf_vector_to_lyon(&self, v: &Vector2F) -> lyon::geom::Point<f32> {
lyon::geom::Point::<f32>::new(v.x(), v.y()) / self.units_per_em
}
}
impl allsorts::outline::OutlineSink for OutlineSink {
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);
}
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);
}
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);
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);
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);
}