Cover metafile transform and pen paths

This commit is contained in:
Dylan M. Taylor committed 2026-11-13 07:01:52 +00:00
1 parent ec2937f298
commit f78a805ac7
2 files changed
+54

No files matched your search

+3
View File
@@ -25,3 +25,6 @@ skrifa = { workspace = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
vello_cpu = { workspace = true, features = ["multithreading"] }
[dev-dependencies]
craft-metafile = { workspace = true, features = ["write"] }
+51
View File
@@ -1975,6 +1975,57 @@ fn metafile_dashed_pen_leaves_gaps() {
assert!(ink.iter().filter(|b| !**b).count() > 30, "with gaps between them");
}
#[test]
fn metafile_skewed_bitmap_is_drawn_with_its_transform() {
use craft_metafile::write::{Image as MetafileImage, Op, emf};
use kurbo::{Affine, Rect};
let image = std::sync::Arc::new(MetafileImage { width: 2, height: 2, rgba: [255, 0, 0, 255].repeat(4) });
let xf = Affine::translate((25.0, 25.0)) * Affine::new([1.0, 0.25, 0.2, 1.0, 0.0, 0.0]) * Affine::scale(24.0);
let bytes = emf(&[Op::Image { image, xf, opacity: 1.0 }], Rect::new(0.0, 0.0, 100.0, 100.0), "DeckCraft", "skewed bitmap").expect("EMF");
let img = crate::metafile::rasterize(&bytes, 160).expect("raster");
let red: Vec<_> = img.enumerate_pixels().filter(|(_, _, p)| p[0] > 180 && p[1] < 80 && p[3] > 128).collect();
assert!(red.len() > 100, "the transformed image is visible: {} red pixels", red.len());
let (min_x, max_x) = red.iter().fold((u32::MAX, 0), |(lo, hi), (x, _, _)| (lo.min(*x), hi.max(*x)));
let (min_y, max_y) = red.iter().fold((u32::MAX, 0), |(lo, hi), (_, y, _)| (lo.min(*y), hi.max(*y)));
assert!(max_x - min_x > 25 && max_y - min_y > 25, "the image keeps both transformed axes: x={min_x}..{max_x}, y={min_y}..{max_y}");
}
#[test]
fn metafile_clip_path_and_nondefault_pen_geometry_are_applied() {
use craft_metafile::write::{Op, Pen, emf};
use craft_metafile::{Cap, Join};
use kurbo::{BezPath, Rect, Shape as _};
let mut triangle = BezPath::new();
triangle.move_to((10.0, 10.0));
triangle.line_to((90.0, 10.0));
triangle.line_to((50.0, 90.0));
triangle.close_path();
let mut bevel = BezPath::new();
bevel.move_to((15.0, 35.0));
bevel.line_to((50.0, 60.0));
bevel.line_to((85.0, 35.0));
let mut miter = BezPath::new();
miter.move_to((15.0, 45.0));
miter.line_to((50.0, 70.0));
miter.line_to((85.0, 45.0));
let pen = |join| Pen { rgb: [0, 0, 0], width: 5.0, cap: Cap::Square, join, miter: 4.0, dash: None };
let ops = [
Op::Clip { path: triangle, even_odd: false },
Op::Fill { path: Rect::new(0.0, 0.0, 100.0, 100.0).to_path(0.1), even_odd: false, rgb: [255, 0, 0] },
Op::Stroke { path: bevel, pen: pen(Join::Bevel) },
Op::Stroke { path: miter, pen: pen(Join::Miter) },
Op::Unclip,
];
let bytes = emf(&ops, Rect::new(0.0, 0.0, 100.0, 100.0), "DeckCraft", "clipped strokes").expect("EMF");
let img = crate::metafile::rasterize(&bytes, 160).expect("raster");
assert!(img.get_pixel(80, 12)[0] > 180, "inside the clip path is filled");
assert_eq!(img.get_pixel(8, 80)[3], 0, "outside the clip path remains transparent");
assert!(img.pixels().any(|p| p[3] > 128 && p[0] < 80), "square capped bevel and miter strokes are rendered");
}
#[test]
fn metafile_text_ink_lands_at_its_reference_point() {
// 2 × 1 inch (144 × 72 pt) drawn 288 px wide: 2 px per point. A 36-point "HH", top-left