Compute glyph bounding boxes from paths
This commit is contained in:
parent
43b6aca29a
commit
bf6228f277
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
93
src/text.rs
93
src/text.rs
|
@ -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 }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue