Merge pull request #1271

This commit is contained in:
Brandon Thomas committed 2026-10-18 09:35:33 -04:00
commit de7c8d6dd6
2 files changed
+29 -2

No files matched your search

+11 -2
View File
@@ -57,6 +57,10 @@ struct Draw {
blend: BlendMode,
/// Front / Bevel / Side / Back colours of the paint's extrusion (Advanced 3D).
surfaces: SurfaceColors,
/// Paint item of the contents group this draw was collected in (as opposed to one bubbled up
/// from a nested group). Merge Paths drops only these paints above it (#1174); a group's own
/// fill or stroke keeps rendering inside the group.
local: bool,
}
/// Straight RGB colours of an extrusion's surfaces, by [`effectcraft_model::extrude::Surface`]
@@ -234,6 +238,7 @@ fn collect(ctx: &EvalCtx, layer: &Layer, contents: &PropGroup, arena: &mut Vec<B
opacity: opacity(g),
blend: blend_mode(ctx, layer, g),
surfaces: [None; 4],
local: true,
});
}
"stroke" => {
@@ -247,6 +252,7 @@ fn collect(ctx: &EvalCtx, layer: &Layer, contents: &PropGroup, arena: &mut Vec<B
opacity: opacity(g),
blend: blend_mode(ctx, layer, g),
surfaces: [None; 4],
local: true,
});
}
"gfill" | "gstroke" => {
@@ -270,6 +276,7 @@ fn collect(ctx: &EvalCtx, layer: &Layer, contents: &PropGroup, arena: &mut Vec<B
opacity: opacity(g),
blend: blend_mode(ctx, layer, g),
surfaces: [None; 4],
local: true,
});
}
"trim" => {
@@ -328,8 +335,9 @@ fn collect(ctx: &EvalCtx, layer: &Layer, contents: &PropGroup, arena: &mut Vec<B
arena.push(ops::merge(&input, mode));
live = vec![arena.len() - 1];
// As in After Effects, fills and strokes above a Merge Paths in its group are not
// rendered: the merged path is painted by the paint items below it.
draws.clear();
// rendered: the merged path is painted by the paint items below it. Paint inside
// groups above the merge still renders — it belongs to those groups (#1174).
draws.retain(|d| !d.local);
}
"group" => {
let Some(inner) = g.sub("contents") else { continue };
@@ -341,6 +349,7 @@ fn collect(ctx: &EvalCtx, layer: &Layer, contents: &PropGroup, arena: &mut Vec<B
for mut d in cd {
d.xf = m * d.xf;
d.opacity *= op;
d.local = false;
draws.push(d);
}
live.extend(cl);
+18
View File
@@ -76,6 +76,24 @@ fn merge_add_feeds_fill_below_and_drops_paint_above() {
assert!((coverage(&img) - 80.0 * 40.0).abs() < 20.0, "{}", coverage(&img));
}
/// #1174: Adding Merge Paths to a shape layer's Contents, after the group that holds the
/// shape and its paint, erased the render. The merge drops paints above it *in its group* —
/// not paint that belongs to a group above it.
#[test]
fn merge_paths_below_a_group_keeps_the_group_paint() {
for mode in [0u32, 1] {
let (p, cid) = shape_comp(|ids| {
let rect = build::shape_rect(ids, [40.0, 40.0], [0.0, 0.0], 0.0);
let fill = build::shape_fill(ids, RED);
vec![build::shape_group(ids, "Group 1", vec![rect, fill]), op(ids, "merge", &[("mode", Value::Enum(mode))])]
});
let img = frame(&p, cid);
let c = img.get(100, 50);
assert!(c[0] > 0.99 && c[1] < 0.01 && c[3] > 0.99, "mode {mode}: group rectangle must still render, got {c:?}");
assert!((coverage(&img) - 40.0 * 40.0).abs() < 20.0, "mode {mode}: {}", coverage(&img));
}
}
#[test]
fn merge_subtract_and_intersect() {
let sub = |mode: u32| {