Skip to content

Mix::Clip doesn't interact properly with blends #1198

@DJMcNab

Description

@DJMcNab

Mix::Clip (or if we get #1192, add_clip_layer) is defined as:

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)

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:

Three triangles, the top one labelled No Clip is Dark Blue on Lighter Blue, the bottom left one labelled Mix::Normal is Aquamarine on lighter Blue, and the bottom right one labelled Mix::Clip is Dark blue with a blocky aquamarine border on lighter blue.

This is discussed in #vello > Mix::Clip optimisation: Interaction with layers. 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.

Metadata

Metadata

Assignees

Labels

C-classicApplies to the classic `vello` crate

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions