LibWeb: Return ImmutableBitmap from PaintingSurface::create_snapshot()

This is a preparation for upcoming changes where ImmutableBitmap will
own SkImage allowing Skia to cache GPU textures across repaints.
This commit is contained in:
Aliaksandr Kalenik 2024-11-09 02:36:31 +01:00 committed by Alexander Kalenik
parent 31bf40b659
commit a2c33ea4e1
Notes: github-actions[bot] 2024-11-09 20:21:16 +00:00
13 changed files with 33 additions and 20 deletions

View file

@ -28,6 +28,7 @@ class Line;
class AntiAliasingPainter;
class DeprecatedPainter;
class Painter;
class PaintingSurface;
class Palette;
class PaletteImpl;
class DeprecatedPath;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2023-2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2023-2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

View file

@ -5,6 +5,7 @@
*/
#include <LibGfx/Bitmap.h>
#include <LibGfx/ImmutableBitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <core/SkColorSpace.h>
@ -91,13 +92,13 @@ PaintingSurface::PaintingSurface(NonnullOwnPtr<Impl>&& impl)
PaintingSurface::~PaintingSurface() = default;
RefPtr<Bitmap> PaintingSurface::create_snapshot() const
NonnullRefPtr<ImmutableBitmap> PaintingSurface::create_snapshot() const
{
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size()).value();
auto image_info = SkImageInfo::Make(bitmap->width(), bitmap->height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
SkPixmap const pixmap(image_info, bitmap->begin(), bitmap->pitch());
sk_surface().readPixels(pixmap, 0, 0);
return bitmap;
return ImmutableBitmap::create(bitmap);
}
void PaintingSurface::read_into_bitmap(Gfx::Bitmap& bitmap)

View file

@ -31,7 +31,7 @@ public:
static NonnullRefPtr<PaintingSurface> wrap_metal_surface(Gfx::MetalTexture&, RefPtr<SkiaBackendContext>);
#endif
RefPtr<Bitmap> create_snapshot() const;
NonnullRefPtr<ImmutableBitmap> create_snapshot() const;
void read_into_bitmap(Bitmap&);
IntSize size() const;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -130,7 +131,10 @@ WebIDL::ExceptionOr<JS::GCPtr<CanvasPattern>> CanvasPattern::create(JS::Realm& r
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<HTMLCanvasElement> const& source) -> RefPtr<Gfx::Bitmap> {
auto snapshot = source->surface()->create_snapshot();
return snapshot->bitmap();
},
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); });

View file

@ -2,6 +2,7 @@
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -125,7 +126,10 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
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<HTMLCanvasElement> const& source) -> RefPtr<Gfx::Bitmap> {
auto snapshot = source->surface()->create_snapshot();
return snapshot->bitmap();
},
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); });
if (!bitmap)
@ -389,10 +393,10 @@ WebIDL::ExceptionOr<JS::GCPtr<ImageData>> CanvasRenderingContext2D::get_image_da
// 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().surface())
return image_data;
auto const bitmap = canvas_element().surface()->create_snapshot();
auto const snapshot = canvas_element().surface()->create_snapshot();
auto const& bitmap = 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).
//<<<<<<< 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
@ -401,17 +405,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.

View file

@ -268,7 +268,8 @@ String HTMLCanvasElement::to_data_url(StringView type, Optional<double> quality)
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_surface->create_snapshot(), type, move(quality));
auto snapshot = m_surface->create_snapshot();
auto file = serialize_bitmap(snapshot->bitmap(), type, move(quality));
// 4. If file is null then return "data:,".
if (file.is_error()) {
@ -300,7 +301,8 @@ 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_surface) {
bitmap_result = m_surface->create_snapshot();
auto snapshot = m_surface->create_snapshot();
bitmap_result = snapshot->bitmap();
}
// 4. Run these steps in parallel:

View file

@ -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.surface()->create_snapshot());
auto backing_store = Web::Painting::BitmapBackingStore(canvas.surface()->create_snapshot()->bitmap());
browsing_context.page().client().paint(paint_rect.to_type<Web::DevicePixels>(), backing_store);
// 7. Return success with canvas.

View file

@ -6,11 +6,12 @@
#include <AK/OwnPtr.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Forward.h>
#include <LibWeb/WebGL/OpenGLContext.h>
namespace Web::WebGL {
OwnPtr<OpenGLContext> OpenGLContext::create(Gfx::Bitmap& bitmap)
OwnPtr<OpenGLContext> OpenGLContext::create(Gfx::PaintingSurface& bitmap)
{
(void)bitmap;
return {};

View file

@ -13,9 +13,9 @@ namespace Web::WebGL {
class OpenGLContext {
public:
static OwnPtr<OpenGLContext> create(Gfx::Bitmap&);
static OwnPtr<OpenGLContext> create(Gfx::PaintingSurface&);
virtual void present(Gfx::Bitmap&) = 0;
virtual void present(Gfx::Bitmap const&) = 0;
void clear_buffer_to_default_values();
virtual GLenum gl_get_error() = 0;

View file

@ -46,7 +46,7 @@ JS::ThrowCompletionOr<JS::GCPtr<WebGLRenderingContext>> WebGLRenderingContext::c
}
VERIFY(canvas_element.surface());
auto context = OpenGLContext::create(*canvas_element.surface()->create_snapshot());
auto context = OpenGLContext::create(*canvas_element.surface());
if (!context) {
fire_webgl_context_creation_error(canvas_element);

View file

@ -53,7 +53,7 @@ void WebGLRenderingContextBase::present()
// FIXME: Is this the operation it means?
m_context->gl_flush();
m_context->present(*canvas_element().surface()->create_snapshot());
m_context->present(canvas_element().surface()->create_snapshot()->bitmap());
// "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.