LibWeb: Honor transparent copy compositing in canvas
Do not skip fully transparent canvas fill and stroke styles when the current compositing operator can still affect the destination bitmap. TradingView uses transparent copy fills while repainting its chart canvases, and skipping those draws left old translucent chart pixels in place so later source-over draws accumulated on top. Add text coverage for transparent copy replacing an existing translucent pixel before another translucent draw.
This commit is contained in:
parent
1835d739f5
commit
4c03a6f5fc
3 changed files with 37 additions and 2 deletions
|
|
@ -440,6 +440,13 @@ static Gfx::Path::JoinStyle to_gfx_join(Bindings::CanvasLineJoin const& join_sty
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static bool transparent_source_paint_can_be_ignored(Gfx::CompositingAndBlendingOperator compositing_and_blending_operator)
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#drawing-model
|
||||
// Composite B within the clipping region over the current output bitmap using the current compositing and blending operator.
|
||||
return compositing_and_blending_operator == Gfx::CompositingAndBlendingOperator::SourceOver;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#the-canvas-settings:concept-canvas-alpha
|
||||
Gfx::Color CanvasRenderingContext2D::clear_color() const
|
||||
{
|
||||
|
|
@ -454,7 +461,7 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
|
|||
|
||||
auto& state = drawing_state();
|
||||
auto paint_style = state.stroke_style.to_gfx_paint_style();
|
||||
if (!paint_style->is_visible())
|
||||
if (!paint_style->is_visible() && transparent_source_paint_can_be_ignored(state.current_compositing_and_blending_operator))
|
||||
return;
|
||||
|
||||
auto line_cap = to_gfx_cap(state.line_cap);
|
||||
|
|
@ -500,7 +507,7 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::Winding
|
|||
|
||||
auto& state = this->drawing_state();
|
||||
auto paint_style = state.fill_style.to_gfx_paint_style();
|
||||
if (!paint_style->is_visible())
|
||||
if (!paint_style->is_visible() && transparent_source_paint_can_be_ignored(state.current_compositing_and_blending_operator))
|
||||
return;
|
||||
|
||||
paint_shadow_for_fill_internal(path, winding_rule);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
first draw: 255,0,0,51
|
||||
transparent copy: 0,0,0,0
|
||||
second draw: 255,0,0,51
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
|
||||
const context = canvas.getContext("2d");
|
||||
|
||||
context.fillStyle = "rgba(255, 0, 0, 0.2)";
|
||||
context.fillRect(0, 0, 1, 1);
|
||||
println(`first draw: ${Array.from(context.getImageData(0, 0, 1, 1).data).join(",")}`);
|
||||
|
||||
context.globalCompositeOperation = "copy";
|
||||
context.fillStyle = "rgba(0, 0, 0, 0)";
|
||||
context.fillRect(0, 0, 1, 1);
|
||||
println(`transparent copy: ${Array.from(context.getImageData(0, 0, 1, 1).data).join(",")}`);
|
||||
|
||||
context.globalCompositeOperation = "source-over";
|
||||
context.fillStyle = "rgba(255, 0, 0, 0.2)";
|
||||
context.fillRect(0, 0, 1, 1);
|
||||
println(`second draw: ${Array.from(context.getImageData(0, 0, 1, 1).data).join(",")}`);
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue