Compute glyph bounding boxes from paths

This commit is contained in:
marceline-cramer 2022-07-18 18:43:49 -06:00
parent 43b6aca29a
commit bf6228f277
2 changed files with 105 additions and 14 deletions

View File

@ -1,8 +1,8 @@
#[macro_use]
extern crate num_derive;
pub use num_traits;
use bytemuck::{Pod, Zeroable};
pub use num_traits;
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
@ -11,6 +11,18 @@ pub struct Vec2 {
pub y: f32,
}
impl Vec2 {
pub const INFINITY: Self = Self {
x: f32::INFINITY,
y: f32::INFINITY,
};
pub const NEG_INFINITY: Self = Self {
x: f32::NEG_INFINITY,
y: f32::NEG_INFINITY,
};
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct Color {
@ -65,6 +77,13 @@ pub struct Rect {
pub tr: Vec2,
}
impl Rect {
pub const NEG_INFINITY: Self = Self {
bl: Vec2::INFINITY,
tr: Vec2::NEG_INFINITY,
};
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct GlyphPosition {
@ -82,7 +101,10 @@ mod glam_interop {
impl From<glam::Vec2> for Vec2 {
fn from(other: glam::Vec2) -> Self {
Self { x: other.x, y: other.y }
Self {
x: other.x,
y: other.y,
}
}
}

View File

@ -67,6 +67,7 @@ impl GlyphCache {
pub struct CachedGlyph {
path: Path,
bounding_box: Rect,
mesh: Option<GlyphMesh>,
}
@ -74,9 +75,14 @@ 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 { path, mesh: None }
Self {
bounding_box,
path,
mesh: None,
}
}
pub fn get_mesh(&mut self) -> &GlyphMesh {
@ -115,6 +121,7 @@ impl CachedGlyph {
pub struct OutlineSink {
units_per_em: f32,
bounding_box: Rect,
builder: lyon::path::path::Builder,
}
@ -122,12 +129,13 @@ 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(
&self,
&mut self,
line: &LineSegment2F,
) -> (lyon::geom::Point<f32>, lyon::geom::Point<f32>) {
(
@ -136,8 +144,29 @@ impl OutlineSink {
)
}
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
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
}
}
@ -199,7 +228,51 @@ impl Font {
}
pub fn shape(&self, text: &str) -> TextLayout {
self.data.lock().shape(text)
let positions = self.data.lock().shape(text);
let glyphs = self.glyph_cache.glyphs.read();
let mut bounds = Rect::NEG_INFINITY;
let units_per_em = self.glyph_cache.units_per_em;
let mut xcur = 0;
let mut ycur = 0;
for position in positions.iter() {
let xpos = xcur + position.xoff;
let ypos = ycur + position.yoff;
xcur += position.hori_advance;
ycur += position.vert_advance;
let scale = TEXT_SCALE / units_per_em;
let xpos = xpos as f32 * scale - 0.9;
let ypos = ypos as f32 * scale + 0.9;
let mut bb = glyphs.get(position.index as usize).unwrap().bounding_box;
bb.bl.x = bb.bl.x * TEXT_SCALE + xpos;
bb.bl.y = bb.bl.y * TEXT_SCALE + ypos;
bb.tr.x = bb.tr.x * TEXT_SCALE + xpos;
bb.tr.y = bb.tr.y * TEXT_SCALE + ypos;
// TODO use euclid instead
if bounds.bl.x > bb.bl.x {
bounds.bl.x = bb.bl.x;
}
if bounds.bl.y > bb.bl.y {
bounds.bl.y = bb.bl.y;
}
if bounds.tr.x < bb.tr.x {
bounds.tr.x = bb.tr.x;
}
if bounds.tr.y < bb.tr.y {
bounds.tr.y = bb.tr.y;
}
}
TextLayout {
bounds,
glyphs: positions,
}
}
pub fn draw(&self, positions: &[GlyphPosition], yoff: f32) -> Vec<DrawCommand> {
@ -288,7 +361,7 @@ impl FontData {
.build()
}
pub fn shape(&mut self, text: &str) -> TextLayout {
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();
@ -298,7 +371,7 @@ impl FontData {
let direction = *self.borrow_direction();
let vertical = *self.borrow_vertical();
let glyphs = self.with_inner_mut(|font| {
self.with_inner_mut(|font| {
let mapped = font.map_glyphs(text, script, presentation);
let infos = font
.shape(mapped, script, lang_tag, &features, kerning)
@ -318,11 +391,7 @@ impl FontData {
}
glyphs
});
let bounds = Rect::default();
TextLayout { bounds, glyphs }
})
}
}