System font loading + proper scaling + FontStore
This commit is contained in:
parent
feadf8fa15
commit
dc71971c39
|
@ -20,3 +20,6 @@ lyon = "1"
|
|||
ouroboros = "^0.15"
|
||||
parking_lot = "0.12"
|
||||
wasmtime = "0.38"
|
||||
|
||||
[dependencies.font-kit]
|
||||
version = "0.11"
|
||||
|
|
176
src/text.rs
176
src/text.rs
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue