Skip to content

Commit

Permalink
[skrifa] tthint tests batch 2
Browse files Browse the repository at this point in the history
More work on #803

Tests for move_point(), move_zp2_point(), move_original(), point_displacement(), SHP, SHC, SHZ, SHPIX, MSIRP, MDAP
  • Loading branch information
dfrg committed Mar 21, 2024
1 parent 7c03942 commit e922781
Show file tree
Hide file tree
Showing 3 changed files with 340 additions and 5 deletions.
2 changes: 1 addition & 1 deletion skrifa/src/outline/glyf/hint/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ mod mock {
unscaled: vec![Default::default(); 32],
points: vec![Default::default(); 64],
point_flags: vec![Default::default(); 32],
contours: vec![32],
contours: vec![31],
twilight: vec![Default::default(); 32],
twilight_flags: vec![Default::default(); 32],
}
Expand Down
156 changes: 153 additions & 3 deletions skrifa/src/outline/glyf/hint/engine/outline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,9 +824,11 @@ impl<'a> Engine<'a> {

#[cfg(test)]
mod tests {
use raw::tables::glyf::PointMarker;

use super::{super::MockEngine, CoordAxis};
use super::{super::MockEngine, math, CoordAxis, Engine, ZonePointer};
use raw::{
tables::glyf::PointMarker,
types::{F26Dot6, Point},
};

#[test]
fn flip_point() {
Expand Down Expand Up @@ -960,4 +962,152 @@ mod tests {
// untouch point 2 in both axes
untouch(2, 1, 1, PointMarker::TOUCHED);
}

#[test]
fn shp() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
set_test_vectors(&mut engine);
engine.graphics.backward_compatibility = false;
engine.graphics.zp0 = ZonePointer::Glyph;
engine.graphics.zp2 = ZonePointer::Glyph;
engine.graphics.rp2 = 1;
let point = engine.graphics.zones[1].point_mut(1).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
engine.value_stack.push(1).unwrap();
engine.op_shp(0).unwrap();
let point = engine.graphics.zones[1].point(1).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(136, -254));
}

#[test]
fn shc() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
set_test_vectors(&mut engine);
engine.graphics.backward_compatibility = false;
engine.graphics.zp0 = ZonePointer::Glyph;
engine.graphics.zp2 = ZonePointer::Glyph;
engine.graphics.rp2 = 1;
let point = engine.graphics.zones[1].point_mut(1).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
engine.value_stack.push(0).unwrap();
engine.op_shc(0).unwrap();
let points = engine.graphics.zones[1]
.points
.iter()
.map(|p| p.map(F26Dot6::to_bits))
.take(3)
.collect::<Vec<_>>();
assert_eq!(
points,
&[Point::new(4, 2), Point::new(132, -256), Point::new(4, 2),]
);
}

#[test]
fn shz() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
set_test_vectors(&mut engine);
engine.graphics.backward_compatibility = false;
engine.graphics.zp0 = ZonePointer::Glyph;
engine.graphics.zp2 = ZonePointer::Glyph;
engine.graphics.rp2 = 1;
let point = engine.graphics.zones[1].point_mut(1).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
engine.value_stack.push(0).unwrap();
engine.op_shz(0).unwrap();
let points = engine.graphics.zones[1]
.points
.iter()
.map(|p| p.map(F26Dot6::to_bits))
.take(3)
.collect::<Vec<_>>();
assert_eq!(
points,
&[Point::new(4, 2), Point::new(132, -256), Point::new(4, 2),]
);
}

#[test]
fn shpix() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
set_test_vectors(&mut engine);
engine.graphics.backward_compatibility = false;
engine.graphics.zp2 = ZonePointer::Glyph;
let point = engine.graphics.zones[1].point_mut(1).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
// point index
engine.value_stack.push(1).unwrap();
// amount to move in pixels along freedom vector
engine.value_stack.push(42).unwrap();
engine.op_shpix().unwrap();
let point = engine.graphics.zones[1].point(1).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(170, -237));
}

