Files
ladybird/Libraries/LibWeb/Painting/CanvasPaintable.cpp
Aliaksandr Kalenik 004e5f851e LibWeb: Use ExternalContentSource for canvas painting
present() now snapshots the PaintingSurface into an ImmutableBitmap
and publishes it to the ExternalContentSource, so the rendering thread
never touches the live GPU surface — eliminating the data race
described in the ExternalContentSource commit (problem 1).

Canvas elements are registered with Page and presented once per frame
from the event loop, rather than on every individual draw call in
CRC2D::did_draw(). A dirty flag on HTMLCanvasElement ensures the
snapshot is only taken when content has actually changed, and makes
the present() call in CanvasPaintable::paint() a no-op when the
surface has already been snapshotted for the current frame.
2026-02-20 18:41:33 +01:00

51 lines
1.7 KiB
C++

/*
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Painting/CanvasPaintable.h>
#include <LibWeb/Painting/DisplayListRecorder.h>
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(CanvasPaintable);
GC::Ref<CanvasPaintable> CanvasPaintable::create(Layout::CanvasBox const& layout_box)
{
return layout_box.heap().allocate<CanvasPaintable>(layout_box);
}
CanvasPaintable::CanvasPaintable(Layout::CanvasBox const& layout_box)
: PaintableBox(layout_box)
{
}
void CanvasPaintable::paint(DisplayListRecordingContext& context, PaintPhase phase) const
{
if (!is_visible())
return;
PaintableBox::paint(context, phase);
if (phase == PaintPhase::Foreground) {
auto canvas_rect = context.rounded_device_rect(absolute_rect());
ScopedCornerRadiusClip corner_clip { context, canvas_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) };
auto& canvas_element = as<HTML::HTMLCanvasElement>(*dom_node());
if (canvas_element.surface()) {
// present() snapshots the surface and publishes to ExternalContentSource.
// FIXME: Remove this const_cast.
auto& mutable_canvas_element = const_cast<HTML::HTMLCanvasElement&>(canvas_element);
mutable_canvas_element.present();
auto canvas_int_rect = canvas_rect.to_type<int>();
auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(),
canvas_element.surface()->size(), canvas_int_rect.size());
context.display_list_recorder().draw_external_content(canvas_int_rect,
mutable_canvas_element.ensure_external_content_source(), scaling_mode);
}
}
}
}