From a7cbc7a6b813e1d9b6f11095ac731f2b797022f8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Wed, 25 Sep 2024 15:44:58 +0200 Subject: [PATCH] LibWeb+LibGfx: Use GPU backend for This is implemented by using a GPU-accelerated surface for when a GPU context is available. A side effect of this change is that all canvas modifications have to be performed through Gfx::Painter, and whenever its content has to be accessed, we need to take a snapshot of the corresponding GPU surface. A new DrawPaintingSurface display list command is introduced to allow cheap blitting of canvas content without having to read GPU surface content into RAM. --- Userland/Libraries/LibGfx/Painter.cpp | 4 +- Userland/Libraries/LibGfx/PainterSkia.cpp | 22 ++++----- Userland/Libraries/LibGfx/PainterSkia.h | 3 +- .../LibWeb/HTML/Canvas/CanvasDrawImage.cpp | 10 +++- .../Libraries/LibWeb/HTML/CanvasPattern.cpp | 9 +++- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 35 ++++++++------ .../LibWeb/HTML/HTMLCanvasElement.cpp | 47 ++++++++++++------- .../Libraries/LibWeb/HTML/HTMLCanvasElement.h | 9 ++-- .../LibWeb/HTML/TraversableNavigable.h | 2 + .../LibWeb/Painting/CanvasPaintable.cpp | 9 ++-- Userland/Libraries/LibWeb/Painting/Command.h | 12 +++++ .../Libraries/LibWeb/Painting/DisplayList.cpp | 1 + .../Libraries/LibWeb/Painting/DisplayList.h | 1 + .../LibWeb/Painting/DisplayListPlayerSkia.cpp | 11 +++++ .../LibWeb/Painting/DisplayListPlayerSkia.h | 1 + .../LibWeb/Painting/DisplayListRecorder.cpp | 12 +++++ .../LibWeb/Painting/DisplayListRecorder.h | 1 + .../Libraries/LibWeb/WebDriver/Screenshot.cpp | 6 +-- .../LibWeb/WebGL/WebGLRenderingContext.cpp | 8 ++-- .../WebGL/WebGLRenderingContextBase.cpp | 2 +- 20 files changed, 140 insertions(+), 65 deletions(-) diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 3de5f326887..c8a06a3f794 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -6,6 +6,7 @@ #include #include +#include namespace Gfx { @@ -13,7 +14,8 @@ Painter::~Painter() = default; NonnullOwnPtr Painter::create(NonnullRefPtr target_bitmap) { - return make(move(target_bitmap)); + auto painting_surface = PaintingSurface::wrap_bitmap(target_bitmap); + return make(painting_surface); } } diff --git a/Userland/Libraries/LibGfx/PainterSkia.cpp b/Userland/Libraries/LibGfx/PainterSkia.cpp index b68689bd1cc..5093cfb2736 100644 --- a/Userland/Libraries/LibGfx/PainterSkia.cpp +++ b/Userland/Libraries/LibGfx/PainterSkia.cpp @@ -58,21 +58,17 @@ static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type) } struct PainterSkia::Impl { - NonnullRefPtr gfx_bitmap; - OwnPtr sk_bitmap; - OwnPtr sk_canvas; + RefPtr painting_surface; - Impl(NonnullRefPtr target_bitmap) - : gfx_bitmap(move(target_bitmap)) + Impl(Gfx::PaintingSurface& surface) + : painting_surface(surface) { - sk_bitmap = make(); - SkImageInfo info = SkImageInfo::Make(gfx_bitmap->width(), gfx_bitmap->height(), to_skia_color_type(gfx_bitmap->format()), to_skia_alpha_type(gfx_bitmap->alpha_type())); - sk_bitmap->installPixels(info, gfx_bitmap->scanline(0), gfx_bitmap->pitch()); - - sk_canvas = make(*sk_bitmap); } - SkCanvas* canvas() { return sk_canvas; } + SkCanvas* canvas() const + { + return &painting_surface->canvas(); + } }; static constexpr SkRect to_skia_rect(auto const& rect) @@ -101,8 +97,8 @@ static SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule) VERIFY_NOT_REACHED(); } -PainterSkia::PainterSkia(NonnullRefPtr target_bitmap) - : m_impl(adopt_own(*new Impl { move(target_bitmap) })) +PainterSkia::PainterSkia(NonnullRefPtr painting_surface) + : m_impl(adopt_own(*new Impl { move(painting_surface) })) { } diff --git a/Userland/Libraries/LibGfx/PainterSkia.h b/Userland/Libraries/LibGfx/PainterSkia.h index b0440f39d66..ba0f3b0390e 100644 --- a/Userland/Libraries/LibGfx/PainterSkia.h +++ b/Userland/Libraries/LibGfx/PainterSkia.h @@ -9,12 +9,13 @@ #include #include #include +#include namespace Gfx { class PainterSkia final : public Painter { public: - explicit PainterSkia(NonnullRefPtr); + explicit PainterSkia(NonnullRefPtr); virtual ~PainterSkia() override; virtual void clear_rect(Gfx::FloatRect const&, Color) override; diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp index 7cb01326bca..a32504087ad 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp @@ -32,6 +32,15 @@ static void default_source_size(CanvasImageSource const& image, float& source_wi source_height = source->video_height(); } }, + [&source_width, &source_height](JS::Handle const& source) { + if (source->surface()) { + source_width = source->surface()->size().width(); + source_height = source->surface()->size().height(); + } else { + source_width = source->width(); + source_height = source->height(); + } + }, [&source_width, &source_height](auto const& source) { if (source->bitmap()) { source_width = source->bitmap()->width(); @@ -70,5 +79,4 @@ WebIDL::ExceptionOr CanvasDrawImage::draw_image(Web::HTML::CanvasImageSour { return draw_image_internal(image, source_x, source_y, source_width, source_height, destination_x, destination_y, destination_width, destination_height); } - } diff --git a/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp b/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp index 0ec066c5d3b..310ac52cfed 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp @@ -127,10 +127,15 @@ WebIDL::ExceptionOr> CanvasPattern::create(JS::Realm& r return WebIDL::SyntaxError::create(realm, "Repetition value is not valid"_string); // Note: Bitmap won't be null here, as if it were it would have "bad" usability. - auto const& bitmap = *image.visit([](auto const& source) -> Gfx::Bitmap const* { return source->bitmap(); }); + auto bitmap = image.visit( + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }, + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }, + [](JS::Handle const& source) -> RefPtr { return source->surface()->create_snapshot(); }, + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }, + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }); // 6. Let pattern be a new CanvasPattern object with the image image and the repetition behavior given by repetition. - auto pattern = TRY_OR_THROW_OOM(realm.vm(), CanvasPatternPaintStyle::create(bitmap, *repetition_value)); + auto pattern = TRY_OR_THROW_OOM(realm.vm(), CanvasPatternPaintStyle::create(*bitmap, *repetition_value)); // FIXME: 7. If image is not origin-clean, then mark pattern as not origin-clean. diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 641155154d6..0b6013f16a6 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -121,7 +122,12 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm if (usability == CanvasImageSourceUsability::Bad) return {}; - auto const* bitmap = image.visit([](auto const& source) -> Gfx::Bitmap const* { return source->bitmap(); }); + auto bitmap = image.visit( + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }, + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }, + [](JS::Handle const& source) -> RefPtr { return source->surface()->create_snapshot(); }, + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }, + [](JS::Handle const& source) -> RefPtr { return *source->bitmap(); }); if (!bitmap) return {}; @@ -180,11 +186,11 @@ void CanvasRenderingContext2D::did_draw(Gfx::FloatRect const&) Gfx::Painter* CanvasRenderingContext2D::painter() { - if (!canvas_element().bitmap()) { - if (!canvas_element().create_bitmap()) + if (!canvas_element().surface()) { + if (!canvas_element().allocate_painting_surface()) return nullptr; canvas_element().document().invalidate_display_list(); - m_painter = Gfx::Painter::create(*canvas_element().bitmap()); + m_painter = make(*canvas_element().surface()); } return m_painter.ptr(); } @@ -379,11 +385,12 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_da 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). - if (!canvas_element().bitmap()) + if (!canvas_element().surface()) return image_data; - auto const& bitmap = *canvas_element().bitmap(); + auto const bitmap = canvas_element().surface()->create_snapshot(); // 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). + //<<<<<<< HEAD auto source_rect = Gfx::Rect { x, y, abs_width, abs_height }; // NOTE: The spec doesn't seem to define this behavior, but MDN does and the WPT tests @@ -392,17 +399,17 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_da if (width < 0 || height < 0) { source_rect = source_rect.translated(min(width, 0), min(height, 0)); } - auto source_rect_intersected = source_rect.intersected(bitmap.rect()); + auto source_rect_intersected = source_rect.intersected(bitmap->rect()); // 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 // might result in a loss of precision, but is according to spec. // See: https://html.spec.whatwg.org/multipage/canvas.html#premultiplied-alpha-and-the-2d-rendering-context - ASSERT(bitmap.alpha_type() == Gfx::AlphaType::Premultiplied); + ASSERT(bitmap->alpha_type() == Gfx::AlphaType::Premultiplied); ASSERT(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(), bitmap, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, drawing_state().global_alpha); + painter->draw_bitmap(image_data->bitmap().rect().to_type(), *bitmap, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, drawing_state().global_alpha); // 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. @@ -423,11 +430,11 @@ void CanvasRenderingContext2D::put_image_data(ImageData const& image_data, float // https://html.spec.whatwg.org/multipage/canvas.html#reset-the-rendering-context-to-its-default-state void CanvasRenderingContext2D::reset_to_default_state() { - auto* bitmap = canvas_element().bitmap(); + auto surface = canvas_element().surface(); // 1. Clear canvas's bitmap to transparent black. - if (bitmap) { - painter()->clear_rect(bitmap->rect().to_type(), Color::Transparent); + if (surface) { + painter()->clear_rect(surface->rect().to_type(), Color::Transparent); } // 2. Empty the list of subpaths in context's current default path. @@ -439,8 +446,8 @@ void CanvasRenderingContext2D::reset_to_default_state() // 4. Reset everything that drawing state consists of to their initial values. reset_drawing_state(); - if (bitmap) - did_draw(bitmap->rect().to_type()); + if (surface) + did_draw(surface->rect().to_type()); } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-measuretext diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index 0367aaabe0a..48e2b215cc2 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -115,7 +116,7 @@ void HTMLCanvasElement::reset_context_to_default_state() WebIDL::ExceptionOr HTMLCanvasElement::set_width(unsigned value) { TRY(set_attribute(HTML::AttributeNames::width, String::number(value))); - m_bitmap = nullptr; + m_surface = nullptr; reset_context_to_default_state(); return {}; } @@ -123,7 +124,7 @@ WebIDL::ExceptionOr HTMLCanvasElement::set_width(unsigned value) WebIDL::ExceptionOr HTMLCanvasElement::set_height(unsigned value) { TRY(set_attribute(HTML::AttributeNames::height, String::number(value))); - m_bitmap = nullptr; + m_surface = nullptr; reset_context_to_default_state(); return {}; } @@ -204,20 +205,23 @@ static Gfx::IntSize bitmap_size_for_canvas(HTMLCanvasElement const& canvas, size return Gfx::IntSize(width, height); } -bool HTMLCanvasElement::create_bitmap(size_t minimum_width, size_t minimum_height) +bool HTMLCanvasElement::allocate_painting_surface(size_t minimum_width, size_t minimum_height) { + if (m_surface) + return true; + + auto traversable = document().navigable()->traversable_navigable(); + VERIFY(traversable); + auto size = bitmap_size_for_canvas(*this, minimum_width, minimum_height); if (size.is_empty()) { - m_bitmap = nullptr; + m_surface = nullptr; return false; } - if (!m_bitmap || m_bitmap->size() != size) { - auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size); - if (bitmap_or_error.is_error()) - return false; - m_bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); + if (!m_surface || m_surface->size() != size) { + m_surface = Gfx::PaintingSurface::create_with_size(traversable->skia_backend_context(), size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied); } - return m_bitmap; + return m_surface; } struct SerializeBitmapResult { @@ -252,18 +256,19 @@ static ErrorOr serialize_bitmap(Gfx::Bitmap const& bitmap String HTMLCanvasElement::to_data_url(StringView type, Optional quality) { // It is possible the the canvas doesn't have a associated bitmap so create one - if (!bitmap()) - create_bitmap(); + if (!m_surface) { + allocate_painting_surface(); + } // 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 (!m_bitmap) + if (!m_surface) 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 file = serialize_bitmap(*m_bitmap, type, move(quality)); + auto file = serialize_bitmap(*m_surface->create_snapshot(), type, move(quality)); // 4. If file is null then return "data:,". if (file.is_error()) { @@ -283,8 +288,9 @@ String HTMLCanvasElement::to_data_url(StringView type, Optional quality) WebIDL::ExceptionOr HTMLCanvasElement::to_blob(JS::NonnullGCPtr callback, StringView type, Optional quality) { // It is possible the the canvas doesn't have a associated bitmap so create one - if (!bitmap()) - create_bitmap(); + if (!m_surface) { + allocate_painting_surface(); + } // FIXME: 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException. @@ -293,8 +299,9 @@ WebIDL::ExceptionOr HTMLCanvasElement::to_blob(JS::NonnullGCPtrclone()); + if (m_surface) { + bitmap_result = m_surface->create_snapshot(); + } // 4. Run these steps in parallel: Platform::EventLoopPlugin::the().deferred_invoke(JS::create_heap_function(heap(), [this, callback, bitmap_result, type, quality] { @@ -326,6 +333,10 @@ WebIDL::ExceptionOr HTMLCanvasElement::to_blob(JS::NonnullGCPtrflush(); + } + m_context.visit( [](JS::NonnullGCPtr&) { // Do nothing, CRC2D writes directly to the canvas bitmap. diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h index 62e8ffa1982..786c6f7dfde 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -22,9 +23,9 @@ public: virtual ~HTMLCanvasElement() override; - Gfx::Bitmap const* bitmap() const { return m_bitmap; } - Gfx::Bitmap* bitmap() { return m_bitmap; } - bool create_bitmap(size_t minimum_width = 0, size_t minimum_height = 0); + bool allocate_painting_surface(size_t minimum_width = 0, size_t minimum_height = 0); + RefPtr surface() { return m_surface; } + RefPtr surface() const { return m_surface; } JS::ThrowCompletionOr get_context(String const& type, JS::Value options); @@ -58,7 +59,7 @@ private: JS::ThrowCompletionOr create_webgl_context(JS::Value options); void reset_context_to_default_state(); - RefPtr m_bitmap; + RefPtr m_surface; Variant, JS::NonnullGCPtr, Empty> m_context; }; diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h index 5e8f548a1f7..6e824b9c8a6 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h @@ -105,6 +105,8 @@ public: }; CheckIfUnloadingIsCanceledResult check_if_unloading_is_canceled(Vector> navigables_that_need_before_unload); + RefPtr skia_backend_context() const { return m_skia_backend_context; } + private: TraversableNavigable(JS::NonnullGCPtr); diff --git a/Userland/Libraries/LibWeb/Painting/CanvasPaintable.cpp b/Userland/Libraries/LibWeb/Painting/CanvasPaintable.cpp index cbef61c5ed5..373b03952a7 100644 --- a/Userland/Libraries/LibWeb/Painting/CanvasPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/CanvasPaintable.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include namespace Web::Painting { @@ -36,11 +37,13 @@ void CanvasPaintable::paint(PaintContext& context, PaintPhase phase) const auto canvas_rect = context.rounded_device_rect(absolute_rect()); ScopedCornerRadiusClip corner_clip { context, canvas_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) }; - if (layout_box().dom_node().bitmap()) { + if (layout_box().dom_node().surface()) { + auto surface = layout_box().dom_node().surface(); + // FIXME: Remove this const_cast. const_cast(layout_box().dom_node()).present(); - auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), layout_box().dom_node().bitmap()->rect(), canvas_rect.to_type()); - context.display_list_recorder().draw_scaled_bitmap(canvas_rect.to_type(), *layout_box().dom_node().bitmap(), layout_box().dom_node().bitmap()->rect(), scaling_mode); + auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), surface->rect(), canvas_rect.to_type()); + context.display_list_recorder().draw_painting_surface(canvas_rect.to_type(), *layout_box().dom_node().surface(), surface->rect(), scaling_mode); } } } diff --git a/Userland/Libraries/LibWeb/Painting/Command.h b/Userland/Libraries/LibWeb/Painting/Command.h index 48262a73506..3256c35a779 100644 --- a/Userland/Libraries/LibWeb/Painting/Command.h +++ b/Userland/Libraries/LibWeb/Painting/Command.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,16 @@ struct DrawScaledBitmap { void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } }; +struct DrawPaintingSurface { + Gfx::IntRect dst_rect; + NonnullRefPtr surface; + Gfx::IntRect src_rect; + Gfx::ScalingMode scaling_mode; + + [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } + void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } +}; + struct DrawScaledImmutableBitmap { Gfx::IntRect dst_rect; NonnullRefPtr bitmap; @@ -417,6 +428,7 @@ using Command = Variant< DrawGlyphRun, FillRect, DrawScaledBitmap, + DrawPaintingSurface, DrawScaledImmutableBitmap, DrawRepeatedImmutableBitmap, Save, diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp index e67a66c5962..c9d40913c52 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp @@ -72,6 +72,7 @@ void DisplayListPlayer::execute(DisplayList& display_list) HANDLE_COMMAND(DrawGlyphRun, draw_glyph_run) else HANDLE_COMMAND(FillRect, fill_rect) else HANDLE_COMMAND(DrawScaledBitmap, draw_scaled_bitmap) + else HANDLE_COMMAND(DrawPaintingSurface, draw_painting_surface) else HANDLE_COMMAND(DrawScaledImmutableBitmap, draw_scaled_immutable_bitmap) else HANDLE_COMMAND(DrawRepeatedImmutableBitmap, draw_repeated_immutable_bitmap) else HANDLE_COMMAND(AddClipRect, add_clip_rect) diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.h b/Userland/Libraries/LibWeb/Painting/DisplayList.h index ceffadbb7dd..733d006ba35 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayList.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayList.h @@ -46,6 +46,7 @@ private: virtual void draw_glyph_run(DrawGlyphRun const&) = 0; virtual void fill_rect(FillRect const&) = 0; virtual void draw_scaled_bitmap(DrawScaledBitmap const&) = 0; + virtual void draw_painting_surface(DrawPaintingSurface const&) = 0; virtual void draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) = 0; virtual void draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) = 0; virtual void save(Save const&) = 0; diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index 840449b3f96..55f1eff50fd 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -355,6 +355,17 @@ void DisplayListPlayerSkia::fill_rect(FillRect const& command) canvas.drawRect(to_skia_rect(rect), paint); } +void DisplayListPlayerSkia::draw_painting_surface(DrawPaintingSurface const& command) +{ + auto src_rect = to_skia_rect(command.src_rect); + auto dst_rect = to_skia_rect(command.dst_rect); + auto& sk_surface = command.surface->sk_surface(); + auto& canvas = surface().canvas(); + auto image = sk_surface.makeImageSnapshot(); + SkPaint paint; + canvas.drawImageRect(image, src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint); +} + void DisplayListPlayerSkia::draw_scaled_bitmap(DrawScaledBitmap const& command) { auto src_rect = to_skia_rect(command.src_rect); diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h index f9276928b8a..705073ad71f 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h @@ -32,6 +32,7 @@ public: private: void draw_glyph_run(DrawGlyphRun const&) override; void fill_rect(FillRect const&) override; + void draw_painting_surface(DrawPaintingSurface const&) override; void draw_scaled_bitmap(DrawScaledBitmap const&) override; void draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override; void draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) override; diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index ffd8aed4b76..9ae63a866cc 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -180,6 +180,18 @@ void DisplayListRecorder::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx:: }); } +void DisplayListRecorder::draw_painting_surface(Gfx::IntRect const& dst_rect, NonnullRefPtr surface, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode) +{ + if (dst_rect.is_empty()) + return; + append(DrawPaintingSurface { + .dst_rect = dst_rect, + .surface = surface, + .src_rect = src_rect, + .scaling_mode = scaling_mode, + }); +} + void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode) { if (dst_rect.is_empty()) diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h index f5c363433d4..76fc23af11a 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -88,6 +88,7 @@ public: void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false); void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); + void draw_painting_surface(Gfx::IntRect const& dst_rect, NonnullRefPtr, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat); diff --git a/Userland/Libraries/LibWeb/WebDriver/Screenshot.cpp b/Userland/Libraries/LibWeb/WebDriver/Screenshot.cpp index c995b525de2..3bb1e68697a 100644 --- a/Userland/Libraries/LibWeb/WebDriver/Screenshot.cpp +++ b/Userland/Libraries/LibWeb/WebDriver/Screenshot.cpp @@ -42,7 +42,7 @@ ErrorOr, WebDriver::Error> draw_boundi MUST(canvas.set_height(paint_height)); // FIXME: 5. Let context, a canvas context mode, be the result of invoking the 2D context creation algorithm given canvas as the target. - if (!canvas.create_bitmap(paint_width, paint_height)) + if (!canvas.allocate_painting_surface(paint_width, paint_height)) return Error::from_code(ErrorCode::UnableToCaptureScreen, "Unable to create a screenshot bitmap"sv); // 6. Complete implementation specific steps equivalent to drawing the region of the framebuffer specified by the following coordinates onto context: @@ -52,7 +52,7 @@ ErrorOr, WebDriver::Error> draw_boundi // - Height: paint height Gfx::IntRect paint_rect { rect.x(), rect.y(), paint_width, paint_height }; - auto backing_store = Web::Painting::BitmapBackingStore(*canvas.bitmap()); + auto backing_store = Web::Painting::BitmapBackingStore(canvas.surface()->create_snapshot()); browsing_context.page().client().paint(paint_rect.to_type(), backing_store); // 7. Return success with canvas. @@ -65,7 +65,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.bitmap()->width() == 0 || canvas.bitmap()->height() == 0) + if (canvas.surface()->size().is_empty()) 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/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp index 36148faac89..cfe16146274 100644 --- a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp +++ b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp @@ -39,14 +39,14 @@ JS::ThrowCompletionOr> WebGLRenderingContext::c // We should be coming here from getContext being called on a wrapped element. auto context_attributes = TRY(convert_value_to_context_attributes_dictionary(canvas_element.vm(), options)); - bool created_bitmap = canvas_element.create_bitmap(/* minimum_width= */ 1, /* minimum_height= */ 1); - if (!created_bitmap) { + bool created_surface = canvas_element.allocate_painting_surface(/* minimum_width= */ 1, /* minimum_height= */ 1); + if (!created_surface) { fire_webgl_context_creation_error(canvas_element); return JS::GCPtr { nullptr }; } - VERIFY(canvas_element.bitmap()); - auto context = OpenGLContext::create(*canvas_element.bitmap()); + VERIFY(canvas_element.surface()); + auto context = OpenGLContext::create(*canvas_element.surface()->create_snapshot()); if (!context) { fire_webgl_context_creation_error(canvas_element); diff --git a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp index a10ca8fa59e..15e7d57744e 100644 --- a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp +++ b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp @@ -53,7 +53,7 @@ void WebGLRenderingContextBase::present() // FIXME: Is this the operation it means? m_context->gl_flush(); - m_context->present(*canvas_element().bitmap()); + m_context->present(*canvas_element().surface()->create_snapshot()); // "By default, after compositing the contents of the drawing buffer shall be cleared to their default values, as shown in the table above. // This default behavior can be changed by setting the preserveDrawingBuffer attribute of the WebGLContextAttributes object.