#[test]
fn msirp() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
set_test_vectors(&mut engine);
engine.graphics.backward_compatibility = false;
engine.graphics.zp0 = ZonePointer::Glyph;
engine.graphics.zp1 = ZonePointer::Glyph;
let point = engine.graphics.zones[1].point_mut(1).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
// point index
engine.value_stack.push(1).unwrap();
// amount to move in pixels along freedom vector
engine.value_stack.push(-42).unwrap();
engine.op_msirp(0).unwrap();
let point = engine.graphics.zones[1].point(1).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(91, -277));
assert_eq!(engine.graphics.rp0, 0);
// opcode with bit 0 set changes rp0 to point_ix
engine.value_stack.push(4).unwrap();
engine.value_stack.push(0).unwrap();
engine.op_msirp(1).unwrap();
assert_eq!(engine.graphics.rp0, 4);
}

#[test]
fn mdap() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
set_test_vectors(&mut engine);
engine.graphics.backward_compatibility = false;
engine.graphics.zp0 = ZonePointer::Glyph;
// with rounding
let point = engine.graphics.zones[1].point_mut(1).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
engine.value_stack.push(1).unwrap();
engine.op_mdap(1).unwrap();
let point = engine.graphics.zones[1].point(1).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(128, -258));
// without rounding
let point = engine.graphics.zones[1].point_mut(2).unwrap();
point.x = F26Dot6::from_bits(132);
point.y = F26Dot6::from_bits(-256);
engine.value_stack.push(2).unwrap();
engine.op_mdap(0).unwrap();
let point = engine.graphics.zones[1].point(2).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(132, -256));
}

fn set_test_vectors(engine: &mut Engine) {
let v = math::normalize14(100, 50);
engine.graphics.proj_vector = v;
engine.graphics.dual_proj_vector = v;
engine.graphics.freedom_vector = v;
engine.graphics.update_projection_state();
}
}
187 changes: 186 additions & 1 deletion skrifa/src/outline/glyf/hint/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ impl GraphicsState<'_> {
}
}

#[derive(PartialEq, Debug)]
pub(crate) struct PointDisplacement {
pub zone: ZonePointer,
pub point_ix: usize,
Expand All @@ -550,7 +551,7 @@ impl CoordAxis {

#[cfg(test)]
mod tests {
use super::{CoordAxis, Zone};
use super::{math, CoordAxis, GraphicsState, PointDisplacement, Zone, ZonePointer};
use raw::{
tables::glyf::{PointFlags, PointMarker},
types::{F26Dot6, Point},
Expand Down Expand Up @@ -637,6 +638,190 @@ mod tests {
);
}

#[test]
fn move_point_x() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(100, 0);
let point_ix = 0;
let orig_x = gs.zones[1].point(point_ix).unwrap().x;
let dx = F26Dot6::from_bits(10);
// backward compatibility is on by default and we don't move x coord
gs.move_point(ZonePointer::Glyph, 0, dx).unwrap();
assert_eq!(orig_x, gs.zones[1].point(point_ix).unwrap().x);
// disable so we actually move
gs.backward_compatibility = false;
gs.move_point(ZonePointer::Glyph, 0, dx).unwrap();
let new_x = gs.zones[1].point(point_ix).unwrap().x;
assert_ne!(orig_x, new_x);
assert_eq!(new_x, orig_x + dx)
}

#[test]
fn move_point_y() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(0, 100);
let point_ix = 0;
let orig_y = gs.zones[1].point(point_ix).unwrap().y;
let dy = F26Dot6::from_bits(10);
// movement in y is prevented post-iup when backward
// compatibility is enabled
gs.did_iup_x = true;
gs.did_iup_y = true;
gs.move_point(ZonePointer::Glyph, 0, dy).unwrap();
assert_eq!(orig_y, gs.zones[1].point(point_ix).unwrap().y);
// allow movement
gs.did_iup_x = false;
gs.did_iup_y = false;
gs.move_point(ZonePointer::Glyph, 0, dy).unwrap();
let new_y = gs.zones[1].point(point_ix).unwrap().y;
assert_ne!(orig_y, new_y);
assert_eq!(new_y, orig_y + dy)
}

#[test]
fn move_point_x_and_y() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(100, 50);
let point_ix = 0;
let orig_point = gs.zones[1].point(point_ix).unwrap();
let dist = F26Dot6::from_bits(10);
// prevent movement in x and y
gs.did_iup_x = true;
gs.did_iup_y = true;
gs.move_point(ZonePointer::Glyph, 0, dist).unwrap();
assert_eq!(orig_point, gs.zones[1].point(point_ix).unwrap());
// allow movement
gs.backward_compatibility = false;
gs.did_iup_x = false;
gs.did_iup_y = false;
gs.move_point(ZonePointer::Glyph, 0, dist).unwrap();
let point = gs.zones[1].point(point_ix).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(4, -16));
}

