`Mix::Clip` doesn't interact properly with blends
- Dominant language
- Rust
- Stars
- 4.3k
- Forks
- 300
- Avg merge
- 2d 13h
- Merged PRs (30d)
- 61
Description
`Mix::Clip` (or if we get #1192, `add_clip_layer`) [is defined as](https://docs.rs/peniko/0.4.0/peniko/enum.Mix.html#variant.Clip):
> `Clip` is the same as `Normal`, but the latter always creates an isolated blend group and the former can optimize that out.
To validate this, I created the following test scene:
Code for example (long)
```rust
pub(super) fn clipped_blend(scene: &mut Scene, params: &mut SceneParams<'_>) {
params.resolution = Some(Vec2::new(1000., 1200.));
let transform = Affine::translate((10., 100.));
params.text.add_run(
&mut *scene,
None,
32.,
Color::WHITE,
transform.then_translate((10., -30.).into()),
None,
&Style::Fill(Fill::EvenOdd),
"No Clip",
);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::BLUE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.push_layer(
Mix::Multiply,
1.0,
transform,
&Triangle::from_coords((200., 0.), (0., 400.), (400., 400.)),
);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::AQUAMARINE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.pop_layer();
let transform = Affine::translate((0., 600.));
params.text.add_run(
&mut *scene,
None,
32.,
Color::WHITE,
transform.then_translate((10., -30.).into()),
None,
&Style::Fill(Fill::EvenOdd),
"Mix::Normal",
);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::BLUE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
let layer_shape = Triangle::from_coords((200., 0.), (0., 400.), (400., 400.));
scene.push_layer(Mix::Normal, 1.0, transform, &layer_shape);
scene.push_layer(Mix::Multiply, 1.0, transform, &layer_shape);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::AQUAMARINE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.pop_layer();
scene.pop_layer();
let transform = Affine::translate((500., 600.));
params.text.add_run(
&mut *scene,
None,
32.,
Color::WHITE,
transform.then_translate((10., -30.).into()),
None,
&Style::Fill(Fill::EvenOdd),
"Mix::Clip",
);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::BLUE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
let layer_shape = Triangle::from_coords((200., 0.), (0., 400.), (400., 400.));
scene.push_layer(Mix::Clip, 1.0, transform, &layer_shape);
scene.push_layer(Mix::Multiply, 1.0, transform, &layer_shape);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::AQUAMARINE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.pop_layer();
scene.pop_layer();
}
```
This shows `Mix::Clip` followed by a `Multiply` blend, and both of the two reasonable rendering results from the blends; either the same as there not being a clip, or it being equivalent to `Mix::Normal`.
This however renders as:
This is discussed in [#vello > Mix::Clip optimisation: Interaction with layers](https://xi.zulipchat.com/#narrow/channel/197075-vello/topic/Mix.3A.3AClip.20optimisation.3A.20Interaction.20with.20layers/with/538168481). Essentially, the fixes look like making the Clip act the same as No Clip, which likely requires us to implement non-isolated blending. For clip layers in particular, that's pretty easy, although the general case of non-isolated blending is pretty gnarly because the web compositing spec is pretty lax about properly specifying the semantics.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.