diff --git a/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h b/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h
index 8887bdb4e3..d422c00084 100644
--- a/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h
+++ b/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h
@@ -5,6 +5,7 @@
*/
#include
+#include
#include
#include
#include
diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp b/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp
index 56b04037c1..b368831b8f 100644
--- a/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp
+++ b/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp
@@ -31,8 +31,6 @@ Gfx::IntSize canvas_image_source_dimensions(CanvasImageSource const& image)
return { source->width()->anim_val()->value(), source->height()->anim_val()->value() };
},
[](GC::Ref source) -> Gfx::IntSize {
- if (auto painting_surface = source->surface())
- return painting_surface->size();
return { source->width(), source->height() };
},
[](GC::Ref source) -> Gfx::IntSize {
@@ -67,11 +65,11 @@ Optional canvas_image_source_frame(CanvasImageSource con
return image_data->frame(0, size);
},
[](GC::Ref const& canvas) -> Optional {
- canvas->present();
- auto surface = canvas->surface();
- if (!surface)
- return Gfx::DecodedImageFrame { *canvas->get_bitmap_from_surface() };
- return Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
+ canvas->prepare_for_compositing();
+ auto bitmap = canvas->get_bitmap_from_surface();
+ if (!bitmap)
+ return {};
+ return Gfx::DecodedImageFrame { *bitmap };
},
[](OneOf, GC::Ref> auto const& source) -> Optional {
auto bitmap = source->bitmap();
diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h b/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h
index 8b08ad4e66..e10735852a 100644
--- a/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h
+++ b/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h
@@ -17,7 +17,7 @@ public:
virtual WebIDL::ExceptionOr> create_image_data(int width, int height, Optional const& settings = {}) const = 0;
virtual WebIDL::ExceptionOr> create_image_data(ImageData const&) const = 0;
- virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) const = 0;
+ virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) = 0;
virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y) = 0;
virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) = 0;
diff --git a/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h b/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h
index e5bc3a2156..ca68ed96ff 100644
--- a/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h
+++ b/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h
@@ -20,11 +20,12 @@ class WEB_API RemoteCanvas2DTransport : public RefCounted create_context(Gfx::IntSize, bool alpha) = 0;
- virtual void destroy_context(Painting::CanvasId) = 0;
- virtual void update_commands(Painting::CanvasId, Gfx::CanvasCommandList const&) = 0;
+ virtual bool create_context(Gfx::IntSize, bool alpha) = 0;
+ virtual Optional canvas_id() const = 0;
+ virtual void destroy_context() = 0;
+ virtual void update_commands(Gfx::CanvasCommandList const&) = 0;
- virtual RefPtr read_back_pixels(Painting::CanvasId, Gfx::IntRect const&) = 0;
+ virtual RefPtr read_back_pixels(Gfx::IntRect const&) = 0;
};
}
diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp
index 85862c1573..f2c4f483b6 100644
--- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp
+++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp
@@ -14,7 +14,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -30,7 +29,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -40,16 +41,21 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
#include
#include
namespace Web::HTML {
+// Keep bitmap-heavy recorded command lists small enough to send to the Compositor without exceeding IPC attachment limits.
+static constexpr size_t max_pending_canvas_commands = 64;
+
GC_DEFINE_ALLOCATOR(CanvasRenderingContext2D);
JS::ThrowCompletionOr> CanvasRenderingContext2D::create(JS::Realm& realm, HTMLCanvasElement& element, JS::Value options)
@@ -75,6 +81,12 @@ void CanvasRenderingContext2D::initialize(JS::Realm& realm)
set_prototype(&Bindings::ensure_web_prototype(realm, "CanvasRenderingContext2D"_string));
}
+void CanvasRenderingContext2D::finalize()
+{
+ discard_backing_storage();
+ Base::finalize();
+}
+
void CanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
@@ -85,10 +97,10 @@ void CanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
size_t CanvasRenderingContext2D::external_memory_size() const
{
auto size = Base::external_memory_size();
- if (!m_player)
+ if (!has_backing_storage())
return size;
- auto surface_size = m_player->surface()->size();
+ auto surface_size = m_size;
if (surface_size.is_empty())
return size;
@@ -162,7 +174,7 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm
auto frame = canvas_image_source_frame(image);
if (!frame.has_value())
return {};
- auto const& bitmap = frame->bitmap();
+ auto source_bitmap_rect = frame->rect();
// 4. Establish the source and destination rectangles as follows:
// If not specified, the dw and dh arguments must default to the values of sw and sh, interpreted such that one CSS pixel in the image is treated as one unit in the output bitmap's coordinate space.
@@ -195,7 +207,7 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm
auto destination_rect = Gfx::FloatRect { destination_x, destination_y, destination_width, destination_height };
// When the source rectangle is outside the source image, the source rectangle must be clipped
// to the source image and the destination rectangle must be clipped in the same proportion.
- auto clipped_source = source_rect.intersected(bitmap.rect().to_type());
+ auto clipped_source = source_rect.intersected(source_bitmap_rect.to_type());
auto clipped_destination = destination_rect;
if (clipped_source != source_rect) {
clipped_destination.set_width(clipped_destination.width() * (clipped_source.width() / source_rect.width()));
@@ -242,26 +254,53 @@ void CanvasRenderingContext2D::did_draw(Gfx::FloatRect const&)
Gfx::CanvasCommandList* CanvasRenderingContext2D::canvas_command_list()
{
- allocate_painting_surface_if_needed();
- if (!m_player)
+ if (is_context_lost())
return nullptr;
+ ensure_backing_storage();
+ if (!has_backing_storage())
+ return nullptr;
+ if (m_commands.size() >= max_pending_canvas_commands)
+ flush_recorded_commands();
return &m_commands;
}
-RefPtr CanvasRenderingContext2D::surface()
+bool CanvasRenderingContext2D::ensure_remote_canvas_context()
{
- if (!m_player)
- return nullptr;
- flush_recorded_commands();
- return m_player->surface();
+ if (m_transport)
+ return true;
+
+ auto& page = m_element->document().page();
+ if (!page.has_compositor_host())
+ return false;
+ auto transport = page.compositor_host().create_canvas_2d_transport();
+ if (!transport)
+ return false;
+
+ // FIXME: implement context attribute .color_space
+ // FIXME: implement context attribute .color_type
+ // FIXME: implement context attribute .desynchronized
+ // FIXME: implement context attribute .will_read_frequently
+ if (!transport->create_context(m_element->bitmap_size_for_canvas(), m_context_attributes.alpha))
+ return false;
+ m_transport = move(transport);
+ return true;
}
void CanvasRenderingContext2D::flush_recorded_commands()
{
- if (m_commands.is_empty())
+ if (m_commands.is_empty() || !m_transport)
return;
+
auto commands = move(m_commands);
- m_player->play(commands);
+ m_transport->update_commands(commands);
+}
+
+RefPtr CanvasRenderingContext2D::read_pixels(Gfx::IntRect const& rect)
+{
+ if (!has_backing_storage())
+ return nullptr;
+ flush_recorded_commands();
+ return m_transport->read_back_pixels(rect);
}
void CanvasRenderingContext2D::set_size(Gfx::IntSize const& size)
@@ -269,38 +308,38 @@ void CanvasRenderingContext2D::set_size(Gfx::IntSize const& size)
if (m_size == size)
return;
m_size = size;
- m_commands = {};
- m_player = nullptr;
+ discard_backing_storage();
}
-void CanvasRenderingContext2D::present()
+void CanvasRenderingContext2D::prepare_for_compositing()
{
- if (!m_player)
- return;
flush_recorded_commands();
}
-void CanvasRenderingContext2D::allocate_painting_surface_if_needed()
+Optional CanvasRenderingContext2D::canvas_id() const
{
- if (m_player || m_size.is_empty())
+ if (!m_transport)
+ return {};
+ return m_transport->canvas_id();
+}
+
+void CanvasRenderingContext2D::ensure_backing_storage()
+{
+ if (has_backing_storage() || m_size.is_empty())
+ return;
+ if (!ensure_remote_canvas_context())
return;
- // FIXME: implement context attribute .color_space
- // FIXME: implement context attribute .color_type
- // FIXME: implement context attribute .desynchronized
- // FIXME: implement context attribute .will_read_frequently
-
- auto color_type = m_context_attributes.alpha ? Gfx::BitmapFormat::BGRA8888 : Gfx::BitmapFormat::BGRx8888;
-
- auto surface_size = m_element->bitmap_size_for_canvas();
- m_player = make(nullptr, surface_size, color_type, Gfx::AlphaType::Premultiplied);
m_element->set_needs_repaint();
+}
- // https://html.spec.whatwg.org/multipage/canvas.html#the-canvas-settings:concept-canvas-alpha
- // Thus, the bitmap of such a context starts off as opaque black instead of transparent black;
- // AD-HOC: Skia provides us with a full transparent surface by default; only clear the surface if alpha is disabled.
- if (!m_context_attributes.alpha)
- m_player->clear(clear_color());
+void CanvasRenderingContext2D::discard_backing_storage()
+{
+ m_commands = {};
+ if (m_transport) {
+ m_transport->destroy_context();
+ m_transport = nullptr;
+ }
}
Gfx::Path CanvasRenderingContext2D::text_path(Utf16String const& text, float x, float y, Optional max_width)
@@ -585,7 +624,7 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::create_image_d
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-getimagedata
-WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data(int x, int y, int width, int height, Optional const& settings) const
+WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data(int x, int y, int width, int height, Optional const& settings)
{
// 1. If either the sw or sh arguments are zero, then throw an "IndexSizeError" DOMException.
if (width == 0 || height == 0)
@@ -604,12 +643,6 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data
// 4. Initialize imageData given sw, sh, settings set to settings, and defaultColorSpace set to this's color space.
auto image_data = TRY(ImageData::create(realm(), abs_width, abs_height, settings));
- // NOTE: We don't attempt to create the underlying bitmap here; if it doesn't exist, it's like copying only transparent black pixels (which is a no-op).
- auto surface = m_element->surface();
- if (!surface)
- return image_data;
- auto const snapshot = Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
-
// 5. Let the source rectangle be the rectangle whose corners are the four points (sx, sy), (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh).
auto source_rect = Gfx::Rect { x, y, abs_width, abs_height };
@@ -619,7 +652,16 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data
if (width < 0 || height < 0) {
source_rect = source_rect.translated(min(width, 0), min(height, 0));
}
- auto source_rect_intersected = source_rect.intersected(snapshot.rect());
+ auto source_rect_intersected = source_rect.intersected(Gfx::IntRect { {}, m_size });
+ if (source_rect_intersected.is_empty())
+ return image_data;
+
+ // NOTE: If reading back from the Compositor fails (no backing storage or no connection),
+ // it's like copying only transparent black pixels (which is a no-op).
+ auto pixels = read_pixels(source_rect_intersected);
+ if (!pixels)
+ return image_data;
+ auto const snapshot = Gfx::DecodedImageFrame { *pixels };
// 6. Set the pixel values of imageData to be the pixels of this's output bitmap in the area specified by the source rectangle in the bitmap's coordinate space units, converted from this's color space to imageData's colorSpace using 'relative-colorimetric' rendering intent.
// NOTE: Internally we must use premultiplied alpha, but ImageData should hold unpremultiplied alpha. This conversion
@@ -629,7 +671,7 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data
VERIFY(image_data->bitmap().alpha_type() == Gfx::AlphaType::Unpremultiplied);
auto painter = Gfx::Painter::create(image_data->bitmap());
- painter->draw_bitmap(image_data->bitmap().rect().to_type(), snapshot, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver);
+ painter->draw_bitmap(image_data->bitmap().rect().to_type(), snapshot, snapshot.rect(), Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver);
// 7. Set the pixels values of imageData for areas of the source rectangle that are outside of the output bitmap to transparent black.
// NOTE: No-op, already done during creation.
@@ -744,12 +786,11 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::put_pixels_from_an_image_dat
// https://html.spec.whatwg.org/multipage/canvas.html#reset-the-rendering-context-to-its-default-state
void CanvasRenderingContext2D::reset_to_default_state()
{
- auto surface = m_element->surface();
+ auto* canvas_command_list = has_backing_storage() ? this->canvas_command_list() : nullptr;
// 1. Clear canvas's bitmap to transparent black.
- if (surface) {
- canvas_command_list()->append(Gfx::CanvasCommands::ClearRect { .rect = surface->rect().to_type(), .color = clear_color() });
- }
+ if (canvas_command_list)
+ canvas_command_list->append(Gfx::CanvasCommands::ClearRect { .rect = Gfx::FloatRect { {}, m_size.to_type() }, .color = clear_color() });
// 2. Empty the list of subpaths in context's current default path.
path().clear();
@@ -760,9 +801,9 @@ void CanvasRenderingContext2D::reset_to_default_state()
// 4. Reset everything that drawing state consists of to their initial values.
reset_drawing_state();
- if (surface) {
- canvas_command_list()->append(Gfx::CanvasCommands::Reset {});
- did_draw(surface->rect().to_type());
+ if (canvas_command_list) {
+ canvas_command_list->append(Gfx::CanvasCommands::Reset {});
+ did_draw(Gfx::FloatRect { {}, m_size.to_type() });
}
}
diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
index 8c0bb70af9..47f1c6c2b9 100644
--- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
+++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
@@ -8,12 +8,13 @@
#pragma once
+#include
#include
-#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -57,6 +58,8 @@ class CanvasRenderingContext2D
GC_DECLARE_ALLOCATOR(CanvasRenderingContext2D);
public:
+ static constexpr bool OVERRIDES_FINALIZE = true;
+
static JS::ThrowCompletionOr> create(JS::Realm&, HTMLCanvasElement&, JS::Value options);
virtual ~CanvasRenderingContext2D() override;
@@ -78,7 +81,7 @@ public:
virtual WebIDL::ExceptionOr> create_image_data(int width, int height, Optional const& settings = {}) const override;
virtual WebIDL::ExceptionOr> create_image_data(ImageData const& image_data) const override;
- virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) const override;
+ virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) override;
virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override;
WebIDL::ExceptionOr put_pixels_from_an_image_data_onto_a_bitmap(ImageData&, Gfx::CanvasCommandList&, float dx, float dy, float dirty_x, float dirty_y, float dirty_width, float dirty_height);
@@ -121,10 +124,15 @@ public:
virtual void set_shadow_color(String) override;
void set_size(Gfx::IntSize const&);
- void present();
+ void prepare_for_compositing();
- RefPtr surface();
- void allocate_painting_surface_if_needed();
+ void ensure_backing_storage();
+
+ void discard_backing_storage();
+
+ Optional canvas_id() const;
+
+ RefPtr read_pixels(Gfx::IntRect const&);
protected:
[[nodiscard]] Gfx::CanvasCommandList* canvas_command_list() override;
@@ -139,6 +147,7 @@ private:
virtual bool is_canvas_rendering_context_2d() const final { return true; }
virtual void initialize(JS::Realm&) override;
+ virtual void finalize() override;
virtual void visit_edges(Cell::Visitor&) override;
virtual size_t external_memory_size() const override;
@@ -167,10 +176,14 @@ private:
void flush_recorded_commands();
+ bool ensure_remote_canvas_context();
+
+ bool has_backing_storage() const { return m_transport != nullptr; }
+
GC::Ref m_element;
Gfx::CanvasCommandList m_commands;
- OwnPtr m_player;
+ RefPtr m_transport;
// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
bool m_origin_clean { true };
diff --git a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
index ad2885cca8..348007f030 100644
--- a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
+++ b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
@@ -524,10 +524,10 @@ void EventLoop::update_the_rendering()
// FIXME: 21. For each doc of docs, mark paint timing for doc.
- // AD-HOC: Present all canvas element surfaces in documents' pages after callbacks
+ // AD-HOC: Flush dirty canvas contexts in documents' pages after callbacks
// have had a chance to update them, and before painting snapshots the frame.
for (auto& document : docs)
- document->page().present_all_canvas_element_surfaces();
+ document->page().prepare_canvas_contexts_for_compositing();
// 22. For each doc of docs, update the rendering or user interface of doc and its node navigable to reflect the current state.
for (auto& doc : docs.in_reverse()) {
diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
index 46f010046a..5f9fd886e0 100644
--- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -29,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
@@ -36,7 +38,13 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(HTMLCanvasElement);
-static constexpr auto max_canvas_area = 16384 * 16384;
+static RefPtr create_transparent_canvas_bitmap(Gfx::IntSize const& size)
+{
+ auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size);
+ if (bitmap_or_error.is_error())
+ return nullptr;
+ return bitmap_or_error.release_value();
+}
HTMLCanvasElement::HTMLCanvasElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: HTMLElement(document, move(qualified_name))
@@ -54,7 +62,10 @@ void HTMLCanvasElement::initialize(JS::Realm& realm)
void HTMLCanvasElement::finalize()
{
- clear_compositor_surface();
+ // The remote canvas context belongs to the 2D context; tear it down with the
+ // element, since nothing will reach the context afterwards.
+ if (auto context = canvas_rendering_context_2d())
+ context->discard_backing_storage();
Base::finalize();
document().page().unregister_canvas_element({}, unique_id());
}
@@ -132,16 +143,8 @@ WebIDL::UnsignedLong HTMLCanvasElement::height() const
return 150;
}
-Painting::CompositorSurfaceId HTMLCanvasElement::ensure_compositor_surface_id()
-{
- if (!m_compositor_surface_id.has_value())
- m_compositor_surface_id = Painting::allocate_compositor_surface_id();
- return *m_compositor_surface_id;
-}
-
void HTMLCanvasElement::reset_context_to_default_state()
{
- clear_compositor_surface();
m_context.visit(
[](GC::Ref& context) {
context->reset_to_default_state();
@@ -323,7 +326,7 @@ Gfx::IntSize HTMLCanvasElement::bitmap_size_for_canvas(size_t minimum_width, siz
dbgln("Refusing to create {}x{} canvas (overflow)", width, height);
return {};
}
- if (area.value() > max_canvas_area) {
+ if (area.value() > Gfx::max_canvas_area) {
dbgln("Refusing to create {}x{} canvas (exceeds maximum size)", width, height);
return {};
}
@@ -333,26 +336,17 @@ Gfx::IntSize HTMLCanvasElement::bitmap_size_for_canvas(size_t minimum_width, siz
// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl
String HTMLCanvasElement::to_data_url(StringView type, Optional js_quality)
{
- // It is possible the canvas doesn't have an associated bitmap so create one
- allocate_painting_surface_if_needed();
- auto surface = this->surface();
- auto size = bitmap_size_for_canvas();
- if (!surface && !size.is_empty()) {
- // If the context is not initialized yet, we need to allocate transparent surface for serialization
- surface = Gfx::PaintingSurface::create_with_size(size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
- }
-
// FIXME: 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
// 2. If this canvas element's bitmap has no pixels (i.e. either its horizontal dimension or its vertical dimension is zero),
// then return the string "data:,". (This is the shortest data: URL; it represents the empty string in a text/plain resource.)
- if (!surface)
+ auto bitmap = get_bitmap_from_surface();
+ if (!bitmap)
return "data:,"_string;
// 3. Let file be a serialization of this canvas element's bitmap as a file, passing type and quality if given.
- auto bitmap = surface->snapshot_bitmap();
Optional quality = js_quality.has_value() && js_quality->is_number() ? js_quality->as_double() : Optional();
- auto file = serialize_bitmap(bitmap, type, quality);
+ auto file = serialize_bitmap(*bitmap, type, quality);
// 4. If file is null, then return "data:,".
if (file.is_error()) {
@@ -408,19 +402,40 @@ WebIDL::ExceptionOr HTMLCanvasElement::to_blob(GC::Ref const& context) -> WebGL::WebGLRenderingContextBase* { return context.ptr(); },
+ [](GC::Ref const& context) -> WebGL::WebGLRenderingContextBase* { return context.ptr(); },
+ [](auto const&) -> WebGL::WebGLRenderingContextBase* { return nullptr; });
+}
+
+Optional HTMLCanvasElement::canvas_id() const
+{
+ if (auto context = canvas_rendering_context_2d())
+ return context->canvas_id();
+ if (auto* webgl_context = this->webgl_context(); webgl_context && !webgl_context->is_context_lost())
+ return webgl_context->context().canvas_id();
+ return {};
+}
+
RefPtr HTMLCanvasElement::get_bitmap_from_surface()
{
- // It is possible the canvas doesn't have an associated bitmap so create one
- allocate_painting_surface_if_needed();
- auto surface = this->surface();
- if (auto const size = bitmap_size_for_canvas(); !surface && !size.is_empty()) {
- // If the context is not initialized yet, we need to allocate transparent surface for serialization
- surface = Gfx::PaintingSurface::create_with_size(size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
- }
+ auto const size = bitmap_size_for_canvas();
+ if (size.is_empty())
+ return nullptr;
RefPtr bitmap;
- if (surface) {
- bitmap = surface->snapshot_bitmap();
+ if (auto* webgl_context = this->webgl_context()) {
+ bitmap = webgl_context->context().read_back_drawing_buffer({ {}, size });
+ } else {
+ if (auto context = canvas_rendering_context_2d()) {
+ ensure_backing_storage();
+ if (auto pixels = context->read_pixels({ {}, size }); pixels && pixels->size() == size)
+ bitmap = pixels;
+ } else {
+ bitmap = create_transparent_canvas_bitmap(size);
+ }
}
return bitmap;
@@ -431,7 +446,7 @@ void HTMLCanvasElement::set_canvas_content_dirty()
m_canvas_content_dirty = true;
}
-void HTMLCanvasElement::present()
+void HTMLCanvasElement::prepare_for_compositing()
{
if (!m_canvas_content_dirty)
return;
@@ -439,80 +454,34 @@ void HTMLCanvasElement::present()
m_context.visit(
[](GC::Ref& context) {
- context->present();
+ context->prepare_for_compositing();
},
[](GC::Ref& context) {
- context->present();
+ context->prepare_for_compositing();
},
[](GC::Ref& context) {
- context->present();
- },
- [](Empty) {
- // Do nothing.
- });
-
- update_compositor_surface();
-}
-
-void HTMLCanvasElement::republish_compositor_surface()
-{
- if (m_canvas_content_dirty) {
- present();
- return;
- }
-
- update_compositor_surface();
-}
-
-void HTMLCanvasElement::update_compositor_surface()
-{
- if (auto surface = this->surface()) {
- surface->flush();
- if (auto navigable = document().navigable(); navigable && navigable->has_compositor_context())
- navigable->compositor_context().update_compositor_surface(ensure_compositor_surface_id(), surface->snapshot_into_shared_image());
- }
-}
-
-void HTMLCanvasElement::clear_compositor_surface()
-{
- if (!m_compositor_surface_id.has_value())
- return;
- if (auto navigable = document().navigable(); navigable && navigable->has_compositor_context())
- navigable->compositor_context().clear_compositor_surface(*m_compositor_surface_id);
-}
-
-RefPtr HTMLCanvasElement::surface() const
-{
- return m_context.visit(
- [&](GC::Ref const& context) {
- return context->surface();
- },
- [&](GC::Ref const& context) -> RefPtr {
- return context->surface();
- },
- [&](GC::Ref const& context) -> RefPtr {
- return context->surface();
- },
- [](Empty) -> RefPtr {
- return {};
- });
-}
-
-void HTMLCanvasElement::allocate_painting_surface_if_needed()
-{
- m_context.visit(
- [&](GC::Ref& context) {
- context->allocate_painting_surface_if_needed();
- },
- [&](GC::Ref& context) {
- context->allocate_painting_surface_if_needed();
- },
- [&](GC::Ref& context) {
- context->allocate_painting_surface_if_needed();
+ context->prepare_for_compositing();
},
[](Empty) {
// Do nothing.
});
}
+Optional HTMLCanvasElement::canvas_surface_content_size() const
+{
+ if (!canvas_id().has_value())
+ return {};
+
+ auto size = bitmap_size_for_canvas();
+ if (size.is_empty())
+ return {};
+ return size;
+}
+
+void HTMLCanvasElement::ensure_backing_storage()
+{
+ if (auto context = canvas_rendering_context_2d())
+ context->ensure_backing_storage();
+}
+
}
diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Libraries/LibWeb/HTML/HTMLCanvasElement.h
index f01abd52af..345bed1ae0 100644
--- a/Libraries/LibWeb/HTML/HTMLCanvasElement.h
+++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.h
@@ -8,7 +8,6 @@
#include
#include
-#include
#include
#include
#include
@@ -47,14 +46,20 @@ public:
WebIDL::ExceptionOr to_blob(GC::Ref callback, StringView type, Optional quality);
RefPtr get_bitmap_from_surface();
- void present();
- void republish_compositor_surface();
+ void prepare_for_compositing();
void set_canvas_content_dirty();
+ GC::Ptr canvas_rendering_context_2d() const
+ {
+ if (auto const* context = m_context.get_pointer>())
+ return *context;
+ return nullptr;
+ }
- RefPtr surface() const;
- void allocate_painting_surface_if_needed();
+ Optional canvas_id() const;
- Painting::CompositorSurfaceId ensure_compositor_surface_id();
+ Optional canvas_surface_content_size() const;
+
+ void ensure_backing_storage();
CSS::ComputationContext canvas_font_computation_context();
@@ -73,13 +78,11 @@ private:
template
JS::ThrowCompletionOr create_webgl_context(JS::Value options);
+ WebGL::WebGLRenderingContextBase* webgl_context() const;
void reset_context_to_default_state();
void notify_context_about_canvas_size_change();
- void clear_compositor_surface();
- void update_compositor_surface();
Variant, GC::Ref, GC::Ref, Empty> m_context;
- Optional m_compositor_surface_id;
bool m_canvas_content_dirty { false };
};
diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp
index 6c03293dce..705d61b5f9 100644
--- a/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Libraries/LibWeb/HTML/Navigable.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp
index 0e028a8206..8b27bb5430 100644
--- a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp
+++ b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp
@@ -142,7 +142,7 @@ WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::creat
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(ImageData&)"_utf16);
}
-WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional const&) const
+WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional const&)
{
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::get_image_data()"_utf16);
}
diff --git a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h
index 486ace3e6a..7bf5acde3a 100644
--- a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h
+++ b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h
@@ -82,7 +82,7 @@ public:
virtual WebIDL::ExceptionOr> create_image_data(int width, int height, Optional const& settings = {}) const override;
virtual WebIDL::ExceptionOr> create_image_data(ImageData const& image_data) const override;
- virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) const override;
+ virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) override;
virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override;
diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Libraries/LibWeb/HTML/TraversableNavigable.cpp
index 7abb847ba4..fa290bbd74 100644
--- a/Libraries/LibWeb/HTML/TraversableNavigable.cpp
+++ b/Libraries/LibWeb/HTML/TraversableNavigable.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp
index ba3fb12945..3f347f99f3 100644
--- a/Libraries/LibWeb/Page/Page.cpp
+++ b/Libraries/LibWeb/Page/Page.cpp
@@ -716,17 +716,10 @@ void Page::for_each_canvas_element(Callback&& callback)
}
}
-void Page::present_all_canvas_element_surfaces()
+void Page::prepare_canvas_contexts_for_compositing()
{
for_each_canvas_element([](auto& canvas_element) {
- canvas_element.present();
- });
-}
-
-void Page::republish_all_canvas_element_surfaces()
-{
- for_each_canvas_element([](auto& canvas_element) {
- canvas_element.republish_compositor_surface();
+ canvas_element.prepare_for_compositing();
});
}
diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h
index 202a813497..4012061050 100644
--- a/Libraries/LibWeb/Page/Page.h
+++ b/Libraries/LibWeb/Page/Page.h
@@ -233,8 +233,7 @@ public:
void register_canvas_element(Badge, UniqueNodeID canvas_id);
void unregister_canvas_element(Badge, UniqueNodeID canvas_id);
- void present_all_canvas_element_surfaces();
- void republish_all_canvas_element_surfaces();
+ void prepare_canvas_contexts_for_compositing();
struct MediaContextMenu {
URL::URL media_url;
diff --git a/Libraries/LibWeb/Painting/CanvasPaintable.cpp b/Libraries/LibWeb/Painting/CanvasPaintable.cpp
index 82261a1b97..498afdcc48 100644
--- a/Libraries/LibWeb/Painting/CanvasPaintable.cpp
+++ b/Libraries/LibWeb/Painting/CanvasPaintable.cpp
@@ -32,13 +32,14 @@ void CanvasPaintable::paint(DisplayListRecordingContext& context, PaintPhase pha
ScopedCornerRadiusClip corner_clip { context, canvas_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) };
auto& canvas_element = as(*dom_node());
- if (auto surface = canvas_element.surface()) {
+ if (auto content_size = canvas_element.canvas_surface_content_size(); content_size.has_value()) {
+ auto canvas_id = canvas_element.canvas_id();
+ VERIFY(canvas_id.has_value());
auto canvas_int_rect = canvas_rect.to_type();
auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(),
- surface->size(), canvas_int_rect.size());
- auto& mutable_canvas_element = const_cast(canvas_element);
- context.display_list_recorder().draw_compositor_surface(canvas_int_rect,
- mutable_canvas_element.ensure_compositor_surface_id(), scaling_mode);
+ *content_size, canvas_int_rect.size());
+ context.display_list_recorder().draw_canvas(canvas_int_rect,
+ *canvas_id, scaling_mode);
}
}
}
diff --git a/Libraries/LibWeb/WebDriver/Screenshot.cpp b/Libraries/LibWeb/WebDriver/Screenshot.cpp
index def83c2f39..ad8cdeb619 100644
--- a/Libraries/LibWeb/WebDriver/Screenshot.cpp
+++ b/Libraries/LibWeb/WebDriver/Screenshot.cpp
@@ -5,10 +5,13 @@
*/
#include
+#include
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -46,9 +49,6 @@ ErrorOr, WebDriver::Error> draw_bounding_box_fr
// FIXME: 5. Let context, a canvas context mode, be the result of invoking the 2D context creation algorithm given canvas as the target.
MUST(canvas.create_2d_context({}));
- canvas.allocate_painting_surface_if_needed();
- if (!canvas.surface())
- return Error::from_code(ErrorCode::UnableToCaptureScreen, "Failed to allocate painting surface"sv);
// 6. Complete implementation specific steps equivalent to drawing the region of the framebuffer specified by the following coordinates onto context:
// - X coordinate: rectangle x coordinate
@@ -57,7 +57,7 @@ ErrorOr, WebDriver::Error> draw_bounding_box_fr
// - Height: paint height
Gfx::IntRect paint_rect { rect.x(), rect.y(), paint_width, paint_height };
- auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, canvas.surface()->size()));
+ auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, Gfx::IntSize { paint_width, paint_height }));
auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
IGNORE_USE_IN_ESCAPING_LAMBDA bool did_paint = false;
HTML::PaintConfig paint_config { .canvas_fill_rect = paint_rect };
@@ -68,7 +68,10 @@ ErrorOr, WebDriver::Error> draw_bounding_box_fr
return did_paint;
}));
- canvas.surface()->write_from_bitmap(*bitmap);
+ auto image_bitmap = HTML::ImageBitmap::create(element.realm());
+ image_bitmap->set_bitmap(bitmap);
+ if (canvas.canvas_rendering_context_2d()->draw_image(image_bitmap, 0, 0).is_exception())
+ return Error::from_code(ErrorCode::UnableToCaptureScreen, "Failed to draw the screenshot to the canvas"sv);
// 7. Return success with canvas.
return canvas;
@@ -80,7 +83,7 @@ Response encode_canvas_element(HTML::HTMLCanvasElement& canvas)
// FIXME: 1. If the canvas element’s bitmap’s origin-clean flag is set to false, return error with error code unable to capture screen.
// 2. If the canvas element’s bitmap has no pixels (i.e. either its horizontal dimension or vertical dimension is zero) then return error with error code unable to capture screen.
- if (canvas.surface()->size().is_empty())
+ if (!canvas.canvas_surface_content_size().has_value())
return Error::from_code(ErrorCode::UnableToCaptureScreen, "Captured screenshot is empty"sv);
// 3. Let file be a serialization of the canvas element’s bitmap as a file, using "image/png" as an argument.
diff --git a/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp b/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp
index ca76073e47..8045f6aa96 100644
--- a/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include
#include
#include
diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp
index 5c96c59c06..d8e1fc818b 100644
--- a/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include
#include
namespace Web::WebGL {
diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp
index 4dff63f4e3..6396bee8c4 100644
--- a/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include
#include
namespace Web::WebGL {
diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp
index 29c1a684ab..7c75f82ed8 100644
--- a/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include
#include
namespace Web::WebGL {
diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp
index 52356548ea..a5fd7e2eff 100644
--- a/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include
#include
namespace Web::WebGL {
diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp
index 2ed39c6192..f35388d450 100644
--- a/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include
#include
namespace Web::WebGL {
diff --git a/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp b/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp
index 4f729446c6..d4a5b06ec2 100644
--- a/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp
+++ b/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp
@@ -8,7 +8,7 @@
#include
#include
#include
-#include
+#include