tests.rs
raw
use guiduck_scene::paint::{Color, FontData};
use guiduck_scene::{Glyph, GlyphRun, Synthesis};
use skrifa::MetadataProvider;
use super::*;
const SIZE: f32 = 48.0;
/// One `l` from the sample face at the origin: a tall upright stem, so a lean
/// shows as its top moving and its foot staying put.
fn stem(synthesis: Synthesis, hint: bool) -> GlyphRun {
let data = guiduck_samples::sample_font();
let font = FontRef::from_index(data.as_ref(), 0).expect("the sample face");
let id = font.charmap().map('l').expect("the face has an l").to_u32();
GlyphRun {
font: FontData::new(data, 0),
size: SIZE,
brush: Color::BLACK.into(),
glyphs: vec![Glyph { id, x: 0.0, y: 0.0 }],
normalized_coords: Vec::new(),
hint,
synthesis,
}
}
fn path_bounds(run: &GlyphRun) -> tiny_skia::Rect {
let placement = RunPlacement::of(run, Affine::IDENTITY);
run_to_path(run, &placement, &mut GlyphCache::default())
.expect("draws")
.expect("has ink")
.bounds()
}
const ITALIC: Synthesis = Synthesis {
skew: Some(14.0),
embolden: false,
};
const BOLD: Synthesis = Synthesis {
skew: None,
embolden: true,
};
#[test]
fn a_synthesized_italic_leans_the_outline_forward() {
for hint in [false, true] {
let upright = path_bounds(&stem(Synthesis::default(), hint));
let leaned = path_bounds(&stem(ITALIC, hint));
// The foot sits on the baseline and does not move; the top, the
// stem's height above it, moves right by tan(14°) of that height.
let height = f64::from(upright.bottom() - upright.top());
let expected = height * 14f64.to_radians().tan();
let moved = f64::from(leaned.right() - upright.right());
assert!(
(moved - expected).abs() < 1.0,
"hint {hint}: the top moved {moved}, expected about {expected}"
);
assert!(
(leaned.left() - upright.left()).abs() < 0.5,
"hint {hint}: the foot stayed put"
);
assert_eq!(leaned.top(), upright.top(), "hint {hint}: no taller");
}
}
#[test]
fn a_synthesized_bold_grows_the_outline_on_every_side() {
let grow = f64::from(SIZE) * Synthesis::EMBOLDEN_PER_SIDE_EM;
for hint in [false, true] {
let regular = path_bounds(&stem(Synthesis::default(), hint));
let bold = path_bounds(&stem(BOLD, hint));
for (side, delta) in [
("left", regular.left() - bold.left()),
("right", bold.right() - regular.right()),
("top", regular.top() - bold.top()),
("bottom", bold.bottom() - regular.bottom()),
] {
assert!(
(f64::from(delta) - grow).abs() < 0.05,
"hint {hint}: {side} grew {delta}, expected {grow}"
);
}
}
}
/// The raster cache keys on the synthesis: the same glyph at the same size,
/// asked for upright and then leaned, is two rasters rather than one reused.
#[test]
fn the_raster_cache_tells_synthesized_glyphs_apart() {
let mut cache = GlyphCache::default();
let black = [0, 0, 0, 255];
let bounds = |cache: &mut GlyphCache, synthesis| {
cache
.hinted_run_bounds(&stem(synthesis, true), 1.0, 0.0, 0.0, black)
.expect("draws")
.expect("has ink")
};
let upright = bounds(&mut cache, Synthesis::default());
let leaned = bounds(&mut cache, ITALIC);
let bold = bounds(&mut cache, BOLD);
assert!(
leaned.x1 > upright.x1,
"the leaned raster reaches further right"
);
assert!(bold.width() > upright.width(), "the bold raster is wider");
}