Commit efd3797f authored by Alex Butler's avatar Alex Butler

Merge branch 'outline' into 'master'

Implement Support for building outlines

See merge request !153
parents c8ab1842 9d339450
Pipeline #7372 passed with stages
in 4 minutes and 45 seconds
......@@ -115,6 +115,8 @@ use core::fmt;
#[cfg(all(feature = "libm-math", not(feature = "std")))]
use crate::nostd_float::FloatExt;
pub use ttf_parser::OutlineBuilder;
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct GlyphId(pub u16);
......@@ -242,6 +244,18 @@ impl<'font> ScaledGlyph<'font> {
&self.g
}
/// Builds the outline of the glyph with the builder specified. Returns `false` when the
/// outline is either malformed or empty.
pub fn build_outline(&self, builder: &mut impl OutlineBuilder) -> bool {
let mut outliner =
crate::outliner::OutlineScaler::new(builder, vector(self.scale.x, -self.scale.y));
self.font()
.inner()
.outline_glyph(self.id().into(), &mut outliner)
.is_some()
}
/// Augments this glyph with positioning information, making methods that
/// depend on the position of the glyph available.
pub fn positioned(self, p: Point<f32>) -> PositionedGlyph<'font> {
......@@ -316,7 +330,7 @@ impl<'font> ScaledGlyph<'font> {
#[inline]
fn pixel_bounds_at(&self, p: Point<f32>) -> Option<Rect<i32>> {
// Use subpixel fraction in floor/ceil rounding to elimate rounding error
// Use subpixel fraction in floor/ceil rounding to eliminate rounding error
// from identical subpixel positions
let (x_trunc, x_fract) = (p.x.trunc() as i32, p.x.fract());
let (y_trunc, y_fract) = (p.y.trunc() as i32, p.y.fract());
......@@ -386,6 +400,22 @@ impl<'font> PositionedGlyph<'font> {
self.position
}
/// Builds the outline of the glyph with the builder specified. Returns `false` when the
/// outline is either malformed or empty.
pub fn build_outline(&self, builder: &mut impl OutlineBuilder) -> bool {
let bb = if let Some(bb) = self.bb.as_ref() {
bb
} else {
return false;
};
let offset = vector(bb.min.x as f32, bb.min.y as f32);
let mut outliner = crate::outliner::OutlineTranslator::new(builder, self.position - offset);
self.sg.build_outline(&mut outliner)
}
/// Rasterises this glyph. For each pixel in the rect given by
/// `pixel_bounding_box()`, `o` is called:
///
......@@ -416,18 +446,9 @@ impl<'font> PositionedGlyph<'font> {
let width = (bb.max.x - bb.min.x) as u32;
let height = (bb.max.y - bb.min.y) as u32;
let offset = vector(bb.min.x as f32, bb.min.y as f32);
let mut outliner = crate::outliner::OutlineRasterizer::new(
self.position - offset,
self.sg.scale,
width as _,
height as _,
);
let mut outliner = crate::outliner::OutlineRasterizer::new(width as _, height as _);
self.font()
.inner()
.outline_glyph(self.id().into(), &mut outliner);
self.build_outline(&mut outliner);
outliner.rasterizer.for_each_pixel_2d(o);
}
......@@ -459,7 +480,7 @@ impl fmt::Debug for PositionedGlyph<'_> {
}
/// Defines the size of a rendered face of a font, in pixels, horizontally and
/// vertically. A vertical scale of `y` pixels means that the distance betwen
/// vertically. A vertical scale of `y` pixels means that the distance between
/// the ascent and descent lines (see `VMetrics`) of the face will be `y`
/// pixels. If `x` and `y` are equal the scaling is uniform. Non-uniform scaling
/// by a factor *f* in the horizontal direction is achieved by setting `x` equal
......
use crate::{Point, Vector};
use ab_glyph_rasterizer::{point as ab_point, Point as AbPoint, Rasterizer};
use ttf_parser::OutlineBuilder;
pub(crate) struct OutlineScaler<'b, T: ?Sized> {
inner: &'b mut T,
scale: Vector<f32>,
}
impl<'b, T: ?Sized> OutlineScaler<'b, T> {
pub(crate) fn new(inner: &'b mut T, scale: Vector<f32>) -> Self {
Self { inner, scale }
}
}
impl<T: OutlineBuilder + ?Sized> OutlineBuilder for OutlineScaler<'_, T> {
fn move_to(&mut self, x: f32, y: f32) {
self.inner.move_to(x * self.scale.x, y * self.scale.y)
}
fn line_to(&mut self, x1: f32, y1: f32) {
self.inner.line_to(x1 * self.scale.x, y1 * self.scale.y)
}
fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
self.inner.quad_to(
x1 * self.scale.x,
y1 * self.scale.y,
x2 * self.scale.x,
y2 * self.scale.y,
)
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) {
self.inner.curve_to(
x1 * self.scale.x,
y1 * self.scale.y,
x2 * self.scale.x,
y2 * self.scale.y,
x3 * self.scale.x,
y3 * self.scale.y,
)
}
fn close(&mut self) {
self.inner.close()
}
}
pub(crate) struct OutlineTranslator<'b, T: ?Sized> {
inner: &'b mut T,
translation: Point<f32>,
}
impl<'b, T: ?Sized> OutlineTranslator<'b, T> {
pub(crate) fn new(inner: &'b mut T, translation: Point<f32>) -> Self {
Self { inner, translation }
}
}
impl<T: OutlineBuilder + ?Sized> OutlineBuilder for OutlineTranslator<'_, T> {
fn move_to(&mut self, x: f32, y: f32) {
self.inner
.move_to(x + self.translation.x, y + self.translation.y)
}
fn line_to(&mut self, x1: f32, y1: f32) {
self.inner
.line_to(x1 + self.translation.x, y1 + self.translation.y)
}
fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
self.inner.quad_to(
x1 + self.translation.x,
y1 + self.translation.y,
x2 + self.translation.x,
y2 + self.translation.y,
)
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) {
self.inner.curve_to(
x1 + self.translation.x,
y1 + self.translation.y,
x2 + self.translation.x,
y2 + self.translation.y,
x3 + self.translation.x,
y3 + self.translation.y,
)
}
fn close(&mut self) {
self.inner.close()
}
}
pub(crate) struct OutlineRasterizer {
pub(crate) rasterizer: Rasterizer,
last: AbPoint,
last_move: Option<AbPoint>,
position: AbPoint,
scale: Vector<f32>,
}
impl OutlineRasterizer {
pub(crate) fn new(
position: Point<f32>,
scale: Vector<f32>,
width: usize,
height: usize,
) -> Self {
pub(crate) fn new(width: usize, height: usize) -> Self {
Self {
rasterizer: Rasterizer::new(width, height),
last: ab_point(0.0, 0.0),
last_move: None,
position: ab_point(position.x, position.y),
scale,
}
}
}
impl ttf_parser::OutlineBuilder for OutlineRasterizer {
impl OutlineBuilder for OutlineRasterizer {
fn move_to(&mut self, x: f32, y: f32) {
self.last = AbPoint {
x: x as f32 * self.scale.x + self.position.x,
y: -y as f32 * self.scale.y + self.position.y,
};
self.last = AbPoint { x, y };
self.last_move = Some(self.last);
}
fn line_to(&mut self, x1: f32, y1: f32) {
let p1 = AbPoint {
x: x1 as f32 * self.scale.x + self.position.x,
y: -y1 as f32 * self.scale.y + self.position.y,
};
let p1 = AbPoint { x: x1, y: y1 };
self.rasterizer.draw_line(self.last, p1);
self.last = p1;
}
fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
let p1 = AbPoint {
x: x1 as f32 * self.scale.x + self.position.x,
y: -y1 as f32 * self.scale.y + self.position.y,
};
let p2 = AbPoint {
x: x2 as f32 * self.scale.x + self.position.x,
y: -y2 as f32 * self.scale.y + self.position.y,
};
let p1 = AbPoint { x: x1, y: y1 };
let p2 = AbPoint { x: x2, y: y2 };
self.rasterizer.draw_quad(self.last, p1, p2);
self.last = p2;
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) {
let p1 = AbPoint {
x: x1 as f32 * self.scale.x + self.position.x,
y: -y1 as f32 * self.scale.y + self.position.y,
};
let p2 = AbPoint {
x: x2 as f32 * self.scale.x + self.position.x,
y: -y2 as f32 * self.scale.y + self.position.y,
};
let p3 = AbPoint {
x: x3 as f32 * self.scale.x + self.position.x,
y: -y3 as f32 * self.scale.y + self.position.y,
};
let p1 = AbPoint { x: x1, y: y1 };
let p2 = AbPoint { x: x2, y: y2 };
let p3 = AbPoint { x: x3, y: y3 };
self.rasterizer.draw_cubic(self.last, p1, p2, p3);
self.last = p3;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment