mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
Merge branch 'pr-513' into batch/t5
This commit is contained in:
@@ -735,13 +735,17 @@ fn draw_children_clipped(ctx: &Ctx, pix: &mut Pixmap, n: &Node, xf: Affine, dept
|
||||
// layers behind the frame.
|
||||
let Some(mut layer) = pix.clone_rect(ir) else { return };
|
||||
let lxf = Affine::translate((-f64::from(lx), -f64::from(ly))) * xf;
|
||||
let rect_clip = n.corners.uniform() == Some(0.0) && is_axis_aligned(xf);
|
||||
// The clip (a rectangular one is the layer's own box) is known before the children draw, so
|
||||
// their backdrops are clipped the way the layer will be.
|
||||
let square_aligned = n.corners.uniform() == Some(0.0) && is_axis_aligned(xf);
|
||||
let rect_clip = square_aligned && on_whole_pixels(dev);
|
||||
// The clip (a rectangular one on whole pixels is the layer's own box) is known before the
|
||||
// children draw, so their backdrops are clipped the way the layer will be. An axis-aligned
|
||||
// square frame with fractional edges gets an anti-aliased mask of its box so its edge pixels
|
||||
// are only partly covered.
|
||||
let mask = if rect_clip {
|
||||
None
|
||||
} else {
|
||||
clip.and_then(ts_path).and_then(|cp| {
|
||||
let own_box = (square_aligned && clip.is_none()).then(|| n.local_rect().to_path(0.1));
|
||||
clip.or(own_box.as_ref()).and_then(ts_path).and_then(|cp| {
|
||||
let mut m = Mask::new(ir.width(), ir.height())?;
|
||||
m.fill_path(&cp, FillRule::Winding, true, ts_transform(lxf));
|
||||
Some(m)
|
||||
@@ -782,3 +786,9 @@ fn is_axis_aligned(xf: Affine) -> bool {
|
||||
fn fract_ok(_v: f64) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Whether all four edges of the device rectangle `r` lie on whole pixels, up to the float noise
|
||||
/// `snap_px` forgives.
|
||||
fn on_whole_pixels(r: Rect) -> bool {
|
||||
[r.x0, r.y0, r.x1, r.y1].into_iter().all(|v| (snap_px(v) - v.round()).abs() < 1e-9)
|
||||
}
|
||||
|
||||
@@ -330,3 +330,28 @@ fn inner_shadow_reaches_every_edge() {
|
||||
assert!(px(&pix, 11, 25)[0] < 235, "{:?}", px(&pix, 11, 25));
|
||||
assert!(px(&pix, 35, 30)[0] > 245);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rect_clip_frame_matches_same_rect_fill_on_fractional_edges() {
|
||||
let white = Options { background: Some(Color::WHITE), ..Default::default() };
|
||||
for (x, y, w, h) in [(0.5, 0.5, 10.0, 10.0), (0.0, 0.0, 10.5, 10.5), (0.0, 0.0, 10.0, 10.0)] {
|
||||
let mut clipped = figcraft_doc::Document::new("clipped");
|
||||
let p = clipped.first_page().unwrap();
|
||||
let f = clipped.create(p, NodeKind::Frame, "F", x, y, w, h).unwrap();
|
||||
clipped.get_mut(f).unwrap().fills.clear();
|
||||
clipped.get_mut(f).unwrap().clip = true;
|
||||
let c = clipped.create(f, NodeKind::Rectangle, "C", -5.0, -5.0, w + 10.0, h + 10.0).unwrap();
|
||||
clipped.get_mut(c).unwrap().fills = vec![Paint::solid(Color::BLACK)];
|
||||
|
||||
let mut direct = figcraft_doc::Document::new("direct");
|
||||
let dp = direct.first_page().unwrap();
|
||||
let r = direct.create(dp, NodeKind::Rectangle, "R", x, y, w, h).unwrap();
|
||||
direct.get_mut(r).unwrap().fills = vec![Paint::solid(Color::BLACK)];
|
||||
|
||||
let a = render_view(&clipped, p, &view(12, 12), &white).unwrap();
|
||||
let e = render_view(&direct, dp, &view(12, 12), &white).unwrap();
|
||||
for (px_x, px_y) in [(0, 0), (0, 5), (5, 0), (5, 5), (10, 5), (5, 10), (10, 10), (11, 11)] {
|
||||
assert_eq!(px(&a, px_x, px_y), px(&e, px_x, px_y), "frame {x},{y} {w}x{h} at ({px_x},{px_y})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user