mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-14 21:44:17 +00:00
Pattern tiles: analyse the backdrop afresh inside a plain clip group
paint_pattern draws the tile content as a tree of its own on a fresh tile, so the plain-clip state inherited from an enclosing group must not apply there: an inner clip that blends against the tile was drawn as a plain clip and lost its backdrop. Save and clear SurfaceBudget::plain around the tile draw, as apply_soft_mask does.
This commit is contained in:
@@ -1372,7 +1372,11 @@ fn paint_pattern(dst: &mut Image, cov: &Mask, region: Region, pattern: &Pattern,
|
||||
fallback_paint(dst, budget);
|
||||
return;
|
||||
}
|
||||
// The tile's content is a tree of its own, drawn on a fresh transparent tile: what is known
|
||||
// of the enclosing groups (all Normal) says nothing about the backdrop inside it.
|
||||
let plain = std::mem::replace(&mut budget.plain, false);
|
||||
draw_group(&pattern.content, content_to_tile, 1.0, &mut tile, budget);
|
||||
budget.plain = plain;
|
||||
let shader = Shader::Pattern(pattern, &tile, device_to_pattern);
|
||||
paint_shader(dst, cov, region, &shader, opacity);
|
||||
budget.release(&tile);
|
||||
|
||||
@@ -1220,6 +1220,40 @@ fn a_blend_mode_inside_a_clip_group_reaches_the_backdrop() {
|
||||
assert!(near(px(&img, 2, 10), [0.0, 0.0, 1.0, 1.0], 1e-3), "{:?}", px(&img, 2, 10));
|
||||
}
|
||||
|
||||
/// A pattern tile is a tree of its own: a clip group inside it that blends (Multiply) against the
|
||||
/// tile's backdrop must not be drawn as a plain clip just because the pattern fill sits inside an
|
||||
/// outer plain clipping group. Red multiplied over the tile's blue is black inside the inner clip.
|
||||
#[test]
|
||||
fn a_pattern_inside_a_plain_clip_group_keeps_its_own_backdrop_analysis() {
|
||||
let mut multiply = Group::new("Multiply");
|
||||
multiply.blend = BlendMode::Multiply;
|
||||
multiply.children = vec![solid_rect(kurbo::Rect::new(0.0, 0.0, 20.0, 20.0), [1.0, 0.0, 0.0])];
|
||||
let mut content = Group::new("tile");
|
||||
content.children = vec![
|
||||
solid_rect(kurbo::Rect::new(0.0, 0.0, 20.0, 20.0), [0.0, 0.0, 1.0]),
|
||||
clip_group(kurbo::Rect::new(8.0, 8.0, 12.0, 12.0), vec![Node::Group(multiply)]),
|
||||
];
|
||||
let pattern = Pattern {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
pattern_units: PatternUnits::UserSpaceOnUse,
|
||||
content_units: PatternUnits::UserSpaceOnUse,
|
||||
transform: Affine::IDENTITY,
|
||||
content: std::sync::Arc::new(content),
|
||||
render_complexity: 4,
|
||||
fallback: None,
|
||||
};
|
||||
let Node::Shape(mut filled) = solid_rect(kurbo::Rect::new(0.0, 0.0, 20.0, 20.0), [0.0; 3]) else { return };
|
||||
filled.fill = Some(Paint::Pattern(pattern));
|
||||
let doc = doc_of(20.0, 20.0, vec![clip_group(kurbo::Rect::new(2.0, 2.0, 18.0, 18.0), vec![Node::Shape(filled)])]);
|
||||
let img = rasterize(&doc, 20, 20, 1.0);
|
||||
assert!(near(px(&img, 10, 10), [0.0, 0.0, 0.0, 1.0], 1e-3), "inner clip {:?}", px(&img, 10, 10));
|
||||
assert!(near(px(&img, 4, 10), [0.0, 0.0, 1.0, 1.0], 1e-3), "tile backdrop {:?}", px(&img, 4, 10));
|
||||
assert!(px(&img, 0, 10)[3] < 1e-3, "outside the outer clip {:?}", px(&img, 0, 10));
|
||||
}
|
||||
|
||||
/// A stroke reaches past its path's bounds by half its width: a horizontal line (a path
|
||||
/// without height) stroked 4 px wide covers 4 rows, and its butt caps end at the path's ends.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user