EXTREMELY HACKY multifont
This commit is contained in:
parent
6c44feab62
commit
e070a54bb6
|
@ -59,7 +59,7 @@ impl MainMenu {
|
|||
|
||||
impl Default for MainMenu {
|
||||
fn default() -> Self {
|
||||
let icon_font = Font::new("Leon Sans");
|
||||
let icon_font = Font::new("Iosevka Nerd Font");
|
||||
let icons = ["", "", "", "", "", ""];
|
||||
|
||||
let mut buttons = Vec::new();
|
||||
|
|
|
@ -247,9 +247,10 @@ impl ScriptAbi for ScriptAbiImpl {
|
|||
fn draw_text_layout(&self, id: u32, offset: Vec2, scale: f32, color: Color) {
|
||||
// TODO multiple fonts per layout
|
||||
let layouts = self.text_layouts.read();
|
||||
let glyphs = layouts.get(id as usize).unwrap().glyphs.as_slice();
|
||||
let layout = layouts.get(id as usize).unwrap();
|
||||
let glyphs = layout.glyphs.as_slice();
|
||||
let loaded = self.loaded_fonts.read();
|
||||
let font = loaded.get(glyphs[0].face as usize).unwrap();
|
||||
let font = loaded.get(layout.font_id as usize).unwrap();
|
||||
let cmds = font.draw(glyphs, offset, scale, color);
|
||||
self.draw_cmds.lock().extend(cmds.into_iter());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
use super::AllsortsFont;
|
||||
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 canary_types::{Rect, Vec2};
|
||||
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 {
|
||||
x: position.x,
|
||||
y: 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;
|
||||
|
||||
// TODO clean this up with helper math methods?
|
||||
let bb = &mut self.bounding_box;
|
||||
|
||||
if point.x < bb.bl.x {
|
||||
bb.bl.x = point.x;
|
||||
}
|
||||
|
||||
if point.x > bb.tr.x {
|
||||
bb.tr.x = point.x;
|
||||
}
|
||||
|
||||
if point.y < bb.bl.y {
|
||||
bb.bl.y = point.y;
|
||||
}
|
||||
|
||||
if point.y > bb.tr.y {
|
||||
bb.tr.y = point.y;
|
||||
}
|
||||
|
||||
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>,
|
||||
}
|
|
@ -1,205 +1,24 @@
|
|||
use super::{DrawCommand, MeshIndex, MeshVertex};
|
||||
|
||||
use allsorts::binary::read::ReadScope;
|
||||
use allsorts::cff::CFF;
|
||||
use allsorts::font::{GlyphTableFlags, MatchingPresentation};
|
||||
use allsorts::font::MatchingPresentation;
|
||||
use allsorts::font_data::{DynamicFontTableProvider, FontData as AllsortsFontData};
|
||||
use allsorts::glyph_position::{GlyphLayout, TextDirection};
|
||||
use allsorts::outline::OutlineBuilder;
|
||||
use allsorts::pathfinder_geometry::{line_segment::LineSegment2F, vector::Vector2F};
|
||||
use allsorts::tables::{glyf::GlyfTable, loca::LocaTable};
|
||||
use allsorts::tables::{FontTableProvider, SfntVersion};
|
||||
use allsorts::{tag, Font as AllsortsFont};
|
||||
use canary_types::{Color, Rect, Vec2};
|
||||
use lyon::path::Path;
|
||||
use ouroboros::self_referencing;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct GlyphCache {
|
||||
units_per_em: f32,
|
||||
glyphs: RwLock<Vec<CachedGlyph>>,
|
||||
}
|
||||
pub mod glyph;
|
||||
|
||||
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 {
|
||||
path: Path,
|
||||
bounding_box: Rect,
|
||||
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 {
|
||||
x: position.x,
|
||||
y: 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;
|
||||
|
||||
// TODO clean this up with helper math methods?
|
||||
let bb = &mut self.bounding_box;
|
||||
|
||||
if point.x < bb.bl.x {
|
||||
bb.bl.x = point.x;
|
||||
}
|
||||
|
||||
if point.x > bb.tr.x {
|
||||
bb.tr.x = point.x;
|
||||
}
|
||||
|
||||
if point.y < bb.bl.y {
|
||||
bb.bl.y = point.y;
|
||||
}
|
||||
|
||||
if point.y > bb.tr.y {
|
||||
bb.tr.y = point.y;
|
||||
}
|
||||
|
||||
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 {
|
||||
vertices: Vec<Vec2>,
|
||||
indices: Vec<u32>,
|
||||
}
|
||||
use glyph::GlyphCache;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct GlyphPosition {
|
||||
pub face: u16,
|
||||
pub index: u16,
|
||||
pub hori_advance: i32,
|
||||
pub vert_advance: i32,
|
||||
|
@ -208,27 +27,34 @@ pub struct GlyphPosition {
|
|||
}
|
||||
|
||||
pub struct TextLayout {
|
||||
pub font_id: u32,
|
||||
pub bounds: Rect,
|
||||
pub glyphs: Vec<GlyphPosition>,
|
||||
}
|
||||
|
||||
pub struct Font {
|
||||
font_id: u32,
|
||||
data: Mutex<FontData>,
|
||||
glyph_cache: GlyphCache,
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn load(
|
||||
face_index: u16,
|
||||
font_id: u32,
|
||||
script: u32,
|
||||
direction: TextDirection,
|
||||
vertical: bool,
|
||||
file_buffer: Vec<u8>,
|
||||
) -> Self {
|
||||
let mut data = FontData::load(face_index, script, direction, vertical, file_buffer);
|
||||
let mut data = FontData::load(script, direction, vertical, file_buffer);
|
||||
let glyph_cache = data.with_inner_mut(|font| GlyphCache::new(font));
|
||||
let data = Mutex::new(data);
|
||||
Self { data, glyph_cache }
|
||||
|
||||
Self {
|
||||
font_id,
|
||||
data,
|
||||
glyph_cache,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape(&self, text: &str) -> TextLayout {
|
||||
|
@ -273,6 +99,7 @@ impl Font {
|
|||
}
|
||||
|
||||
TextLayout {
|
||||
font_id: self.font_id,
|
||||
bounds,
|
||||
glyphs: positions,
|
||||
}
|
||||
|
@ -327,7 +154,6 @@ impl Font {
|
|||
#[self_referencing]
|
||||
pub struct FontData {
|
||||
file_buffer: Vec<u8>,
|
||||
face_index: u16,
|
||||
script: u32,
|
||||
direction: TextDirection,
|
||||
vertical: bool,
|
||||
|
@ -347,7 +173,6 @@ pub struct FontData {
|
|||
|
||||
impl FontData {
|
||||
pub fn load(
|
||||
face_index: u16,
|
||||
script: u32,
|
||||
direction: TextDirection,
|
||||
vertical: bool,
|
||||
|
@ -355,7 +180,6 @@ impl FontData {
|
|||
) -> Self {
|
||||
FontDataBuilder {
|
||||
file_buffer,
|
||||
face_index,
|
||||
script,
|
||||
direction,
|
||||
vertical,
|
||||
|
@ -371,7 +195,6 @@ impl FontData {
|
|||
}
|
||||
|
||||
pub fn shape(&mut self, text: &str) -> Vec<GlyphPosition> {
|
||||
let face_index = *self.borrow_face_index() as u16;
|
||||
let presentation = MatchingPresentation::Required;
|
||||
let script = *self.borrow_script();
|
||||
let lang_tag = None;
|
||||
|
@ -390,7 +213,6 @@ impl FontData {
|
|||
let mut glyphs = Vec::with_capacity(positions.len());
|
||||
for (glyph, position) in infos.iter().zip(&positions) {
|
||||
glyphs.push(GlyphPosition {
|
||||
face: face_index,
|
||||
index: glyph.glyph.glyph_index,
|
||||
hori_advance: position.hori_advance,
|
||||
vert_advance: position.vert_advance,
|
||||
|
@ -407,6 +229,7 @@ impl FontData {
|
|||
pub struct FontStore {
|
||||
source: Box<dyn font_kit::source::Source>,
|
||||
loaded: Mutex<HashMap<String, Arc<Font>>>,
|
||||
current_index: AtomicU32,
|
||||
}
|
||||
|
||||
impl Default for FontStore {
|
||||
|
@ -423,6 +246,7 @@ impl FontStore {
|
|||
Self {
|
||||
source,
|
||||
loaded: Default::default(),
|
||||
current_index: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,7 +281,7 @@ impl FontStore {
|
|||
};
|
||||
|
||||
let script = tag::LATN;
|
||||
let face_index = 0;
|
||||
let face_index = self.current_index.fetch_add(1, Ordering::Relaxed);
|
||||
let direction = TextDirection::LeftToRight;
|
||||
let vertical = false;
|
||||
let font = Font::load(face_index, script, direction, vertical, font_data.clone());
|
Loading…
Reference in New Issue