LibGfx+LibWeb: Cache SkImage in ImmutableBitmap

By caching the SkImage that is reused across repaints, we allow Skia t
optimize GPU texture caching.

ImmutableBitmap is chosen to own the SkImage because it guarantees that
the underlying pixels cannot be modified. This is not the case for
Gfx::Bitmap, where invalidating the SkImage would be challenging since
it exposes pointers to underlying data through methods like scanline().
This commit is contained in:
Aliaksandr Kalenik 2024-11-09 05:48:17 +01:00 committed by Alexander Kalenik
parent 460803d2da
commit 698bca686e
Notes: github-actions[bot] 2024-11-09 20:21:03 +00:00
20 changed files with 186 additions and 118 deletions

View file

@ -10,6 +10,7 @@
#include <LibGfx/ImageFormats/PNGLoader.h>
#include <LibGfx/ImageFormats/TIFFLoader.h>
#include <LibGfx/ImageFormats/TIFFMetadata.h>
#include <LibGfx/ImmutableBitmap.h>
#include <LibGfx/Painter.h>
#include <png.h>
@ -239,11 +240,11 @@ ErrorOr<size_t> PNGLoadingContext::read_frames(png_structp png_ptr, png_infop in
case PNG_BLEND_OP_SOURCE:
// All color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region.
painter->clear_rect(frame_rect, Gfx::Color::Transparent);
painter->draw_bitmap(frame_rect, *decoded_frame_bitmap, decoded_frame_bitmap->rect(), Gfx::ScalingMode::NearestNeighbor, 1.0f);
painter->draw_bitmap(frame_rect, Gfx::ImmutableBitmap::create(*decoded_frame_bitmap), decoded_frame_bitmap->rect(), Gfx::ScalingMode::NearestNeighbor, 1.0f);
break;
case PNG_BLEND_OP_OVER:
// The frame should be composited onto the output buffer based on its alpha, using a simple OVER operation as described in the "Alpha Channel Processing" section of the PNG specification.
painter->draw_bitmap(frame_rect, *decoded_frame_bitmap, decoded_frame_bitmap->rect(), ScalingMode::NearestNeighbor, 1.0f);
painter->draw_bitmap(frame_rect, Gfx::ImmutableBitmap::create(*decoded_frame_bitmap), decoded_frame_bitmap->rect(), ScalingMode::NearestNeighbor, 1.0f);
break;
default:
VERIFY_NOT_REACHED();
@ -262,7 +263,7 @@ ErrorOr<size_t> PNGLoadingContext::read_frames(png_structp png_ptr, png_infop in
case PNG_DISPOSE_OP_PREVIOUS:
// The frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame.
painter->clear_rect(frame_rect, Gfx::Color::Transparent);
painter->draw_bitmap(frame_rect, *prev_output_buffer, IntRect { x, y, width, height }, Gfx::ScalingMode::NearestNeighbor, 1.0f);
painter->draw_bitmap(frame_rect, Gfx::ImmutableBitmap::create(*prev_output_buffer), IntRect { x, y, width, height }, Gfx::ScalingMode::NearestNeighbor, 1.0f);
break;
default:
VERIFY_NOT_REACHED();

View file

@ -6,16 +6,105 @@
#include <LibGfx/ImmutableBitmap.h>
#include <core/SkBitmap.h>
#include <core/SkImage.h>
namespace Gfx {
struct ImmutableBitmapImpl {
sk_sp<SkImage> sk_image;
SkBitmap sk_bitmap;
RefPtr<Gfx::Bitmap> gfx_bitmap;
};
int ImmutableBitmap::width() const
{
return m_impl->sk_image->width();
}
int ImmutableBitmap::height() const
{
return m_impl->sk_image->height();
}
IntRect ImmutableBitmap::rect() const
{
return { {}, size() };
}
IntSize ImmutableBitmap::size() const
{
return { width(), height() };
}
Gfx::AlphaType ImmutableBitmap::alpha_type() const
{
return m_impl->sk_image->alphaType() == kPremul_SkAlphaType ? Gfx::AlphaType::Premultiplied : Gfx::AlphaType::Unpremultiplied;
}
SkImage const* ImmutableBitmap::sk_image() const
{
return m_impl->sk_image.get();
}
RefPtr<Gfx::Bitmap const> ImmutableBitmap::bitmap() const
{
return m_impl->gfx_bitmap;
}
Color ImmutableBitmap::get_pixel(int x, int y) const
{
if (m_impl->gfx_bitmap) {
return m_impl->gfx_bitmap->get_pixel(x, y);
}
VERIFY_NOT_REACHED();
}
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
{
switch (format) {
case Gfx::BitmapFormat::Invalid:
return kUnknown_SkColorType;
case Gfx::BitmapFormat::BGRA8888:
case Gfx::BitmapFormat::BGRx8888:
return kBGRA_8888_SkColorType;
case Gfx::BitmapFormat::RGBA8888:
return kRGBA_8888_SkColorType;
case Gfx::BitmapFormat::RGBx8888:
return kRGB_888x_SkColorType;
default:
return kUnknown_SkColorType;
}
}
static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type)
{
switch (alpha_type) {
case AlphaType::Premultiplied:
return kPremul_SkAlphaType;
case AlphaType::Unpremultiplied:
return kUnpremul_SkAlphaType;
default:
VERIFY_NOT_REACHED();
}
}
NonnullRefPtr<ImmutableBitmap> ImmutableBitmap::create(NonnullRefPtr<Bitmap> bitmap)
{
return adopt_ref(*new ImmutableBitmap(move(bitmap)));
ImmutableBitmapImpl impl;
auto info = SkImageInfo::Make(bitmap->width(), bitmap->height(), to_skia_color_type(bitmap->format()), to_skia_alpha_type(bitmap->alpha_type()));
impl.sk_bitmap.installPixels(info, const_cast<void*>(static_cast<void const*>(bitmap->scanline(0))), bitmap->pitch());
impl.sk_bitmap.setImmutable();
impl.sk_image = impl.sk_bitmap.asImage();
impl.gfx_bitmap = bitmap;
return adopt_ref(*new ImmutableBitmap(make<ImmutableBitmapImpl>(impl)));
}
ImmutableBitmap::ImmutableBitmap(NonnullRefPtr<Bitmap> bitmap)
: m_bitmap(move(bitmap))
ImmutableBitmap::ImmutableBitmap(NonnullOwnPtr<ImmutableBitmapImpl> impl)
: m_impl(move(impl))
{
}
ImmutableBitmap::~ImmutableBitmap() = default;
}

View file

@ -7,31 +7,41 @@
#pragma once
#include <AK/Forward.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/RefCounted.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Rect.h>
class SkImage;
namespace Gfx {
struct ImmutableBitmapImpl;
class ImmutableBitmap final : public RefCounted<ImmutableBitmap> {
public:
static NonnullRefPtr<ImmutableBitmap> create(NonnullRefPtr<Bitmap> bitmap);
~ImmutableBitmap() = default;
~ImmutableBitmap();
Bitmap const& bitmap() const { return *m_bitmap; }
int width() const;
int height() const;
IntRect rect() const;
IntSize size() const;
size_t width() const { return m_bitmap->width(); }
size_t height() const { return m_bitmap->height(); }
Gfx::AlphaType alpha_type() const;
IntRect rect() const { return m_bitmap->rect(); }
IntSize size() const { return m_bitmap->size(); }
SkImage const* sk_image() const;
Color get_pixel(int x, int y) const;
RefPtr<Bitmap const> bitmap() const;
private:
NonnullRefPtr<Bitmap> m_bitmap;
NonnullOwnPtr<ImmutableBitmapImpl> m_impl;
explicit ImmutableBitmap(NonnullRefPtr<Bitmap> bitmap);
explicit ImmutableBitmap(NonnullOwnPtr<ImmutableBitmapImpl> bitmap);
};
}

View file

@ -23,7 +23,7 @@ public:
virtual void clear_rect(Gfx::FloatRect const&, Gfx::Color) = 0;
virtual void fill_rect(Gfx::FloatRect const&, Gfx::Color) = 0;
virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) = 0;
virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) = 0;
virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) = 0;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, float thickness, float global_alpha) = 0;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -7,6 +8,7 @@
#define AK_DONT_REPLACE_STD
#include <AK/OwnPtr.h>
#include <LibGfx/ImmutableBitmap.h>
#include <LibGfx/PainterSkia.h>
#include <LibGfx/PathSkia.h>
@ -28,35 +30,6 @@
namespace Gfx {
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
{
switch (format) {
case Gfx::BitmapFormat::Invalid:
return kUnknown_SkColorType;
case Gfx::BitmapFormat::BGRA8888:
case Gfx::BitmapFormat::BGRx8888:
return kBGRA_8888_SkColorType;
case Gfx::BitmapFormat::RGBA8888:
return kRGBA_8888_SkColorType;
case Gfx::BitmapFormat::RGBx8888:
return kRGB_888x_SkColorType;
default:
return kUnknown_SkColorType;
}
}
static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type)
{
switch (alpha_type) {
case AlphaType::Premultiplied:
return kPremul_SkAlphaType;
case AlphaType::Unpremultiplied:
return kUnpremul_SkAlphaType;
default:
VERIFY_NOT_REACHED();
}
}
struct PainterSkia::Impl {
RefPtr<Gfx::PaintingSurface> painting_surface;
@ -134,17 +107,13 @@ static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode)
}
}
void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, float global_alpha)
void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, float global_alpha)
{
SkBitmap sk_bitmap;
SkImageInfo info = SkImageInfo::Make(src_bitmap.width(), src_bitmap.height(), to_skia_color_type(src_bitmap.format()), to_skia_alpha_type(src_bitmap.alpha_type()));
sk_bitmap.installPixels(info, const_cast<void*>(static_cast<void const*>(src_bitmap.scanline(0))), src_bitmap.pitch());
SkPaint paint;
paint.setAlpha(static_cast<u8>(global_alpha * 255));
impl().canvas()->drawImageRect(
sk_bitmap.asImage(),
src_bitmap.sk_image(),
to_skia_rect(src_rect),
to_skia_rect(dst_rect),
to_skia_sampling_options(scaling_mode),

View file

@ -20,7 +20,7 @@ public:
virtual void clear_rect(Gfx::FloatRect const&, Color) override;
virtual void fill_rect(Gfx::FloatRect const&, Color) override;
virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) override;
virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) override;
virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) override;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, float thickness, float global_alpha) override;
virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) override;

View file

@ -169,7 +169,7 @@ Optional<Gfx::Color> ImageStyleValue::color_if_single_pixel_bitmap() const
{
if (auto const* b = bitmap(m_current_frame_index)) {
if (b->width() == 1 && b->height() == 1)
return b->bitmap().get_pixel(0, 0);
return b->get_pixel(0, 0);
}
return {};
}

View file

@ -13,10 +13,20 @@ namespace Web::HTML {
static void default_source_size(CanvasImageSource const& image, float& source_width, float& source_height)
{
image.visit(
[&source_width, &source_height](JS::Handle<HTMLImageElement> const& source) {
if (source->immutable_bitmap()) {
source_width = source->immutable_bitmap()->width();
source_height = source->immutable_bitmap()->height();
} else {
// FIXME: This is very janky and not correct.
source_width = source->width();
source_height = source->height();
}
},
[&source_width, &source_height](JS::Handle<SVG::SVGImageElement> const& source) {
if (source->bitmap()) {
source_width = source->bitmap()->width();
source_height = source->bitmap()->height();
if (source->current_image_bitmap()) {
source_width = source->current_image_bitmap()->width();
source_height = source->current_image_bitmap()->height();
} else {
// FIXME: This is very janky and not correct.
source_width = source->width()->anim_val()->value();

View file

@ -46,8 +46,8 @@ void CanvasPatternPaintStyle::paint(Gfx::IntRect physical_bounding_box, PaintFun
// 6. The resulting bitmap is what is to be rendered, with the same origin and same scale.
auto const bitmap_width = m_bitmap->width();
auto const bitmap_height = m_bitmap->height();
auto const bitmap_width = m_immutable_bitmap->width();
auto const bitmap_height = m_immutable_bitmap->height();
paint([=, this](auto point) {
point.translate_by(physical_bounding_box.location());
@ -78,8 +78,8 @@ void CanvasPatternPaintStyle::paint(Gfx::IntRect physical_bounding_box, PaintFun
VERIFY_NOT_REACHED();
}
}();
if (m_bitmap->rect().contains(point))
return m_bitmap->get_pixel(point);
if (m_immutable_bitmap->rect().contains(point))
return m_immutable_bitmap->get_pixel(point.x(), point.y());
return Gfx::Color();
});
}
@ -129,14 +129,11 @@ WebIDL::ExceptionOr<JS::GCPtr<CanvasPattern>> CanvasPattern::create(JS::Realm& r
// Note: Bitmap won't be null here, as if it were it would have "bad" usability.
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> {
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(); });
[](JS::Handle<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->immutable_bitmap(); },
[](JS::Handle<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->current_image_bitmap(); },
[](JS::Handle<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->surface()->create_snapshot(); },
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*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));

View file

@ -21,7 +21,7 @@ public:
NoRepeat
};
static ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> create(Gfx::Bitmap const& bitmap, Repetition repetition)
static ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> create(Gfx::ImmutableBitmap const& bitmap, Repetition repetition)
{
return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasPatternPaintStyle(bitmap, repetition));
}
@ -29,13 +29,13 @@ public:
virtual void paint(Gfx::IntRect physical_bounding_box, PaintFunction paint) const override;
private:
CanvasPatternPaintStyle(Gfx::Bitmap const& bitmap, Repetition repetition)
: m_bitmap(bitmap)
CanvasPatternPaintStyle(Gfx::ImmutableBitmap const& immutable_bitmap, Repetition repetition)
: m_immutable_bitmap(immutable_bitmap)
, m_repetition(repetition)
{
}
NonnullRefPtr<Gfx::Bitmap const> m_bitmap;
NonnullRefPtr<Gfx::ImmutableBitmap> m_immutable_bitmap;
Repetition m_repetition { Repetition::Repeat };
};

View file

@ -124,14 +124,22 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
return {};
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> {
auto snapshot = source->surface()->create_snapshot();
return snapshot->bitmap();
[](JS::Handle<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
return source->immutable_bitmap();
},
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); },
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::Bitmap> { return *source->bitmap(); });
[](JS::Handle<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
return source->current_image_bitmap();
},
[](JS::Handle<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
auto surface = source->surface();
if (!surface)
return {};
return source->surface()->create_snapshot();
},
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
return Gfx::ImmutableBitmap::create(*source->bitmap());
});
if (!bitmap)
return {};
@ -394,7 +402,6 @@ WebIDL::ExceptionOr<JS::GCPtr<ImageData>> CanvasRenderingContext2D::get_image_da
if (!canvas_element().surface())
return image_data;
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).
auto source_rect = Gfx::Rect { x, y, abs_width, abs_height };
@ -405,17 +412,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(snapshot->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(snapshot->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>(), *snapshot, 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.
@ -428,7 +435,7 @@ void CanvasRenderingContext2D::put_image_data(ImageData const& image_data, float
{
if (auto* painter = this->painter()) {
auto dst_rect = Gfx::FloatRect(x, y, image_data.width(), image_data.height());
painter->draw_bitmap(dst_rect, image_data.bitmap(), image_data.bitmap().rect(), Gfx::ScalingMode::NearestNeighbor, 1.0f);
painter->draw_bitmap(dst_rect, Gfx::ImmutableBitmap::create(image_data.bitmap()), image_data.bitmap().rect(), Gfx::ScalingMode::NearestNeighbor, 1.0f);
did_draw(dst_rect);
}
}
@ -622,11 +629,11 @@ WebIDL::ExceptionOr<CanvasImageSourceUsability> check_usability_of_image(CanvasI
// FIXME: If image's current request's state is broken, then throw an "InvalidStateError" DOMException.
// If image is not fully decodable, then return bad.
if (!image_element->bitmap())
if (!image_element->immutable_bitmap())
return { CanvasImageSourceUsability::Bad };
// If image has an intrinsic width or intrinsic height (or both) equal to zero, then return bad.
if (image_element->bitmap()->width() == 0 || image_element->bitmap()->height() == 0)
if (image_element->immutable_bitmap()->width() == 0 || image_element->immutable_bitmap()->height() == 0)
return { CanvasImageSourceUsability::Bad };
return Optional<CanvasImageSourceUsability> {};
},
@ -635,11 +642,11 @@ WebIDL::ExceptionOr<CanvasImageSourceUsability> check_usability_of_image(CanvasI
// FIXME: If image's current request's state is broken, then throw an "InvalidStateError" DOMException.
// If image is not fully decodable, then return bad.
if (!image_element->bitmap())
if (!image_element->current_image_bitmap())
return { CanvasImageSourceUsability::Bad };
// If image has an intrinsic width or intrinsic height (or both) equal to zero, then return bad.
if (image_element->bitmap()->width() == 0 || image_element->bitmap()->height() == 0)
if (image_element->current_image_bitmap()->width() == 0 || image_element->current_image_bitmap()->height() == 0)
return { CanvasImageSourceUsability::Bad };
return Optional<CanvasImageSourceUsability> {};
},

View file

@ -269,7 +269,9 @@ String HTMLCanvasElement::to_data_url(StringView type, Optional<double> quality)
// 3. Let file be a serialization of this canvas element's bitmap as a file, passing type and quality if given.
auto snapshot = m_surface->create_snapshot();
auto file = serialize_bitmap(snapshot->bitmap(), type, move(quality));
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, m_surface->size()));
m_surface->read_into_bitmap(*bitmap);
auto file = serialize_bitmap(bitmap, type, move(quality));
// 4. If file is null then return "data:,".
if (file.is_error()) {
@ -301,8 +303,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) {
auto snapshot = m_surface->create_snapshot();
bitmap_result = snapshot->bitmap();
bitmap_result = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, m_surface->size()));
m_surface->read_into_bitmap(*bitmap_result);
}
// 4. Run these steps in parallel:

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -120,13 +121,6 @@ RefPtr<Gfx::ImmutableBitmap> HTMLImageElement::immutable_bitmap() const
return current_image_bitmap();
}
RefPtr<Gfx::Bitmap const> HTMLImageElement::bitmap() const
{
if (auto immutable_bitmap = this->immutable_bitmap())
return immutable_bitmap->bitmap();
return {};
}
bool HTMLImageElement::is_image_available() const
{
return m_current_request && m_current_request->is_available();

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -43,7 +44,6 @@ public:
String src() const { return get_attribute_value(HTML::AttributeNames::src); }
RefPtr<Gfx::ImmutableBitmap> immutable_bitmap() const;
RefPtr<Gfx::Bitmap const> bitmap() const;
unsigned width() const;
WebIDL::ExceptionOr<void> set_width(unsigned);

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2020-2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -337,7 +338,7 @@ EventResult EventHandler::handle_mouseup(CSSPixelPoint viewport_position, CSSPix
if (is<HTML::HTMLImageElement>(*node)) {
auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
auto image_url = image_element.document().parse_url(image_element.src());
m_navigable->page().client().page_did_request_image_context_menu(viewport_position, image_url, "", modifiers, image_element.bitmap());
m_navigable->page().client().page_did_request_image_context_menu(viewport_position, image_url, "", modifiers, image_element.immutable_bitmap()->bitmap());
} else if (is<HTML::HTMLMediaElement>(*node)) {
auto& media_element = verify_cast<HTML::HTMLMediaElement>(*node);

View file

@ -381,18 +381,13 @@ void DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmutableBitm
{
auto src_rect = to_skia_rect(command.src_rect);
auto dst_rect = to_skia_rect(command.dst_rect);
auto bitmap = to_skia_bitmap(command.bitmap->bitmap());
auto image = SkImages::RasterFromBitmap(bitmap);
auto& canvas = surface().canvas();
SkPaint paint;
canvas.drawImageRect(image, src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint);
canvas.drawImageRect(command.bitmap->sk_image(), src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint);
}
void DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const& command)
{
auto bitmap = to_skia_bitmap(command.bitmap->bitmap());
auto image = SkImages::RasterFromBitmap(bitmap);
SkMatrix matrix;
auto dst_rect = command.dst_rect.to_type<float>();
auto src_size = command.bitmap->size().to_type<float>();
@ -402,7 +397,7 @@ void DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutable
auto tile_mode_x = command.repeat.x ? SkTileMode::kRepeat : SkTileMode::kDecal;
auto tile_mode_y = command.repeat.y ? SkTileMode::kRepeat : SkTileMode::kDecal;
auto shader = image->makeShader(tile_mode_x, tile_mode_y, sampling_options, matrix);
auto shader = command.bitmap->sk_image()->makeShader(tile_mode_x, tile_mode_y, sampling_options, matrix);
SkPaint paint;
paint.setShader(shader);

View file

@ -29,15 +29,6 @@ public:
Gfx::Rect<CSSPixels> bounding_box() const;
// FIXME: This is a hack for images used as CanvasImageSource. Do something more elegant.
RefPtr<Gfx::Bitmap> bitmap() const
{
auto bitmap = current_image_bitmap();
if (!bitmap)
return nullptr;
return bitmap->bitmap();
}
// ^Layout::ImageProvider
virtual bool is_image_available() const override;
virtual Optional<CSSPixels> intrinsic_width() const override;

View file

@ -52,7 +52,9 @@ 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()->bitmap());
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, canvas.surface()->size()));
canvas.surface()->read_into_bitmap(*bitmap);
auto backing_store = Web::Painting::BitmapBackingStore(bitmap);
browsing_context.page().client().paint(paint_rect.to_type<Web::DevicePixels>(), backing_store);
// 7. Return success with canvas.

View file

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

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()->bitmap());
m_context->present();
// "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.