use crate::platform::{self, abs, atan2, f32x4, sqrt};
use crate::{Glyph, OutlineBounds};
use alloc::vec;
use alloc::vec::*;
#[derive(Copy, Clone, PartialEq, Debug)]
struct AABB {
xmin: f32,
xmax: f32,
ymin: f32,
ymax: f32,
}
impl Default for AABB {
fn default() -> Self {
AABB {
xmin: 0.0,
xmax: 0.0,
ymin: 0.0,
ymax: 0.0,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
struct CubeCurve {
a: Point,
b: Point,
c: Point,
d: Point,
}
impl CubeCurve {
fn new(a: Point, b: Point, c: Point, d: Point) -> CubeCurve {
CubeCurve {
a,
b,
c,
d,
}
}
fn scale(&self, scale: f32) -> CubeCurve {
CubeCurve {
a: self.a.scale(scale),
b: self.b.scale(scale),
c: self.c.scale(scale),
d: self.d.scale(scale),
}
}
fn is_flat(&self, threshold: f32) -> bool {
let (d1, d2, d3, d4) = f32x4::new(
self.a.distance_squared(self.b),
self.b.distance_squared(self.c),
self.c.distance_squared(self.d),
self.a.distance_squared(self.d),
)
.sqrt()
.copied();
(d1 + d2 + d3) < threshold * d4
}
fn split(&self) -> (CubeCurve, CubeCurve) {
let q0 = self.a.midpoint(self.b);
let q1 = self.b.midpoint(self.c);
let q2 = self.c.midpoint(self.d);
let r0 = q0.midpoint(q1);
let r1 = q1.midpoint(q2);
let s0 = r0.midpoint(r1);
(CubeCurve::new(self.a, q0, r0, s0), CubeCurve::new(s0, r1, q2, self.d))
}
fn point(&self, t: f32) -> Point {
let tm = 1.0 - t;
let a = tm * tm * tm;
let b = 3.0 * (tm * tm) * t;
let c = 3.0 * tm * (t * t);
let d = t * t * t;
let x = a * self.a.x + b * self.b.x + c * self.c.x + d * self.d.x;
let y = a * self.a.y + b * self.b.y + c * self.c.y + d * self.d.y;
Point::new(x, y)
}
fn slope(&self, t: f32) -> (f32, f32) {
let tm = 1.0 - t;
let a = 3.0 * (tm * tm);
let b = 6.0 * tm * t;
let c = 3.0 * (t * t);
let x = a * (self.b.x - self.a.x) + b * (self.c.x - self.b.x) + c * (self.d.x - self.c.x);
let y = a * (self.b.y - self.a.y) + b * (self.c.y - self.b.y) + c * (self.d.y - self.c.y);
(x, y)
}
fn angle(&self, t: f32) -> f32 {
let (x, y) = self.slope(t);
abs(atan2(x, y))
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
struct QuadCurve {
a: Point,
b: Point,
c: Point,
}
impl QuadCurve {
fn new(a: Point, b: Point, c: Point) -> QuadCurve {
QuadCurve {
a,
b,
c,
}
}
fn scale(&self, scale: f32) -> QuadCurve {
QuadCurve {
a: self.a.scale(scale),
b: self.b.scale(scale),
c: self.c.scale(scale),
}
}
fn is_flat(&self, threshold: f32) -> bool {
let (d1, d2, d3, _) = f32x4::new(
self.a.distance_squared(self.b),
self.b.distance_squared(self.c),
self.a.distance_squared(self.c),
1.0,
)
.sqrt()
.copied();
(d1 + d2) < threshold * d3
}
fn split(&self) -> (QuadCurve, QuadCurve) {
let q0 = self.a.midpoint(self.b);
let q1 = self.b.midpoint(self.c);
let r0 = q0.midpoint(q1);
(QuadCurve::new(self.a, q0, r0), QuadCurve::new(r0, q1, self.c))
}
fn point(&self, t: f32) -> Point {
let tm = 1.0 - t;
let a = tm * tm;
let b = 2.0 * tm * t;
let c = t * t;
let x = a * self.a.x + b * self.b.x + c * self.c.x;
let y = a * self.a.y + b * self.b.y + c * self.c.y;
Point::new(x, y)
}
fn slope(&self, t: f32) -> (f32, f32) {
let tm = 1.0 - t;
let a = 2.0 * tm;
let b = 2.0 * t;
let x = a * (self.b.x - self.a.x) + b * (self.c.x - self.b.x);
let y = a * (self.b.y - self.a.y) + b * (self.c.y - self.b.y);
(x, y)
}
fn angle(&self, t: f32) -> f32 {
let (x, y) = self.slope(t);
abs(atan2(x, y))
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Point {
pub x: f32,
pub y: f32,
}
impl Default for Point {
fn default() -> Self {
Point {
x: 0.0,
y: 0.0,
}
}
}
impl Point {
pub fn new(x: f32, y: f32) -> Point {
Point {
x,
y,
}
}
pub fn scale(&self, scale: f32) -> Point {
Point {
x: self.x * scale,
y: self.y * scale,
}
}
pub fn distance_squared(&self, other: Point) -> f32 {
let x = self.x - other.x;
let y = self.y - other.y;
x * x + y * y
}
pub fn distance(&self, other: Point) -> f32 {
let x = self.x - other.x;
let y = self.y - other.y;
sqrt(x * x + y * y)
}
pub fn midpoint(&self, other: Point) -> Point {
Point {
x: (self.x + other.x) / 2.0,
y: (self.y + other.y) / 2.0,
}
}
}
#[derive(Copy, Clone)]
pub struct Line {
pub coords: f32x4,
pub nudge: f32x4,
pub adjustment: f32x4,
pub params: f32x4,
}
impl Line {
pub fn new(start: Point, end: Point) -> Line {
const FLOOR_NUDGE: u32 = 0;
const CEIL_NUDGE: u32 = 1;
let (x_start_nudge, x_first_adj) = if end.x >= start.x {
(FLOOR_NUDGE, 1.0)
} else {
(CEIL_NUDGE, 0.0)
};
let (y_start_nudge, y_first_adj) = if end.y >= start.y {
(FLOOR_NUDGE, 1.0)
} else {
(CEIL_NUDGE, 0.0)
};
let x_end_nudge = if end.x > start.x {
CEIL_NUDGE
} else {
FLOOR_NUDGE
};
let y_end_nudge = if end.y > start.y {
CEIL_NUDGE
} else {
FLOOR_NUDGE
};
let dx = end.x - start.x;
let dy = end.y - start.y;
let tdx = if dx == 0.0 {
core::f32::MAX
} else {
1.0 / dx
};
let tdy = 1.0 / dy;
Line {
coords: f32x4::new(start.x, start.y, end.x, end.y),
nudge: f32x4::new_u32(x_start_nudge, y_start_nudge, x_end_nudge, y_end_nudge),
adjustment: f32x4::new(x_first_adj, y_first_adj, 0.0, 0.0),
params: f32x4::new(tdx, tdy, dx, dy),
}
}
fn reposition(&mut self, bounds: AABB, reverse: bool) {
let (mut x0, mut y0, mut x1, mut y1) = if !reverse {
self.coords.copied()
} else {
let (x0, y0, x1, y1) = self.coords.copied();
(x1, y1, x0, y0)
};
x0 -= bounds.xmin;
y0 -= bounds.ymax;
y0 = abs(y0);
x1 -= bounds.xmin;
y1 -= bounds.ymax;
y1 = abs(y1);
*self = Self::new(Point::new(x0, y0), Point::new(x1, y1));
}
}
#[derive(Clone)]
pub struct Geometry {
v_lines: Vec<Line>,
m_lines: Vec<Line>,
effective_bounds: AABB,
start_point: Point,
previous_point: Point,
area: f32,
reverse_points: bool,
max_area: f32,
}
struct Segment {
a: Point,
at: f32,
c: Point,
ct: f32,
}
impl Segment {
fn new(a: Point, at: f32, c: Point, ct: f32) -> Segment {
Segment {
a,
at,
c,
ct,
}
}
}
impl ttf_parser::OutlineBuilder for Geometry {
fn move_to(&mut self, x0: f32, y0: f32) {
let next_point = Point::new(x0, y0);
self.start_point = next_point;
self.previous_point = next_point;
}
fn line_to(&mut self, x0: f32, y0: f32) {
let next_point = Point::new(x0, y0);
self.push(self.previous_point, next_point);
self.previous_point = next_point;
}
fn quad_to(&mut self, x0: f32, y0: f32, x1: f32, y1: f32) {
let control_point = Point::new(x0, y0);
let next_point = Point::new(x1, y1);
let curve = QuadCurve::new(self.previous_point, control_point, next_point);
let mut stack = vec![Segment::new(self.previous_point, 0.0, next_point, 1.0)];
while let Some(seg) = stack.pop() {
let bt = (seg.at + seg.ct) * 0.5;
let b = curve.point(bt);
let area = (b.x - seg.a.x) * (seg.c.y - seg.a.y) - (seg.c.x - seg.a.x) * (b.y - seg.a.y);
if platform::abs(area) > self.max_area {
stack.push(Segment::new(seg.a, seg.at, b, bt));
stack.push(Segment::new(b, bt, seg.c, seg.ct));
} else {
self.push(seg.a, seg.c);
}
}
self.previous_point = next_point;
}
fn curve_to(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, x2: f32, y2: f32) {
let first_control = Point::new(x0, y0);
let second_control = Point::new(x1, y1);
let next_point = Point::new(x2, y2);
let curve = CubeCurve::new(self.previous_point, first_control, second_control, next_point);
let mut stack = vec![Segment::new(self.previous_point, 0.0, next_point, 1.0)];
while let Some(seg) = stack.pop() {
let bt = (seg.at + seg.ct) * 0.5;
let b = curve.point(bt);
let area = (b.x - seg.a.x) * (seg.c.y - seg.a.y) - (seg.c.x - seg.a.x) * (b.y - seg.a.y);
if platform::abs(area) > self.max_area {
stack.push(Segment::new(seg.a, seg.at, b, bt));
stack.push(Segment::new(b, bt, seg.c, seg.ct));
} else {
self.push(seg.a, seg.c);
}
}
self.previous_point = next_point;
}
fn close(&mut self) {
if self.start_point != self.previous_point {
self.push(self.previous_point, self.start_point);
}
self.previous_point = self.start_point;
}
}
impl Geometry {
pub fn new(scale: f32, units_per_em: f32) -> Geometry {
const ERROR_THRESHOLD: f32 = 3.0; let max_area = ERROR_THRESHOLD * 2.0 * (units_per_em / scale);
Geometry {
v_lines: Vec::new(),
m_lines: Vec::new(),
effective_bounds: AABB {
xmin: core::f32::MAX,
xmax: core::f32::MIN,
ymin: core::f32::MAX,
ymax: core::f32::MIN,
},
start_point: Point::default(),
previous_point: Point::default(),
area: 0.0,
reverse_points: false,
max_area,
}
}
fn push(&mut self, start: Point, end: Point) {
if start.y.to_bits() != end.y.to_bits() {
self.area += (end.y - start.y) * (end.x + start.x);
if start.x.to_bits() == end.x.to_bits() {
self.v_lines.push(Line::new(start, end));
} else {
self.m_lines.push(Line::new(start, end));
}
Self::recalculate_bounds(&mut self.effective_bounds, start.x, start.y);
Self::recalculate_bounds(&mut self.effective_bounds, end.x, end.y);
}
}
pub(crate) fn finalize(mut self, glyph: &mut Glyph) {
if self.v_lines.is_empty() && self.m_lines.is_empty() {
self.effective_bounds = AABB::default();
} else {
self.reverse_points = self.area > 0.0;
for line in self.v_lines.iter_mut().chain(self.m_lines.iter_mut()) {
line.reposition(self.effective_bounds, self.reverse_points);
}
self.v_lines.shrink_to_fit();
self.m_lines.shrink_to_fit();
}
glyph.v_lines = self.v_lines;
glyph.m_lines = self.m_lines;
glyph.bounds = OutlineBounds {
xmin: self.effective_bounds.xmin,
ymin: self.effective_bounds.ymin,
width: self.effective_bounds.xmax - self.effective_bounds.xmin,
height: self.effective_bounds.ymax - self.effective_bounds.ymin,
};
}
fn recalculate_bounds(bounds: &mut AABB, x: f32, y: f32) {
if x < bounds.xmin {
bounds.xmin = x;
}
if x > bounds.xmax {
bounds.xmax = x;
}
if y < bounds.ymin {
bounds.ymin = y;
}
if y > bounds.ymax {
bounds.ymax = y;
}
}
}