#[test]
fn move_original_x() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(100, 0);
let point_ix = 0;
let orig_x = gs.zones[1].original(point_ix).unwrap().x;
let dx = F26Dot6::from_bits(10);
gs.move_original(ZonePointer::Glyph, 0, dx).unwrap();
let new_x = gs.zones[1].original(point_ix).unwrap().x;
assert_eq!(new_x, orig_x + dx)
}

#[test]
fn move_original_y() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(0, 100);
let point_ix = 0;
let orig_y = gs.zones[1].original(point_ix).unwrap().y;
let dy = F26Dot6::from_bits(10);
gs.move_original(ZonePointer::Glyph, 0, dy).unwrap();
let new_y = gs.zones[1].original(point_ix).unwrap().y;
assert_eq!(new_y, orig_y + dy)
}

#[test]
fn move_original_x_and_y() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(100, 50);
let point_ix = 0;
let dist = F26Dot6::from_bits(10);
gs.move_original(ZonePointer::Glyph, 0, dist).unwrap();
let point = gs.zones[1].original(point_ix).unwrap();
assert_eq!(point.map(F26Dot6::to_bits), Point::new(9, 4));
}

#[test]
fn move_zp2_point() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(100, 50);
gs.zp2 = ZonePointer::Glyph;
let point_ix = 0;
let orig_point = gs.zones[1].point(point_ix).unwrap();
let dx = F26Dot6::from_bits(10);
let dy = F26Dot6::from_bits(-10);
// prevent movement in x and y
gs.did_iup_x = true;
gs.did_iup_y = true;
gs.move_zp2_point(point_ix, dx, dy, false).unwrap();
assert_eq!(orig_point, gs.zones[1].point(point_ix).unwrap());
// allow movement
gs.backward_compatibility = false;
gs.did_iup_x = false;
gs.did_iup_y = false;
gs.move_zp2_point(point_ix, dx, dy, false).unwrap();
let point = gs.zones[1].point(point_ix).unwrap();
assert_eq!(point, orig_point + Point::new(dx, dy));
}

#[test]
fn point_displacement() {
let mut mock = MockGraphicsState::new();
let mut gs = mock.graphics_state(100, 50);
gs.zp0 = ZonePointer::Glyph;
gs.rp1 = 0;
assert_eq!(
gs.point_displacement(1).unwrap(),
PointDisplacement {
zone: ZonePointer::Glyph,
point_ix: 0,
dx: F26Dot6::from_f64(-0.1875),
dy: F26Dot6::from_f64(-0.09375),
}
);
gs.rp2 = 2;
assert_eq!(
gs.point_displacement(0).unwrap(),
PointDisplacement {
zone: ZonePointer::Glyph,
point_ix: 2,
dx: F26Dot6::from_f64(0.390625),
dy: F26Dot6::from_f64(0.203125),
}
);
}

struct MockGraphicsState {
points: [Point<F26Dot6>; 3],
original: [Point<F26Dot6>; 3],
contours: [u16; 1],
flags: [PointFlags; 3],
}

impl MockGraphicsState {
fn new() -> Self {
Self {
points: f26dot6_points([(-5, -20), (10, 10), (20, 20)]),
original: f26dot6_points([(0, 0), (10, 10), (20, -42)]),
flags: [PointFlags::default(); 3],
contours: [3],
}
}

fn graphics_state(&mut self, fv_x: i32, fv_y: i32) -> GraphicsState {
let glyph = Zone {
unscaled: &mut [],
original: &mut self.original,
points: &mut self.points,
contours: &self.contours,
flags: &mut self.flags,
};
let v = math::normalize14(fv_x, fv_y);
let mut gs = GraphicsState {
zones: [Zone::default(), glyph],
freedom_vector: v,
proj_vector: v,
zp0: ZonePointer::Glyph,
..Default::default()
};
gs.update_projection_state();
gs
}
}

fn point_markers() -> [PointFlags; 2] {
let untouched = PointFlags::default();
let mut touched = untouched;
Expand Down

0 comments on commit e922781

Please sign in to comment.