mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-30 13:19:02 +00:00
LibWeb+LibGfx: Use GPU backend for <canvas>
This is implemented by using a GPU-accelerated surface for <canvas> 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.
This commit is contained in:
parent
ab9ecbd79d
commit
a7cbc7a6b8
Notes:
github-actions[bot]
2024-11-07 12:49:12 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: a7cbc7a6b8
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1532
Reviewed-by: https://github.com/ADKaster
20 changed files with 140 additions and 65 deletions
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/PainterSkia.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
@ -13,7 +14,8 @@ Painter::~Painter() = default;
|
|||
|
||||
NonnullOwnPtr<Painter> Painter::create(NonnullRefPtr<Gfx::Bitmap> target_bitmap)
|
||||
{
|
||||
return make<PainterSkia>(move(target_bitmap));
|
||||
auto painting_surface = PaintingSurface::wrap_bitmap(target_bitmap);
|
||||
return make<PainterSkia>(painting_surface);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -58,21 +58,17 @@ static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type)
|
|||
}
|
||||
|
||||
struct PainterSkia::Impl {
|
||||
NonnullRefPtr<Gfx::Bitmap> gfx_bitmap;
|
||||
OwnPtr<SkBitmap> sk_bitmap;
|
||||
OwnPtr<SkCanvas> sk_canvas;
|
||||
RefPtr<Gfx::PaintingSurface> painting_surface;
|
||||
|
||||
Impl(NonnullRefPtr<Gfx::Bitmap> target_bitmap)
|
||||
: gfx_bitmap(move(target_bitmap))
|
||||
Impl(Gfx::PaintingSurface& surface)
|
||||
: painting_surface(surface)
|
||||
{
|
||||
sk_bitmap = make<SkBitmap>();
|
||||
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<SkCanvas>(*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<Gfx::Bitmap> target_bitmap)
|
||||
: m_impl(adopt_own(*new Impl { move(target_bitmap) }))
|
||||
PainterSkia::PainterSkia(NonnullRefPtr<Gfx::PaintingSurface> painting_surface)
|
||||
: m_impl(adopt_own(*new Impl { move(painting_surface) }))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class PainterSkia final : public Painter {
|
||||
public:
|
||||
explicit PainterSkia(NonnullRefPtr<Gfx::Bitmap>);
|
||||
explicit PainterSkia(NonnullRefPtr<Gfx::PaintingSurface>);
|
||||
virtual ~PainterSkia() override;
|
||||
|
||||
virtual void clear_rect(Gfx::FloatRect const&, Color) override;
|
||||
|
|
|
@ -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<HTMLCanvasElement> 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<void> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -127,10 +127,15 @@ WebIDL::ExceptionOr<JS::GCPtr<CanvasPattern>> 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<HTMLImageElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
|
||||
[](JS::Handle<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
|
||||
[](JS::Handle<HTMLCanvasElement> const& source) -> RefPtr<Gfx::Bitmap> { return source->surface()->create_snapshot(); },
|
||||
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
|
||||
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::Bitmap> { 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.
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/PainterSkia.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibUnicode/Segmenter.h>
|
||||
|
@ -121,7 +122,12 @@ WebIDL::ExceptionOr<void> 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<HTMLImageElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
|
||||
[](JS::Handle<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
|
||||
[](JS::Handle<HTMLCanvasElement> const& source) -> RefPtr<Gfx::Bitmap> { return source->surface()->create_snapshot(); },
|
||||
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
|
||||
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::Bitmap> { 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<Gfx::PainterSkia>(*canvas_element().surface());
|
||||
}
|
||||
return m_painter.ptr();
|
||||
}
|
||||
|
@ -379,11 +385,12 @@ WebIDL::ExceptionOr<JS::GCPtr<ImageData>> 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<JS::GCPtr<ImageData>> 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<float>(), bitmap, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, drawing_state().global_alpha);
|
||||
painter->draw_bitmap(image_data->bitmap().rect().to_type<float>(), *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<float>(), Color::Transparent);
|
||||
if (surface) {
|
||||
painter()->clear_rect(surface->rect().to_type<float>(), 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<float>());
|
||||
if (surface)
|
||||
did_draw(surface->rect().to_type<float>());
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-measuretext
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <LibWeb/HTML/HTMLCanvasElement.h>
|
||||
#include <LibWeb/HTML/Numbers.h>
|
||||
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||
#include <LibWeb/Layout/CanvasBox.h>
|
||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
|
@ -115,7 +116,7 @@ void HTMLCanvasElement::reset_context_to_default_state()
|
|||
WebIDL::ExceptionOr<void> 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<void> HTMLCanvasElement::set_width(unsigned value)
|
|||
WebIDL::ExceptionOr<void> 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<SerializeBitmapResult> serialize_bitmap(Gfx::Bitmap const& bitmap
|
|||
String HTMLCanvasElement::to_data_url(StringView type, Optional<double> 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<double> quality)
|
|||
WebIDL::ExceptionOr<void> HTMLCanvasElement::to_blob(JS::NonnullGCPtr<WebIDL::CallbackType> callback, StringView type, Optional<double> 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<void> HTMLCanvasElement::to_blob(JS::NonnullGCPtr<WebIDL::Ca
|
|||
|
||||
// 3. If this canvas element's bitmap has pixels (i.e., neither its horizontal dimension nor its vertical dimension is zero),
|
||||
// then set result to a copy of this canvas element's bitmap.
|
||||
if (m_bitmap)
|
||||
bitmap_result = TRY_OR_THROW_OOM(vm(), m_bitmap->clone());
|
||||
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<void> HTMLCanvasElement::to_blob(JS::NonnullGCPtr<WebIDL::Ca
|
|||
|
||||
void HTMLCanvasElement::present()
|
||||
{
|
||||
if (m_surface) {
|
||||
m_surface->flush();
|
||||
}
|
||||
|
||||
m_context.visit(
|
||||
[](JS::NonnullGCPtr<CanvasRenderingContext2D>&) {
|
||||
// Do nothing, CRC2D writes directly to the canvas bitmap.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibWeb/HTML/HTMLElement.h>
|
||||
#include <LibWeb/WebGL/WebGLRenderingContext.h>
|
||||
|
||||
|
@ -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<Gfx::PaintingSurface> surface() { return m_surface; }
|
||||
RefPtr<Gfx::PaintingSurface const> surface() const { return m_surface; }
|
||||
|
||||
JS::ThrowCompletionOr<RenderingContext> get_context(String const& type, JS::Value options);
|
||||
|
||||
|
@ -58,7 +59,7 @@ private:
|
|||
JS::ThrowCompletionOr<HasOrCreatedContext> create_webgl_context(JS::Value options);
|
||||
void reset_context_to_default_state();
|
||||
|
||||
RefPtr<Gfx::Bitmap> m_bitmap;
|
||||
RefPtr<Gfx::PaintingSurface> m_surface;
|
||||
|
||||
Variant<JS::NonnullGCPtr<HTML::CanvasRenderingContext2D>, JS::NonnullGCPtr<WebGL::WebGLRenderingContext>, Empty> m_context;
|
||||
};
|
||||
|
|
|
@ -105,6 +105,8 @@ public:
|
|||
};
|
||||
CheckIfUnloadingIsCanceledResult check_if_unloading_is_canceled(Vector<JS::Handle<Navigable>> navigables_that_need_before_unload);
|
||||
|
||||
RefPtr<Gfx::SkiaBackendContext> skia_backend_context() const { return m_skia_backend_context; }
|
||||
|
||||
private:
|
||||
TraversableNavigable(JS::NonnullGCPtr<Page>);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||
#include <LibWeb/Painting/CanvasPaintable.h>
|
||||
|
||||
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<HTML::HTMLCanvasElement&>(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<int>());
|
||||
context.display_list_recorder().draw_scaled_bitmap(canvas_rect.to_type<int>(), *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<int>());
|
||||
context.display_list_recorder().draw_painting_surface(canvas_rect.to_type<int>(), *layout_box().dom_node().surface(), surface->rect(), scaling_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <LibGfx/Gradients.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
@ -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<Gfx::PaintingSurface> 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<Gfx::ImmutableBitmap> bitmap;
|
||||
|
@ -417,6 +428,7 @@ using Command = Variant<
|
|||
DrawGlyphRun,
|
||||
FillRect,
|
||||
DrawScaledBitmap,
|
||||
DrawPaintingSurface,
|
||||
DrawScaledImmutableBitmap,
|
||||
DrawRepeatedImmutableBitmap,
|
||||
Save,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Gfx::PaintingSurface> 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())
|
||||
|
|
|
@ -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::PaintingSurface>, 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<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat);
|
||||
|
|
|
@ -42,7 +42,7 @@ ErrorOr<JS::NonnullGCPtr<HTML::HTMLCanvasElement>, 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<JS::NonnullGCPtr<HTML::HTMLCanvasElement>, 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<Web::DevicePixels>(), 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.
|
||||
|
|
|
@ -39,14 +39,14 @@ JS::ThrowCompletionOr<JS::GCPtr<WebGLRenderingContext>> WebGLRenderingContext::c
|
|||
// We should be coming here from getContext being called on a wrapped <canvas> 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<WebGLRenderingContext> { 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);
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue