LibGfx+LibWeb: Make DecodedImageFrame a value type

DecodedImageFrame only wraps a ref-counted Bitmap and color-space
metadata. The frame object itself does not provide shared mutable
state or lifetime ownership beyond those members, so ref-counting it
adds an unnecessary layer of indirection.
This commit is contained in:
Aliaksandr Kalenik 2026-05-07 14:39:50 +02:00 committed by Alexander Kalenik
parent b221d7fe8b
commit f8640d813a
56 changed files with 225 additions and 188 deletions

View file

@ -7,7 +7,6 @@
#pragma once
#include <AK/Assertions.h>
#include <AK/AtomicRefCounted.h>
#include <AK/NonnullRefPtr.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/ColorSpace.h>
@ -16,29 +15,22 @@
namespace Gfx {
class DecodedImageFrame final : public AtomicRefCounted<DecodedImageFrame> {
class DecodedImageFrame final {
public:
static NonnullRefPtr<DecodedImageFrame> create(NonnullRefPtr<Bitmap const> bitmap, ColorSpace color_space = {})
DecodedImageFrame(NonnullRefPtr<Bitmap const> bitmap, ColorSpace color_space = {})
: m_bitmap(move(bitmap))
, m_color_space(move(color_space))
{
return adopt_ref(*new DecodedImageFrame(move(bitmap), move(color_space)));
}
static NonnullRefPtr<DecodedImageFrame> create(Bitmap const& bitmap, ColorSpace color_space = {})
DecodedImageFrame(Bitmap const& bitmap, ColorSpace color_space = {})
: DecodedImageFrame(NonnullRefPtr<Bitmap const> { bitmap }, move(color_space))
{
return create(NonnullRefPtr<Bitmap const> { bitmap }, move(color_space));
}
static NonnullRefPtr<DecodedImageFrame> create(Bitmap const& bitmap, AlphaType alpha_type, ColorSpace color_space = {})
DecodedImageFrame(Bitmap const& bitmap, AlphaType alpha_type, ColorSpace color_space = {})
: DecodedImageFrame(bitmap_with_alpha_type(bitmap, alpha_type), move(color_space))
{
auto converted_bitmap = [&] -> NonnullRefPtr<Bitmap const> {
if (bitmap.alpha_type() == alpha_type)
return NonnullRefPtr<Bitmap const> { bitmap };
auto new_bitmap = MUST(bitmap.clone());
new_bitmap->set_alpha_type_destructive(alpha_type);
return new_bitmap;
}();
return create(move(converted_bitmap), move(color_space));
}
Bitmap const& bitmap() const { return *m_bitmap; }
@ -51,10 +43,13 @@ public:
IntSize size() const { return m_bitmap->size(); }
private:
DecodedImageFrame(NonnullRefPtr<Bitmap const> bitmap, ColorSpace color_space)
: m_bitmap(move(bitmap))
, m_color_space(move(color_space))
static NonnullRefPtr<Bitmap const> bitmap_with_alpha_type(Bitmap const& bitmap, AlphaType alpha_type)
{
if (bitmap.alpha_type() == alpha_type)
return NonnullRefPtr<Bitmap const> { bitmap };
auto new_bitmap = MUST(bitmap.clone());
new_bitmap->set_alpha_type_destructive(alpha_type);
return new_bitmap;
}
NonnullRefPtr<Bitmap const> m_bitmap;

View file

@ -9,6 +9,7 @@
#include <LibGfx/SkiaBackendContext.h>
#include <LibGfx/SkiaUtils.h>
#include <core/SkColorSpace.h>
#include <core/SkImage.h>
#include <gpu/ganesh/GrDirectContext.h>
#include <gpu/ganesh/SkImageGanesh.h>
@ -28,12 +29,13 @@ DecodedImageFrameSkiaImageCache::~DecodedImageFrameSkiaImageCache() = default;
sk_sp<SkImage> DecodedImageFrameSkiaImageCache::image_for_frame(DecodedImageFrame const& frame)
{
if (auto it = m_images.find(&frame); it != m_images.end()) {
auto const& bitmap = frame.bitmap();
if (auto it = m_images.find(frame); it != m_images.end()) {
it->value.last_used_generation = m_generation;
return it->value.image;
}
auto raster_image = sk_image_from_bitmap(frame.bitmap(), frame.color_space());
auto raster_image = sk_image_from_bitmap(bitmap, frame.color_space());
sk_sp<SkImage> image;
auto* gr_context = m_skia_backend_context ? m_skia_backend_context->sk_context() : nullptr;
if (gr_context) {
@ -48,11 +50,10 @@ sk_sp<SkImage> DecodedImageFrameSkiaImageCache::image_for_frame(DecodedImageFram
return nullptr;
CachedImage cached_image {
.frame = &frame,
.image = image,
.last_used_generation = m_generation,
};
m_images.set(&frame, move(cached_image));
m_images.set(frame, move(cached_image));
return image;
}

View file

@ -6,12 +6,15 @@
#pragma once
#include <AK/HashFunctions.h>
#include <AK/HashMap.h>
#include <AK/RefPtr.h>
#include <AK/Types.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <core/SkRefCnt.h>
class SkColorSpace;
class SkImage;
namespace Gfx {
@ -26,14 +29,36 @@ public:
void prune();
private:
struct DecodedImageFrameKeyTraits : public Traits<DecodedImageFrame> {
static unsigned hash(DecodedImageFrame const& frame)
{
return pair_int_hash(
ptr_hash(&frame.bitmap()),
ptr_hash(color_space_pointer(frame)));
}
static bool equals(DecodedImageFrame const& a, DecodedImageFrame const& b)
{
return &a.bitmap() == &b.bitmap()
&& color_space_pointer(a) == color_space_pointer(b);
}
static constexpr bool may_have_slow_equality_check() { return false; }
private:
static SkColorSpace const* color_space_pointer(DecodedImageFrame const& frame)
{
return frame.color_space().color_space<sk_sp<SkColorSpace>>().get();
}
};
struct CachedImage {
RefPtr<DecodedImageFrame const> frame;
sk_sp<SkImage> image;
u64 last_used_generation { 0 };
};
RefPtr<SkiaBackendContext> m_skia_backend_context;
HashMap<DecodedImageFrame const*, CachedImage> m_images;
HashMap<DecodedImageFrame, CachedImage, DecodedImageFrameKeyTraits> m_images;
u64 m_generation { 0 };
};

View file

@ -318,11 +318,11 @@ ErrorOr<size_t> PNGLoadingContext::read_frames(png_structp png_ptr, png_infop in
switch (blend_op) {
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->draw_bitmap(frame_rect, *Gfx::DecodedImageFrame::create(*decoded_frame_bitmap), decoded_frame_bitmap->rect(), Gfx::ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::Copy);
painter->draw_bitmap(frame_rect, Gfx::DecodedImageFrame { *decoded_frame_bitmap }, decoded_frame_bitmap->rect(), Gfx::ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::Copy);
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, *Gfx::DecodedImageFrame::create(*decoded_frame_bitmap), decoded_frame_bitmap->rect(), ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::SourceOver);
painter->draw_bitmap(frame_rect, Gfx::DecodedImageFrame { *decoded_frame_bitmap }, decoded_frame_bitmap->rect(), ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::SourceOver);
break;
default:
VERIFY_NOT_REACHED();
@ -341,7 +341,7 @@ ErrorOr<size_t> PNGLoadingContext::read_frames(png_structp png_ptr, png_infop in
break;
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->draw_bitmap(frame_rect, *Gfx::DecodedImageFrame::create(*prev_output_buffer), IntRect { x, y, width, height }, Gfx::ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::Copy);
painter->draw_bitmap(frame_rect, Gfx::DecodedImageFrame { *prev_output_buffer }, IntRect { x, y, width, height }, Gfx::ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::Copy);
break;
default:
VERIFY_NOT_REACHED();

View file

@ -10,18 +10,18 @@
namespace Gfx {
CanvasPatternPaintStyle::CanvasPatternPaintStyle(RefPtr<DecodedImageFrame> image, Repetition repetition)
CanvasPatternPaintStyle::CanvasPatternPaintStyle(Optional<DecodedImageFrame> image, Repetition repetition)
: m_image(move(image))
, m_repetition(repetition)
{
}
ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> CanvasPatternPaintStyle::create(RefPtr<DecodedImageFrame> image, Repetition repetition)
ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> CanvasPatternPaintStyle::create(Optional<DecodedImageFrame> image, Repetition repetition)
{
return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasPatternPaintStyle(move(image), repetition));
}
RefPtr<DecodedImageFrame> CanvasPatternPaintStyle::image() const
Optional<DecodedImageFrame> CanvasPatternPaintStyle::image() const
{
return m_image;
}

View file

@ -8,12 +8,13 @@
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/QuickSort.h>
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <AK/Vector.h>
#include <LibGfx/AffineTransform.h>
#include <LibGfx/Color.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Gradients.h>
#include <LibGfx/Rect.h>
@ -90,17 +91,17 @@ public:
NoRepeat
};
static ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> create(RefPtr<DecodedImageFrame> image, Repetition repetition);
static ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> create(Optional<DecodedImageFrame> image, Repetition repetition);
RefPtr<DecodedImageFrame> image() const;
Optional<DecodedImageFrame> image() const;
Repetition repetition() const { return m_repetition; }
Optional<AffineTransform> const& transform() const { return m_transform; }
void set_transform(AffineTransform const& transform) { m_transform = transform; }
private:
CanvasPatternPaintStyle(RefPtr<DecodedImageFrame> image, Repetition repetition);
CanvasPatternPaintStyle(Optional<DecodedImageFrame> image, Repetition repetition);
RefPtr<DecodedImageFrame> m_image;
Optional<DecodedImageFrame> m_image;
Repetition m_repetition { Repetition::Repeat };
Optional<AffineTransform> m_transform;
};

View file

@ -87,7 +87,7 @@ static void apply_paint_style(SkPaint& paint, PaintStyle const& style, DecodedIm
paint.setShader(shader);
} else if (auto const* canvas_pattern = as_if<CanvasPatternPaintStyle>(style)) {
auto frame = canvas_pattern->image();
if (!frame)
if (!frame.has_value())
return;
auto sk_image = image_cache.image_for_frame(*frame);
if (!sk_image)

View file

@ -2058,7 +2058,7 @@ NonnullRefPtr<StyleValue const> Parser::parse_as_sizes_attribute(DOM::Element co
// 3. If size is auto, and img is not null, and img is being rendered, and img allows auto-sizes,
// then set size to the concrete object size width of img, in CSS pixels.
// FIXME: "img is being rendered" - we just see if it has a bitmap for now
if (size->has_auto() && img && img->current_image_frame() && img->allows_auto_sizes()) {
if (size->has_auto() && img && img->current_image_frame().has_value() && img->allows_auto_sizes()) {
// FIXME: The spec doesn't seem to tell us how to determine the concrete size of an <img>, so use the default sizing algorithm.
// Should this use some of the methods from FormattingContext?
auto concrete_size = run_default_sizing_algorithm(

View file

@ -118,11 +118,11 @@ bool ImageStyleValue::is_paintable() const
return image_data();
}
RefPtr<Gfx::DecodedImageFrame> ImageStyleValue::frame(size_t frame_index, Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> ImageStyleValue::frame(size_t frame_index, Gfx::IntSize size) const
{
if (auto image_data = this->image_data())
return image_data->frame(frame_index, size);
return nullptr;
return {};
}
void ImageStyleValue::serialize(StringBuilder& builder, SerializationMode) const
@ -170,7 +170,7 @@ void ImageStyleValue::paint(DisplayListRecordingContext& context, DevicePixelRec
image_data->paint(context, m_current_frame_index, dest_int_rect, dest_int_rect, scaling_mode);
}
RefPtr<Gfx::DecodedImageFrame> ImageStyleValue::current_frame(DevicePixelRect const& dest_rect) const
Optional<Gfx::DecodedImageFrame> ImageStyleValue::current_frame(DevicePixelRect const& dest_rect) const
{
return frame(m_current_frame_index, dest_rect.size().to_type<int>());
}
@ -184,7 +184,7 @@ GC::Ptr<HTML::DecodedImageData> ImageStyleValue::image_data() const
Optional<Gfx::Color> ImageStyleValue::color_if_single_pixel_bitmap() const
{
if (auto decoded_frame = frame(m_current_frame_index)) {
if (auto decoded_frame = frame(m_current_frame_index); decoded_frame.has_value()) {
auto const& bitmap = decoded_frame->bitmap();
if (bitmap.width() == 1 && bitmap.height() == 1)
return bitmap.get_pixel(0, 0);

View file

@ -9,7 +9,9 @@
#pragma once
#include <AK/Optional.h>
#include <LibGC/Weak.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
@ -58,7 +60,7 @@ public:
void paint(DisplayListRecordingContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override;
virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override;
RefPtr<Gfx::DecodedImageFrame> current_frame(DevicePixelRect const& dest_rect) const;
Optional<Gfx::DecodedImageFrame> current_frame(DevicePixelRect const& dest_rect) const;
mutable Function<void()> on_animate;
@ -76,7 +78,7 @@ private:
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
void animate();
RefPtr<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const;
Optional<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const;
GC::Ptr<HTML::SharedResourceRequest> m_resource_request;
GC::Ptr<CSSStyleSheet> m_style_sheet;

View file

@ -4583,11 +4583,11 @@ Optional<FlyString> Element::document_scoped_view_transition_name()
// https://drafts.csswg.org/css-view-transitions-1/#capture-the-image
// To capture the image given an element element, perform the following steps. They return an image.
RefPtr<Gfx::DecodedImageFrame> Element::capture_the_image()
Optional<Gfx::DecodedImageFrame> Element::capture_the_image()
{
// FIXME: Actually implement this.
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, Gfx::IntSize(1, 1)));
return Gfx::DecodedImageFrame::create(*bitmap);
return Gfx::DecodedImageFrame { *bitmap };
}
void Element::set_pointer_capture(WebIDL::Long pointer_id)

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibWeb/ARIA/ARIAMixin.h>
#include <LibWeb/Animations/Animatable.h>
#include <LibWeb/Bindings/Element.h>
@ -586,7 +587,7 @@ public:
Optional<FlyString> document_scoped_view_transition_name();
// https://drafts.csswg.org/css-view-transitions-1/#capture-the-image
RefPtr<Gfx::DecodedImageFrame> capture_the_image();
Optional<Gfx::DecodedImageFrame> capture_the_image();
void set_pointer_capture(WebIDL::Long pointer_id);
void release_pointer_capture(WebIDL::Long pointer_id);

View file

@ -68,7 +68,7 @@ GC::Ref<AnimatedDecodedImageData> AnimatedDecodedImageData::create(
for (u32 i = 0; i < initial_bitmaps.size(); ++i) {
auto& slot = data->m_buffer_slots[i % BUFFER_POOL_SIZE];
slot.frame_index = i;
slot.frame = Gfx::DecodedImageFrame::create(*initial_bitmaps[i], data->m_color_space);
slot.frame = Gfx::DecodedImageFrame { *initial_bitmaps[i], data->m_color_space };
slot.generation = ++data->m_write_generation;
}
@ -105,7 +105,7 @@ size_t AnimatedDecodedImageData::external_memory_size() const
{
size_t size = JS::vector_external_memory_size(m_durations);
for (auto const& slot : m_buffer_slots) {
if (slot.frame)
if (slot.frame.has_value())
size = JS::saturating_add_external_memory_size(size, slot.frame->bitmap().data_size());
}
return size;
@ -121,7 +121,7 @@ void AnimatedDecodedImageData::finalize()
AnimatedDecodedImageData::BufferSlot const* AnimatedDecodedImageData::find_slot(u32 frame_index) const
{
for (auto const& slot : m_buffer_slots) {
if (slot.frame_index == frame_index && slot.frame)
if (slot.frame_index == frame_index && slot.frame.has_value())
return &slot;
}
return nullptr;
@ -137,7 +137,7 @@ AnimatedDecodedImageData::BufferSlot& AnimatedDecodedImageData::evict_oldest_slo
return *oldest;
}
RefPtr<Gfx::DecodedImageFrame> AnimatedDecodedImageData::frame(size_t frame_index, Gfx::IntSize) const
Optional<Gfx::DecodedImageFrame> AnimatedDecodedImageData::frame(size_t frame_index, Gfx::IntSize) const
{
if (frame_index >= m_frame_count)
return m_last_displayed_frame;
@ -181,7 +181,7 @@ Optional<Gfx::IntRect> AnimatedDecodedImageData::frame_rect(size_t) const
void AnimatedDecodedImageData::paint(DisplayListRecordingContext& context, size_t frame_index, Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::ScalingMode scaling_mode) const
{
auto decoded_frame = frame(frame_index);
if (!decoded_frame)
if (!decoded_frame.has_value())
return;
context.display_list_recorder().draw_scaled_decoded_image_frame(dst_rect, clip_rect, *decoded_frame, scaling_mode);
}
@ -201,7 +201,7 @@ void AnimatedDecodedImageData::receive_frames(Vector<NonnullRefPtr<Gfx::Bitmap>>
auto& slot = evict_oldest_slot();
slot.frame_index = frame_index;
slot.frame = Gfx::DecodedImageFrame::create(*bitmaps[i], m_color_space);
slot.frame = Gfx::DecodedImageFrame { *bitmaps[i], m_color_space };
slot.generation = ++m_write_generation;
}
}

View file

@ -36,7 +36,7 @@ public:
virtual ~AnimatedDecodedImageData() override;
virtual void finalize() override;
virtual RefPtr<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const override;
virtual Optional<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const override;
virtual int frame_duration(size_t frame_index) const override;
virtual size_t frame_count() const override { return m_frame_count; }
@ -67,7 +67,7 @@ private:
struct BufferSlot {
Optional<u32> frame_index;
RefPtr<Gfx::DecodedImageFrame> frame;
Optional<Gfx::DecodedImageFrame> frame;
u64 generation { 0 };
};
@ -93,7 +93,7 @@ private:
Vector<u32> m_durations;
Array<BufferSlot, BUFFER_POOL_SIZE> m_buffer_slots;
mutable RefPtr<Gfx::DecodedImageFrame> m_last_displayed_frame;
mutable Optional<Gfx::DecodedImageFrame> m_last_displayed_frame;
u64 m_write_generation { 0 };
bool m_request_in_flight { false };
u32 m_current_frame_index { 0 };

View file

@ -33,17 +33,15 @@ BitmapDecodedImageData::~BitmapDecodedImageData() = default;
size_t BitmapDecodedImageData::external_memory_size() const
{
size_t size = JS::vector_external_memory_size(m_frames);
for (auto const& frame : m_frames) {
if (frame.frame)
size = JS::saturating_add_external_memory_size(size, frame.frame->bitmap().data_size());
}
for (auto const& frame : m_frames)
size = JS::saturating_add_external_memory_size(size, frame.frame.bitmap().data_size());
return size;
}
RefPtr<Gfx::DecodedImageFrame> BitmapDecodedImageData::frame(size_t frame_index, Gfx::IntSize) const
Optional<Gfx::DecodedImageFrame> BitmapDecodedImageData::frame(size_t frame_index, Gfx::IntSize) const
{
if (frame_index >= m_frames.size())
return nullptr;
return {};
return m_frames[frame_index].frame;
}
@ -56,27 +54,27 @@ int BitmapDecodedImageData::frame_duration(size_t frame_index) const
Optional<CSSPixels> BitmapDecodedImageData::intrinsic_width() const
{
return m_frames.first().frame->width();
return m_frames.first().frame.width();
}
Optional<CSSPixels> BitmapDecodedImageData::intrinsic_height() const
{
return m_frames.first().frame->height();
return m_frames.first().frame.height();
}
Optional<CSSPixelFraction> BitmapDecodedImageData::intrinsic_aspect_ratio() const
{
return CSSPixels(m_frames.first().frame->width()) / CSSPixels(m_frames.first().frame->height());
return CSSPixels(m_frames.first().frame.width()) / CSSPixels(m_frames.first().frame.height());
}
Optional<Gfx::IntRect> BitmapDecodedImageData::frame_rect(size_t frame_index) const
{
return m_frames[frame_index].frame->rect();
return m_frames[frame_index].frame.rect();
}
void BitmapDecodedImageData::paint(DisplayListRecordingContext& context, size_t frame_index, Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::ScalingMode scaling_mode) const
{
context.display_list_recorder().draw_scaled_decoded_image_frame(dst_rect, clip_rect, *m_frames[frame_index].frame, scaling_mode);
context.display_list_recorder().draw_scaled_decoded_image_frame(dst_rect, clip_rect, m_frames[frame_index].frame, scaling_mode);
}
}

View file

@ -18,14 +18,14 @@ class BitmapDecodedImageData final : public DecodedImageData {
public:
struct Frame {
RefPtr<Gfx::DecodedImageFrame> frame;
Gfx::DecodedImageFrame frame;
int duration { 0 };
};
static ErrorOr<GC::Ref<BitmapDecodedImageData>> create(JS::Realm&, Vector<Frame>&&, size_t loop_count, bool animated);
virtual ~BitmapDecodedImageData() override;
virtual RefPtr<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const override;
virtual Optional<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const override;
virtual int frame_duration(size_t frame_index) const override;
virtual size_t frame_count() const override { return m_frames.size(); }

View file

@ -17,14 +17,14 @@ Gfx::IntSize canvas_image_source_dimensions(CanvasImageSource const& image)
{
return image.visit(
[](GC::Root<HTMLImageElement> const& source) -> Gfx::IntSize {
if (auto frame = source->current_image_frame())
if (auto frame = source->current_image_frame(); frame.has_value())
return frame->size();
// FIXME: This is very janky and not correct.
return { source->width(), source->height() };
},
[](GC::Root<SVG::SVGImageElement> const& source) -> Gfx::IntSize {
if (auto decoded_image_frame = source->current_image_frame())
if (auto decoded_image_frame = source->current_image_frame(); decoded_image_frame.has_value())
return decoded_image_frame->size();
// FIXME: This is very janky and not correct.
@ -50,10 +50,10 @@ Gfx::IntSize canvas_image_source_dimensions(CanvasImageSource const& image)
});
}
RefPtr<Gfx::DecodedImageFrame> canvas_image_source_frame(CanvasImageSource const& image)
Optional<Gfx::DecodedImageFrame> canvas_image_source_frame(CanvasImageSource const& image)
{
return image.visit(
[](OneOf<GC::Root<HTMLImageElement>, GC::Root<SVG::SVGImageElement>> auto const& element) -> RefPtr<Gfx::DecodedImageFrame> {
[](OneOf<GC::Root<HTMLImageElement>, GC::Root<SVG::SVGImageElement>> auto const& element) -> Optional<Gfx::DecodedImageFrame> {
auto image_data = element->decoded_image_data();
if (!image_data)
return {};
@ -66,20 +66,20 @@ RefPtr<Gfx::DecodedImageFrame> canvas_image_source_frame(CanvasImageSource const
return image_data->frame(0, size);
},
[](GC::Root<HTMLCanvasElement> const& canvas) -> RefPtr<Gfx::DecodedImageFrame> {
[](GC::Root<HTMLCanvasElement> const& canvas) -> Optional<Gfx::DecodedImageFrame> {
canvas->present();
auto surface = canvas->surface();
if (!surface)
return Gfx::DecodedImageFrame::create(*canvas->get_bitmap_from_surface());
return Gfx::DecodedImageFrame::create(*surface->snapshot_bitmap());
return Gfx::DecodedImageFrame { *canvas->get_bitmap_from_surface() };
return Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
},
[](OneOf<GC::Root<ImageBitmap>, GC::Root<OffscreenCanvas>> auto const& source) -> RefPtr<Gfx::DecodedImageFrame> {
[](OneOf<GC::Root<ImageBitmap>, GC::Root<OffscreenCanvas>> auto const& source) -> Optional<Gfx::DecodedImageFrame> {
auto bitmap = source->bitmap();
if (!bitmap)
return {};
return Gfx::DecodedImageFrame::create(*bitmap);
return Gfx::DecodedImageFrame { *bitmap };
},
[](GC::Root<HTMLVideoElement> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
[](GC::Root<HTMLVideoElement> const& source) -> Optional<Gfx::DecodedImageFrame> {
return source->current_decoded_image_frame();
});
}

View file

@ -7,6 +7,8 @@
#pragma once
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Size.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/HTMLCanvasElement.h>
@ -23,7 +25,7 @@ namespace Web::HTML {
using CanvasImageSource = Variant<GC::Root<HTMLImageElement>, GC::Root<SVG::SVGImageElement>, GC::Root<HTMLCanvasElement>, GC::Root<ImageBitmap>, GC::Root<OffscreenCanvas>, GC::Root<HTMLVideoElement>>;
Gfx::IntSize canvas_image_source_dimensions(CanvasImageSource const&);
RefPtr<Gfx::DecodedImageFrame> canvas_image_source_frame(CanvasImageSource const&);
Optional<Gfx::DecodedImageFrame> canvas_image_source_frame(CanvasImageSource const&);
// https://html.spec.whatwg.org/multipage/canvas.html#canvasdrawimage
class CanvasDrawImage {

View file

@ -165,7 +165,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
return {};
auto frame = canvas_image_source_frame(image);
if (!frame)
if (!frame.has_value())
return {};
auto const& bitmap = frame->bitmap();
@ -576,7 +576,7 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data
auto surface = canvas_element().surface();
if (!surface)
return image_data;
auto const snapshot = Gfx::DecodedImageFrame::create(*surface->snapshot_bitmap());
auto const snapshot = Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
// 5. Let the source rectangle be the rectangle whose corners are the four points (sx, sy), (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh).
auto source_rect = Gfx::Rect { x, y, abs_width, abs_height };
@ -587,17 +587,17 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data
if (width < 0 || height < 0) {
source_rect = source_rect.translated(min(width, 0), min(height, 0));
}
auto source_rect_intersected = source_rect.intersected(snapshot->rect());
auto source_rect_intersected = source_rect.intersected(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
VERIFY(snapshot->bitmap().alpha_type() == Gfx::AlphaType::Premultiplied);
VERIFY(snapshot.bitmap().alpha_type() == Gfx::AlphaType::Premultiplied);
VERIFY(image_data->bitmap().alpha_type() == Gfx::AlphaType::Unpremultiplied);
auto painter = Gfx::Painter::create(image_data->bitmap());
painter->draw_bitmap(image_data->bitmap().rect().to_type<float>(), *snapshot, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver);
painter->draw_bitmap(image_data->bitmap().rect().to_type<float>(), snapshot, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver);
// 7. Set the pixels values of imageData for areas of the source rectangle that are outside of the output bitmap to transparent black.
// NOTE: No-op, already done during creation.
@ -689,7 +689,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::put_pixels_from_an_image_dat
painter.set_transform({});
painter.draw_bitmap(
dst_rect,
*Gfx::DecodedImageFrame::create(image_data.bitmap(), Gfx::AlphaType::Unpremultiplied),
Gfx::DecodedImageFrame { image_data.bitmap(), Gfx::AlphaType::Unpremultiplied },
Gfx::IntRect { dirty_x, dirty_y, dirty_width, dirty_height },
Gfx::ScalingMode::NearestNeighbor,
{},
@ -916,11 +916,12 @@ WebIDL::ExceptionOr<CanvasImageSourceUsability> check_usability_of_image(CanvasI
return WebIDL::InvalidStateError::create(image_element->realm(), "Image element state is broken"_utf16);
// If image is not fully decodable, then return bad.
if (!image_element->current_image_frame())
auto current_image_frame = image_element->current_image_frame();
if (!current_image_frame.has_value())
return { CanvasImageSourceUsability::Bad };
// If image has an intrinsic width or intrinsic height (or both) equal to zero, then return bad.
if (image_element->current_image_frame()->width() == 0 || image_element->current_image_frame()->height() == 0)
if (current_image_frame->width() == 0 || current_image_frame->height() == 0)
return { CanvasImageSourceUsability::Bad };
return Optional<CanvasImageSourceUsability> {};
},
@ -929,11 +930,12 @@ 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->current_image_frame())
auto current_image_frame = image_element->current_image_frame();
if (!current_image_frame.has_value())
return { CanvasImageSourceUsability::Bad };
// If image has an intrinsic width or intrinsic height (or both) equal to zero, then return bad.
if (image_element->current_image_frame()->width() == 0 || image_element->current_image_frame()->height() == 0)
if (current_image_frame->width() == 0 || current_image_frame->height() == 0)
return { CanvasImageSourceUsability::Bad };
return Optional<CanvasImageSourceUsability> {};
},

View file

@ -6,7 +6,9 @@
#pragma once
#include <AK/Optional.h>
#include <AK/RefCounted.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/ScalingMode.h>
#include <LibGfx/Size.h>
#include <LibJS/Heap/Cell.h>
@ -25,7 +27,7 @@ public:
virtual Optional<Gfx::IntRect> frame_rect([[maybe_unused]] size_t frame_index) const = 0;
virtual void paint([[maybe_unused]] DisplayListRecordingContext&, [[maybe_unused]] size_t frame_index, [[maybe_unused]] Gfx::IntRect dst_rect, [[maybe_unused]] Gfx::IntRect clip_rect, [[maybe_unused]] Gfx::ScalingMode scaling_mode) const = 0;
virtual RefPtr<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const = 0;
virtual Optional<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize = {}) const = 0;
virtual int frame_duration(size_t frame_index) const = 0;
virtual size_t frame_count() const = 0;

View file

@ -464,7 +464,7 @@ void HTMLCanvasElement::present()
if (auto surface = this->surface()) {
surface->flush();
auto snapshot = Gfx::DecodedImageFrame::create(*surface->snapshot_bitmap());
auto snapshot = Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
ensure_external_content_source().update(snapshot);
}
}

View file

@ -241,11 +241,11 @@ void HTMLImageElement::adjust_computed_style(CSS::ComputedProperties& style)
style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::None)));
}
RefPtr<Gfx::DecodedImageFrame> HTMLImageElement::default_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> HTMLImageElement::default_image_frame_sized(Gfx::IntSize size) const
{
if (auto data = m_current_request->image_data())
return data->frame(0, size);
return nullptr;
return {};
}
bool HTMLImageElement::is_image_available() const
@ -274,11 +274,11 @@ Optional<CSSPixelFraction> HTMLImageElement::intrinsic_aspect_ratio() const
return {};
}
RefPtr<Gfx::DecodedImageFrame> HTMLImageElement::current_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> HTMLImageElement::current_image_frame_sized(Gfx::IntSize size) const
{
if (auto data = m_current_request->image_data())
return data->frame(m_current_frame_index, size);
return nullptr;
return {};
}
void HTMLImageElement::set_visible_in_viewport(bool)
@ -303,7 +303,7 @@ WebIDL::UnsignedLong HTMLImageElement::width() const
// ...or else the density-corrected intrinsic width and height of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available but not being rendered.
if (auto bitmap = current_image_frame())
if (auto bitmap = current_image_frame(); bitmap.has_value())
return bitmap->width();
// ...or else 0, if the image is not available or does not have intrinsic dimensions.
@ -334,7 +334,7 @@ WebIDL::UnsignedLong HTMLImageElement::height() const
// ...or else the density-corrected intrinsic height and height of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available but not being rendered.
if (auto bitmap = current_image_frame())
if (auto bitmap = current_image_frame(); bitmap.has_value())
return bitmap->height();
// ...or else 0, if the image is not available or does not have intrinsic dimensions.
@ -353,7 +353,7 @@ unsigned HTMLImageElement::natural_width() const
{
// Return the density-corrected intrinsic width of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available.
if (auto bitmap = current_image_frame())
if (auto bitmap = current_image_frame(); bitmap.has_value())
return bitmap->width();
// ...or else 0.
@ -365,7 +365,7 @@ unsigned HTMLImageElement::natural_height() const
{
// Return the density-corrected intrinsic height of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available.
if (auto bitmap = current_image_frame())
if (auto bitmap = current_image_frame(); bitmap.has_value())
return bitmap->height();
// ...or else 0.

View file

@ -49,7 +49,7 @@ public:
String alt() const { return get_attribute_value(HTML::AttributeNames::alt); }
virtual RefPtr<Gfx::DecodedImageFrame> default_image_frame_sized(Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> default_image_frame_sized(Gfx::IntSize) const override;
WebIDL::UnsignedLong width() const;
void set_width(WebIDL::UnsignedLong);
@ -111,7 +111,7 @@ public:
virtual Optional<CSSPixels> intrinsic_width() const override;
virtual Optional<CSSPixels> intrinsic_height() const override;
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const override;
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual void set_visible_in_viewport(bool) override;
virtual GC::Ptr<DOM::Element const> to_html_element() const override { return *this; }
virtual GC::Ptr<DecodedImageData> decoded_image_data() const override;

View file

@ -2289,11 +2289,11 @@ Optional<CSSPixelFraction> HTMLInputElement::intrinsic_aspect_ratio() const
return {};
}
RefPtr<Gfx::DecodedImageFrame> HTMLInputElement::current_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> HTMLInputElement::current_image_frame_sized(Gfx::IntSize size) const
{
if (auto image_data = this->image_data())
return image_data->frame(0, size);
return nullptr;
return {};
}
void HTMLInputElement::set_visible_in_viewport(bool)
@ -2386,7 +2386,7 @@ WebIDL::UnsignedLong HTMLInputElement::height() const
}
// ...or else the natural height and height of the image, in CSS pixels, if an image is available but not being rendered
if (auto bitmap = current_image_frame())
if (auto bitmap = current_image_frame(); bitmap.has_value())
return bitmap->height();
// ...or else 0, if the image is not available or does not have intrinsic dimensions.
@ -2421,7 +2421,7 @@ WebIDL::UnsignedLong HTMLInputElement::width() const
}
// ...or else the natural width and height of the image, in CSS pixels, if an image is available but not being rendered
if (auto bitmap = current_image_frame())
if (auto bitmap = current_image_frame(); bitmap.has_value())
return bitmap->width();
// ...or else 0, if the image is not available or does not have intrinsic dimensions.

View file

@ -291,7 +291,7 @@ private:
virtual Optional<CSSPixels> intrinsic_width() const override;
virtual Optional<CSSPixels> intrinsic_height() const override;
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const override;
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual void set_visible_in_viewport(bool) override;
virtual GC::Ptr<DOM::Element const> to_html_element() const override { return *this; }
virtual size_t current_frame_index() const override { return 0; }

View file

@ -906,7 +906,7 @@ static NonnullRefPtr<Core::Promise<bool>> decode_favicon(ReadonlyBytes favicon_d
// FIXME: Calculate size based on device pixel ratio
Gfx::IntSize size { 32, 32 };
auto decoded_frame = result.release_value()->frame(0, size);
if (!decoded_frame) {
if (!decoded_frame.has_value()) {
promise->reject(Error::from_string_view("Failed to get bitmap from SVG favicon"sv));
return promise;
}

View file

@ -606,11 +606,11 @@ Optional<CSSPixelFraction> HTMLObjectElement::intrinsic_aspect_ratio() const
return {};
}
RefPtr<Gfx::DecodedImageFrame> HTMLObjectElement::current_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> HTMLObjectElement::current_image_frame_sized(Gfx::IntSize size) const
{
if (auto image_data = this->image_data())
return image_data->frame(0, size);
return nullptr;
return {};
}
void HTMLObjectElement::set_visible_in_viewport(bool)

View file

@ -80,7 +80,7 @@ private:
virtual Optional<CSSPixels> intrinsic_width() const override;
virtual Optional<CSSPixels> intrinsic_height() const override;
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const override;
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual void set_visible_in_viewport(bool) override;
virtual GC::Ptr<DOM::Element const> to_html_element() const override { return *this; }
virtual size_t current_frame_index() const override { return 0; }

View file

@ -339,21 +339,21 @@ HTMLVideoElement::Representation HTMLVideoElement::current_representation() cons
return Representation::VideoFrame;
}
RefPtr<Gfx::DecodedImageFrame> HTMLVideoElement::current_decoded_image_frame() const
Optional<Gfx::DecodedImageFrame> HTMLVideoElement::current_decoded_image_frame() const
{
auto const& sink = selected_video_track_sink();
if (sink == nullptr)
return nullptr;
return {};
auto current_frame = sink->current_frame();
if (!current_frame)
return nullptr;
return {};
auto bitmap_or_error = current_frame->yuv_data().to_bitmap();
if (bitmap_or_error.is_error()) {
dbgln("Could not convert video frame to bitmap: {}", bitmap_or_error.release_error());
return nullptr;
return {};
}
auto bitmap = bitmap_or_error.release_value();
return Gfx::DecodedImageFrame::create(NonnullRefPtr<Gfx::Bitmap const> { *bitmap }, current_frame->color_space());
return Gfx::DecodedImageFrame { NonnullRefPtr<Gfx::Bitmap const> { *bitmap }, current_frame->color_space() };
}
}

View file

@ -8,6 +8,7 @@
#pragma once
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
#include <LibWeb/Forward.h>
@ -59,7 +60,7 @@ public:
};
Representation current_representation() const;
RefPtr<Gfx::DecodedImageFrame> current_decoded_image_frame() const;
Optional<Gfx::DecodedImageFrame> current_decoded_image_frame() const;
private:
HTMLVideoElement(DOM::Document&, DOM::QualifiedName);

View file

@ -296,7 +296,7 @@ public:
});
},
[this](RenderingThread::PublishToExternalContent const& mode) {
auto snapshot = Gfx::DecodedImageFrame::create(*m_backing_stores.front_store->snapshot_bitmap());
auto snapshot = Gfx::DecodedImageFrame { *m_backing_stores.front_store->snapshot_bitmap() };
mode.source->update(move(snapshot));
});
}

View file

@ -187,7 +187,7 @@ void SharedResourceRequest::handle_successful_fetch(URL::URL const& url_string,
Vector<BitmapDecodedImageData::Frame> frames;
for (auto& frame : result.frames) {
frames.append(BitmapDecodedImageData::Frame {
.frame = Gfx::DecodedImageFrame::create(*frame.bitmap, result.color_space),
.frame = Gfx::DecodedImageFrame { *frame.bitmap, result.color_space },
.duration = static_cast<int>(frame.duration),
});
}

View file

@ -469,7 +469,7 @@ GC::Ref<WebIDL::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitmap_imp
// 2. If image's media data has no natural dimensions (e.g., it's a vector graphic with no specified content size), it should be rendered to a bitmap of the size specified by the resizeWidth and the resizeHeight options.
// 3. Set imageBitmap's bitmap data to a copy of image's media data, cropped to the source rectangle with formatting. If this is an animated image, imageBitmap's bitmap data must only be taken from the default image of the animation (the one that the format defines is to be used when animation is not supported or is disabled), or, if there is no such image, the first frame of the animation.
RefPtr<Gfx::DecodedImageFrame> decoded_frame;
Optional<Gfx::DecodedImageFrame> decoded_frame;
if (has_natural_dimensions) {
decoded_frame = image_element->default_image_frame_sized(Gfx::IntSize { *image_element->intrinsic_width(), *image_element->intrinsic_height() });
} else {

View file

@ -25,17 +25,17 @@ Optional<CSSPixelSize> ImageProvider::intrinsic_size() const
return CSSPixelSize { *width, *height };
}
RefPtr<Gfx::DecodedImageFrame> ImageProvider::current_image_frame() const
Optional<Gfx::DecodedImageFrame> ImageProvider::current_image_frame() const
{
return current_image_frame_sized(intrinsic_size().value_or({}).to_type<int>());
}
RefPtr<Gfx::DecodedImageFrame> ImageProvider::default_image_frame() const
Optional<Gfx::DecodedImageFrame> ImageProvider::default_image_frame() const
{
return default_image_frame_sized(intrinsic_size().value_or({}).to_type<int>());
}
RefPtr<Gfx::DecodedImageFrame> ImageProvider::default_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> ImageProvider::default_image_frame_sized(Gfx::IntSize size) const
{
// Defer to the current image by default.
return current_image_frame_sized(size);

View file

@ -6,7 +6,9 @@
#pragma once
#include <AK/Optional.h>
#include <LibGC/Cell.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Size.h>
#include <LibWeb/Forward.h>
#include <LibWeb/PixelUnits.h>
@ -28,11 +30,11 @@ public:
Optional<CSSPixelSize> intrinsic_size() const;
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const = 0;
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame() const;
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const = 0;
virtual Optional<Gfx::DecodedImageFrame> current_image_frame() const;
virtual Optional<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const = 0;
virtual RefPtr<Gfx::DecodedImageFrame> default_image_frame() const;
virtual RefPtr<Gfx::DecodedImageFrame> default_image_frame_sized(Gfx::IntSize) const;
virtual Optional<Gfx::DecodedImageFrame> default_image_frame() const;
virtual Optional<Gfx::DecodedImageFrame> default_image_frame_sized(Gfx::IntSize) const;
virtual void set_visible_in_viewport(bool) = 0;

View file

@ -216,7 +216,7 @@ public:
virtual Optional<CSSPixels> intrinsic_height() const override { return m_image->natural_height(); }
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const override { return m_image->natural_aspect_ratio(); }
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize size) const override
virtual Optional<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize size) const override
{
auto rect = DevicePixelRect { DevicePixelPoint {}, size.to_type<DevicePixels>() };
return m_image->current_frame(rect);

View file

@ -951,7 +951,7 @@ void EventHandler::maybe_show_context_menu(GC::Ref<DOM::Node> node, MouseEventCo
auto image_url = image_element.document().encoding_parse_url(image_element.current_src());
if (image_url.has_value()) {
Optional<Gfx::Bitmap const*> bitmap;
if (auto frame = image_element.current_image_frame())
if (auto frame = image_element.current_image_frame(); frame.has_value())
bitmap = &frame->bitmap();
m_navigable->page().client().page_did_request_image_context_menu(top_level_viewport_position, *image_url, "", modifiers, bitmap);

View file

@ -306,7 +306,7 @@ void paint_background(DisplayListRecordingContext& context, PaintableBox const&
dest_rect.set_height(1);
auto frame = static_cast<CSS::ImageStyleValue const&>(image).current_frame(dest_rect);
if (!frame)
if (!frame.has_value())
return;
auto scaling_mode = to_gfx_scaling_mode(image_rendering, frame->size(), dest_rect.size().to_type<int>());
context.display_list_recorder().draw_repeated_decoded_image_frame(dest_rect.to_type<int>(), clip_rect.to_type<int>(), *frame, scaling_mode, repeat_x, repeat_y);

View file

@ -64,7 +64,7 @@ struct DrawScaledDecodedImageFrame {
Gfx::IntRect dst_rect;
Gfx::IntRect clip_rect;
NonnullRefPtr<Gfx::DecodedImageFrame const> frame;
Gfx::DecodedImageFrame frame;
Gfx::ScalingMode scaling_mode;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return clip_rect; }
@ -81,7 +81,7 @@ struct DrawRepeatedDecodedImageFrame {
Gfx::IntRect dst_rect;
Gfx::IntRect clip_rect;
NonnullRefPtr<Gfx::DecodedImageFrame const> frame;
Gfx::DecodedImageFrame frame;
Gfx::ScalingMode scaling_mode;
Repeat repeat;

View file

@ -154,7 +154,7 @@ void DisplayListPlayerSkia::fill_rect(FillRect const& command)
void DisplayListPlayerSkia::draw_external_content(DrawExternalContent const& command)
{
auto frame = command.source->current_frame();
if (!frame)
if (!frame.has_value())
return;
auto image = m_image_cache.image_for_frame(*frame);
if (!image)
@ -212,7 +212,7 @@ void DisplayListPlayerSkia::draw_video_frame_source(DrawVideoFrameSource const&
void DisplayListPlayerSkia::draw_scaled_decoded_image_frame(DrawScaledDecodedImageFrame const& command)
{
auto image = m_image_cache.image_for_frame(*command.frame);
auto image = m_image_cache.image_for_frame(command.frame);
if (!image)
return;
@ -229,13 +229,13 @@ void DisplayListPlayerSkia::draw_scaled_decoded_image_frame(DrawScaledDecodedIma
void DisplayListPlayerSkia::draw_repeated_decoded_image_frame(DrawRepeatedDecodedImageFrame const& command)
{
auto image = m_image_cache.image_for_frame(*command.frame);
auto image = m_image_cache.image_for_frame(command.frame);
if (!image)
return;
SkMatrix matrix;
auto dst_rect = command.dst_rect.to_type<float>();
auto src_size = command.frame->size().to_type<float>();
auto src_size = command.frame.size().to_type<float>();
matrix.setScale(dst_rect.width() / src_size.width(), dst_rect.height() / src_size.height());
matrix.postTranslate(dst_rect.x(), dst_rect.y());
auto sampling_options = to_skia_sampling_options(command.scaling_mode);

View file

@ -246,19 +246,19 @@ void DisplayListRecorder::draw_video_frame_source(Gfx::IntRect const& dst_rect,
APPEND(DrawVideoFrameSource { .dst_rect = dst_rect, .source = move(source), .scaling_mode = scaling_mode });
}
void DisplayListRecorder::draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::IntRect const& clip_rect, Gfx::DecodedImageFrame const& frame, Gfx::ScalingMode scaling_mode)
void DisplayListRecorder::draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::IntRect const& clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode)
{
if (dst_rect.is_empty())
return;
APPEND(DrawScaledDecodedImageFrame {
.dst_rect = dst_rect,
.clip_rect = clip_rect,
.frame = frame,
.frame = move(frame),
.scaling_mode = scaling_mode,
});
}
void DisplayListRecorder::draw_repeated_decoded_image_frame(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr<Gfx::DecodedImageFrame const> frame, Gfx::ScalingMode scaling_mode, bool repeat_x, bool repeat_y)
void DisplayListRecorder::draw_repeated_decoded_image_frame(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode, bool repeat_x, bool repeat_y)
{
APPEND(DrawRepeatedDecodedImageFrame {
.dst_rect = dst_rect,

View file

@ -71,11 +71,11 @@ public:
void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false);
void draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::IntRect const& clip_rect, Gfx::DecodedImageFrame const& frame, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::IntRect const& clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_external_content(Gfx::IntRect const& dst_rect, NonnullRefPtr<ExternalContentSource>, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_video_frame_source(Gfx::IntRect const& dst_rect, NonnullRefPtr<VideoFrameSource>, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_repeated_decoded_image_frame(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr<Gfx::DecodedImageFrame const> frame, Gfx::ScalingMode scaling_mode, bool repeat_x, bool repeat_y);
void draw_repeated_decoded_image_frame(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode, bool repeat_x, bool repeat_y);
void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::LineStyle style = Gfx::LineStyle::Solid, Color alternate_color = Color::Transparent);

View file

@ -14,9 +14,9 @@ NonnullRefPtr<ExternalContentSource> ExternalContentSource::create()
return adopt_ref(*new ExternalContentSource());
}
void ExternalContentSource::update(RefPtr<Gfx::DecodedImageFrame> frame)
void ExternalContentSource::update(Optional<Gfx::DecodedImageFrame> frame)
{
RefPtr<Gfx::DecodedImageFrame> old;
Optional<Gfx::DecodedImageFrame> old;
{
Threading::MutexLocker const locker { m_mutex };
old = move(m_frame);
@ -26,14 +26,14 @@ void ExternalContentSource::update(RefPtr<Gfx::DecodedImageFrame> frame)
void ExternalContentSource::clear()
{
RefPtr<Gfx::DecodedImageFrame> old;
Optional<Gfx::DecodedImageFrame> old;
{
Threading::MutexLocker const locker { m_mutex };
old = move(m_frame);
}
}
RefPtr<Gfx::DecodedImageFrame> ExternalContentSource::current_frame() const
Optional<Gfx::DecodedImageFrame> ExternalContentSource::current_frame() const
{
Threading::MutexLocker const locker { m_mutex };
return m_frame;

View file

@ -7,7 +7,8 @@
#pragma once
#include <AK/AtomicRefCounted.h>
#include <AK/RefPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <LibThreading/Mutex.h>
@ -18,15 +19,15 @@ class ExternalContentSource final : public AtomicRefCounted<ExternalContentSourc
public:
static NonnullRefPtr<ExternalContentSource> create();
void update(RefPtr<Gfx::DecodedImageFrame>);
void update(Optional<Gfx::DecodedImageFrame>);
void clear();
RefPtr<Gfx::DecodedImageFrame> current_frame() const;
Optional<Gfx::DecodedImageFrame> current_frame() const;
private:
ExternalContentSource() = default;
mutable Threading::Mutex m_mutex;
RefPtr<Gfx::DecodedImageFrame> m_frame;
Optional<Gfx::DecodedImageFrame> m_frame;
};
}

View file

@ -52,12 +52,12 @@ void VideoPaintable::paint(DisplayListRecordingContext& context, PaintPhase phas
auto const& poster_frame = video_element.poster_frame();
auto paint_bitmap = [&](auto const& bitmap) {
auto frame = Gfx::DecodedImageFrame::create(bitmap);
auto frame = Gfx::DecodedImageFrame { bitmap };
auto dst_rect = get_replaced_box_painting_area(*this, context, computed_values().object_fit(), bitmap.size());
if (dst_rect.is_empty())
return;
auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), frame->size(), dst_rect.size());
context.display_list_recorder().draw_scaled_decoded_image_frame(dst_rect, dst_rect, *frame, scaling_mode);
auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), frame.size(), dst_rect.size());
context.display_list_recorder().draw_scaled_decoded_image_frame(dst_rect, dst_rect, move(frame), scaling_mode);
};
auto paint_video_frame = [&]() {

View file

@ -125,7 +125,7 @@ size_t SVGDecodedImageData::external_memory_size() const
size_t size = Base::external_memory_size();
size = JS::saturating_add_external_memory_size(size, JS::hash_map_external_memory_size(m_cached_rendered_frames));
for (auto const& cached_frame : m_cached_rendered_frames)
size = JS::saturating_add_external_memory_size(size, cached_frame.value->bitmap().data_size());
size = JS::saturating_add_external_memory_size(size, cached_frame.value.bitmap().data_size());
size = JS::saturating_add_external_memory_size(size, JS::hash_map_external_memory_size(m_cached_rendered_surfaces));
for (auto const& cached_surface : m_cached_rendered_surfaces)
@ -146,7 +146,7 @@ RefPtr<Gfx::PaintingSurface> SVGDecodedImageData::render_to_surface(Gfx::IntSize
VERIFY(m_document->navigable());
if (size.is_empty())
return nullptr;
return {};
if (auto it = m_cached_rendered_surfaces.find(size); it != m_cached_rendered_surfaces.end())
return it->value;
@ -176,10 +176,10 @@ RefPtr<Gfx::PaintingSurface> SVGDecodedImageData::render_to_surface(Gfx::IntSize
return surface;
}
RefPtr<Gfx::DecodedImageFrame> SVGDecodedImageData::frame(size_t, Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> SVGDecodedImageData::frame(size_t, Gfx::IntSize size) const
{
if (size.is_empty())
return nullptr;
return {};
if (auto it = m_cached_rendered_frames.find(size); it != m_cached_rendered_frames.end())
return it->value;
@ -189,7 +189,7 @@ RefPtr<Gfx::DecodedImageFrame> SVGDecodedImageData::frame(size_t, Gfx::IntSize s
if (m_cached_rendered_frames.size() > 10)
m_cached_rendered_frames.remove(m_cached_rendered_frames.begin());
auto decoded_frame = Gfx::DecodedImageFrame::create(*render_to_surface(size)->snapshot_bitmap());
auto decoded_frame = Gfx::DecodedImageFrame { *render_to_surface(size)->snapshot_bitmap() };
m_cached_rendered_frames.set(size, decoded_frame);
return decoded_frame;
}

View file

@ -21,7 +21,7 @@ public:
static ErrorOr<GC::Ref<SVGDecodedImageData>> create(JS::Realm&, GC::Ref<Page>, URL::URL const&, ReadonlyBytes encoded_svg);
virtual ~SVGDecodedImageData() override;
virtual RefPtr<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> frame(size_t frame_index, Gfx::IntSize) const override;
virtual Optional<CSSPixels> intrinsic_width() const override;
virtual Optional<CSSPixels> intrinsic_height() const override;
@ -49,7 +49,7 @@ private:
RefPtr<Painting::DisplayList> record_display_list(Gfx::IntSize) const;
// FIXME: Remove this once everything is using surfaces instead.
mutable HashMap<Gfx::IntSize, NonnullRefPtr<Gfx::DecodedImageFrame>> m_cached_rendered_frames;
mutable HashMap<Gfx::IntSize, Gfx::DecodedImageFrame> m_cached_rendered_frames;
mutable HashMap<Gfx::IntSize, NonnullRefPtr<Gfx::PaintingSurface>> m_cached_rendered_surfaces;

View file

@ -87,7 +87,7 @@ GC::Ptr<HTML::DecodedImageData> SVGFEImageElement::image_data() const
return m_resource_request->image_data();
}
RefPtr<Gfx::DecodedImageFrame> SVGFEImageElement::current_image_frame(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> SVGFEImageElement::current_image_frame(Gfx::IntSize size) const
{
if (auto data = image_data())
return data->frame(0, size);
@ -97,7 +97,7 @@ RefPtr<Gfx::DecodedImageFrame> SVGFEImageElement::current_image_frame(Gfx::IntSi
Optional<Gfx::IntRect> SVGFEImageElement::content_rect() const
{
auto bitmap = current_image_frame();
if (!bitmap)
if (!bitmap.has_value())
return {};
// NB: Called during painting.
auto layout_node = this->unsafe_layout_node();

View file

@ -6,6 +6,8 @@
#pragma once
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibWeb/Forward.h>
#include <LibWeb/SVG/SVGElement.h>
#include <LibWeb/SVG/SVGFilterPrimitiveStandardAttributes.h>
@ -24,7 +26,7 @@ public:
virtual ~SVGFEImageElement() override = default;
GC::Ptr<HTML::DecodedImageData> image_data() const;
RefPtr<Gfx::DecodedImageFrame> current_image_frame(Gfx::IntSize = {}) const;
Optional<Gfx::DecodedImageFrame> current_image_frame(Gfx::IntSize = {}) const;
Optional<Gfx::IntRect> content_rect() const;
private:

View file

@ -266,7 +266,7 @@ Optional<Gfx::Filter> SVGFilterElement::gfx_filter(Layout::NodeWithStyle const&
return IterationDecision::Continue;
auto frame = image_data->frame(0, {});
if (!frame)
if (!frame.has_value())
return IterationDecision::Continue;
auto src_rect = image_primitive->content_rect();

View file

@ -243,7 +243,7 @@ Optional<CSSPixelFraction> SVGImageElement::intrinsic_aspect_ratio() const
return {};
}
RefPtr<Gfx::DecodedImageFrame> SVGImageElement::default_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> SVGImageElement::default_image_frame_sized(Gfx::IntSize size) const
{
if (!m_resource_request)
return {};
@ -252,7 +252,7 @@ RefPtr<Gfx::DecodedImageFrame> SVGImageElement::default_image_frame_sized(Gfx::I
return {};
}
RefPtr<Gfx::DecodedImageFrame> SVGImageElement::current_image_frame_sized(Gfx::IntSize size) const
Optional<Gfx::DecodedImageFrame> SVGImageElement::current_image_frame_sized(Gfx::IntSize size) const
{
if (!m_resource_request)
return {};

View file

@ -33,14 +33,14 @@ public:
Gfx::FloatRect bounding_box() const;
virtual RefPtr<Gfx::DecodedImageFrame> default_image_frame_sized(Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> default_image_frame_sized(Gfx::IntSize) const override;
// ^Layout::ImageProvider
virtual bool is_image_available() const override;
virtual Optional<CSSPixels> intrinsic_width() const override;
virtual Optional<CSSPixels> intrinsic_height() const override;
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const override;
virtual RefPtr<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual Optional<Gfx::DecodedImageFrame> current_image_frame_sized(Gfx::IntSize) const override;
virtual void set_visible_in_viewport(bool) override { }
virtual GC::Ptr<DOM::Element const> to_html_element() const override { return *this; }
virtual size_t current_frame_index() const override { return m_current_frame_index; }

View file

@ -34,7 +34,7 @@ NamedViewTransitionPseudoElement::NamedViewTransitionPseudoElement(CSS::PseudoEl
{
}
ReplacedNamedViewTransitionPseudoElement::ReplacedNamedViewTransitionPseudoElement(CSS::PseudoElement type, FlyString view_transition_name, RefPtr<Gfx::DecodedImageFrame> content = {})
ReplacedNamedViewTransitionPseudoElement::ReplacedNamedViewTransitionPseudoElement(CSS::PseudoElement type, FlyString view_transition_name, Optional<Gfx::DecodedImageFrame> content = {})
: NamedViewTransitionPseudoElement(type, view_transition_name)
{
m_content = content;
@ -437,7 +437,7 @@ void ViewTransition::setup_transition_pseudo_elements()
group->append_child(image_pair);
// 5. If capturedElements old image is not null, then:
if (captured_element->old_image) {
if (captured_element->old_image.has_value()) {
// 1. Let old be a new '::view-transition-old()', with its view transition name set to transitionName,
// displaying capturedElements old image as its replaced content.
auto old = heap().allocate<ReplacedNamedViewTransitionPseudoElement>(CSS::PseudoElement::ViewTransitionOld, transition_name, captured_element->old_image);
@ -457,7 +457,7 @@ void ViewTransition::setup_transition_pseudo_elements()
}
// 7. If capturedElements old image is null, then:
if (!captured_element->old_image) {
if (!captured_element->old_image.has_value()) {
// 1. Assert: capturedElements new element is not null.
VERIFY(captured_element->new_element);
@ -480,7 +480,7 @@ void ViewTransition::setup_transition_pseudo_elements()
// 8. If capturedElements new element is null, then:
if (!captured_element->new_element) {
// 1. Assert: capturedElements old image is not null.
VERIFY(captured_element->old_image);
VERIFY(captured_element->old_image.has_value());
// 2. Set capturedElements image animation name rule to a new CSSStyleRule representing the
// following CSS, and append it to documents dynamic view transition style sheet:
@ -499,7 +499,7 @@ void ViewTransition::setup_transition_pseudo_elements()
}
// 9. If both of capturedElements old image and new element are not null, then:
if (captured_element->old_image && captured_element->new_element) {
if (captured_element->old_image.has_value() && captured_element->new_element) {
// 1. Let transform be capturedElements old transform.
auto& transform = captured_element->old_transform;
// FIXME: Remove this once tranform gets used in step 5 below.

View file

@ -7,6 +7,8 @@
#pragma once
#include <AK/HashMap.h>
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Bindings/ViewTransition.h>
@ -41,9 +43,9 @@ class ReplacedNamedViewTransitionPseudoElement
GC_CELL(ReplacedNamedViewTransitionPseudoElement, NamedViewTransitionPseudoElement);
GC_DECLARE_ALLOCATOR(ReplacedNamedViewTransitionPseudoElement);
ReplacedNamedViewTransitionPseudoElement(CSS::PseudoElement, FlyString, RefPtr<Gfx::DecodedImageFrame>);
ReplacedNamedViewTransitionPseudoElement(CSS::PseudoElement, FlyString, Optional<Gfx::DecodedImageFrame>);
RefPtr<Gfx::DecodedImageFrame> m_content;
Optional<Gfx::DecodedImageFrame> m_content;
};
// https://drafts.csswg.org/css-view-transitions-1/#captured-element
@ -51,7 +53,7 @@ struct CapturedElement : public JS::Cell {
GC_CELL(CapturedElement, JS::Cell)
GC_DECLARE_ALLOCATOR(CapturedElement);
RefPtr<Gfx::DecodedImageFrame> old_image {};
Optional<Gfx::DecodedImageFrame> old_image {};
CSSPixels old_width = 0;
CSSPixels old_height = 0;
// FIXME: Make this an identity transform function by default.

View file

@ -266,28 +266,28 @@ Optional<Gfx::BitmapExportResult> WebGLRenderingContextBase::read_and_pixel_conv
// a SECURITY_ERR exception must be thrown. See Origin Restrictions.
// FIXME: If source is null then an INVALID_VALUE error is generated.
auto frame = source.visit(
[](GC::Root<HTML::HTMLImageElement> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
[](GC::Root<HTML::HTMLImageElement> const& source) -> Optional<Gfx::DecodedImageFrame> {
return source->current_image_frame();
},
[](GC::Root<HTML::HTMLCanvasElement> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
[](GC::Root<HTML::HTMLCanvasElement> const& source) -> Optional<Gfx::DecodedImageFrame> {
auto surface = source->surface();
if (!surface)
return Gfx::DecodedImageFrame::create(*source->get_bitmap_from_surface());
return Gfx::DecodedImageFrame::create(*surface->snapshot_bitmap());
return Gfx::DecodedImageFrame { *source->get_bitmap_from_surface() };
return Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
},
[](GC::Root<HTML::OffscreenCanvas> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame::create(*source->bitmap());
[](GC::Root<HTML::OffscreenCanvas> const& source) -> Optional<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame { *source->bitmap() };
},
[](GC::Root<HTML::HTMLVideoElement> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
[](GC::Root<HTML::HTMLVideoElement> const& source) -> Optional<Gfx::DecodedImageFrame> {
return source->current_decoded_image_frame();
},
[](GC::Root<HTML::ImageBitmap> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame::create(*source->bitmap());
[](GC::Root<HTML::ImageBitmap> const& source) -> Optional<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame { *source->bitmap() };
},
[](GC::Root<HTML::ImageData> const& source) -> RefPtr<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame::create(source->bitmap());
[](GC::Root<HTML::ImageData> const& source) -> Optional<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame { source->bitmap() };
});
if (!frame)
if (!frame.has_value())
return OptionalNone {};
auto export_format = determine_export_format(format, type);

View file

@ -77,7 +77,7 @@ void WebViewImplementationNative::paint_into_bitmap(void* android_bitmap_raw, An
auto android_bitmap = MUST(Gfx::Bitmap::create_wrapper(to_gfx_bitmap_format(info.format), Gfx::AlphaType::Premultiplied, { info.width, info.height }, info.stride, android_bitmap_raw));
auto painter = Gfx::Painter::create(android_bitmap);
if (auto* bitmap = m_client_state.has_usable_bitmap ? m_client_state.front_bitmap.bitmap.ptr() : m_backup_bitmap.ptr())
painter->draw_bitmap(android_bitmap->rect().to_type<float>(), *Gfx::DecodedImageFrame::create(*MUST(bitmap->clone())), bitmap->rect(), Gfx::ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::Copy);
painter->draw_bitmap(android_bitmap->rect().to_type<float>(), Gfx::DecodedImageFrame { *MUST(bitmap->clone()) }, bitmap->rect(), Gfx::ScalingMode::NearestNeighbor, {}, 1.0f, Gfx::CompositingAndBlendingOperator::Copy);
else
painter->fill_rect(android_bitmap->rect().to_type<float>(), Gfx::Color::Magenta);
}