mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Add API to allow DecodedImageData to paint itself directly
Instead of painting DecodedImageData by first asking it for a bitmap
and then painting that, this commit adds two new APIs:
- frame_rect(frame_index):
Gets the size of the animation frame at the given index.
- paint(context, ...):
Paints the DecodedImageData into a DisplayListRecordingContext.
The main powerful thing here is that this allows SVGDecodedImageData
to render itself using the GPU when available.
This commit is contained in:
parent
cebd4cc10d
commit
4c2a02370d
Notes:
github-actions[bot]
2025-11-05 08:13:04 +00:00
Author: https://github.com/awesomekling
Commit: 4c2a02370d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6699
Reviewed-by: https://github.com/kalenikaliaksandr
6 changed files with 97 additions and 5 deletions
|
|
@ -8,6 +8,8 @@
|
|||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/HTML/AnimatedBitmapDecodedImageData.h>
|
||||
#include <LibWeb/Painting/DisplayListRecorder.h>
|
||||
#include <LibWeb/Painting/DisplayListRecordingContext.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
|
@ -56,4 +58,14 @@ Optional<CSSPixelFraction> AnimatedBitmapDecodedImageData::intrinsic_aspect_rati
|
|||
return CSSPixels(m_frames.first().bitmap->width()) / CSSPixels(m_frames.first().bitmap->height());
|
||||
}
|
||||
|
||||
Optional<Gfx::IntRect> AnimatedBitmapDecodedImageData::frame_rect(size_t frame_index) const
|
||||
{
|
||||
return m_frames[frame_index].bitmap->rect();
|
||||
}
|
||||
|
||||
void AnimatedBitmapDecodedImageData::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_immutable_bitmap(dst_rect, clip_rect, *m_frames[frame_index].bitmap, scaling_mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ public:
|
|||
virtual Optional<CSSPixels> intrinsic_height() const override;
|
||||
virtual Optional<CSSPixelFraction> intrinsic_aspect_ratio() const override;
|
||||
|
||||
virtual Optional<Gfx::IntRect> frame_rect(size_t frame_index) const override;
|
||||
virtual void paint(DisplayListRecordingContext&, size_t frame_index, Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::ScalingMode scaling_mode) const override;
|
||||
|
||||
private:
|
||||
AnimatedBitmapDecodedImageData(Vector<Frame>&&, size_t loop_count, bool animated);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023-2025, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
|
@ -7,8 +7,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/PixelUnits.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
|
@ -20,6 +22,9 @@ class DecodedImageData : public JS::Cell {
|
|||
public:
|
||||
virtual ~DecodedImageData();
|
||||
|
||||
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::ImmutableBitmap> bitmap(size_t frame_index, Gfx::IntSize = {}) const = 0;
|
||||
virtual int frame_duration(size_t frame_index) const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -143,16 +143,18 @@ Navigable::Navigable(GC::Ref<Page> page, bool is_svg_page)
|
|||
{
|
||||
all_navigables().set(*this);
|
||||
|
||||
auto display_list_player_type = page->client().display_list_player_type();
|
||||
if (display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable) {
|
||||
m_skia_backend_context = get_skia_backend_context();
|
||||
}
|
||||
|
||||
if (!m_is_svg_page) {
|
||||
auto display_list_player_type = page->client().display_list_player_type();
|
||||
OwnPtr<Painting::DisplayListPlayerSkia> skia_player;
|
||||
if (display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable) {
|
||||
m_skia_backend_context = get_skia_backend_context();
|
||||
skia_player = make<Painting::DisplayListPlayerSkia>(m_skia_backend_context);
|
||||
} else {
|
||||
skia_player = make<Painting::DisplayListPlayerSkia>();
|
||||
}
|
||||
|
||||
m_rendering_thread.set_skia_player(move(skia_player));
|
||||
m_rendering_thread.start(display_list_player_type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
#include <LibWeb/Page/Page.h>
|
||||
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
|
||||
#include <LibWeb/Painting/DisplayListRecordingContext.h>
|
||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||
#include <LibWeb/SVG/SVGDecodedImageData.h>
|
||||
#include <LibWeb/SVG/SVGSVGElement.h>
|
||||
#include <LibWeb/XML/XMLDocumentBuilder.h>
|
||||
|
|
@ -120,6 +119,33 @@ RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
|
|||
return bitmap;
|
||||
}
|
||||
|
||||
RefPtr<Gfx::PaintingSurface> SVGDecodedImageData::render_to_surface(Gfx::IntSize size) const
|
||||
{
|
||||
VERIFY(m_document->navigable());
|
||||
|
||||
auto surface = Gfx::PaintingSurface::create_with_size(m_document->navigable()->skia_backend_context(), size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
|
||||
|
||||
m_document->navigable()->set_viewport_size(size.to_type<CSSPixels>());
|
||||
m_document->update_layout(DOM::UpdateLayoutReason::SVGDecodedImageDataRender);
|
||||
|
||||
auto display_list = m_document->record_display_list({});
|
||||
if (!display_list)
|
||||
return nullptr;
|
||||
|
||||
switch (m_page_client->display_list_player_type()) {
|
||||
case DisplayListPlayerType::SkiaGPUIfAvailable:
|
||||
case DisplayListPlayerType::SkiaCPU: {
|
||||
Painting::DisplayListPlayerSkia display_list_player;
|
||||
display_list_player.execute(*display_list, {}, surface);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
RefPtr<Gfx::ImmutableBitmap> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize size) const
|
||||
{
|
||||
if (size.is_empty())
|
||||
|
|
@ -192,4 +218,39 @@ void SVGDecodedImageData::SVGPageClient::visit_edges(Visitor& visitor)
|
|||
visitor.visit(m_svg_page);
|
||||
}
|
||||
|
||||
Optional<Gfx::IntRect> SVGDecodedImageData::frame_rect(size_t) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
RefPtr<Gfx::PaintingSurface> SVGDecodedImageData::surface(size_t, Gfx::IntSize size) const
|
||||
{
|
||||
if (size.is_empty())
|
||||
return nullptr;
|
||||
|
||||
if (auto it = m_cached_rendered_surfaces.find(size); it != m_cached_rendered_surfaces.end())
|
||||
return it->value;
|
||||
|
||||
// Prevent the cache from growing too big.
|
||||
// FIXME: Evict least used entries.
|
||||
if (m_cached_rendered_surfaces.size() > 10)
|
||||
m_cached_rendered_surfaces.remove(m_cached_rendered_surfaces.begin());
|
||||
|
||||
auto surface = render_to_surface(size);
|
||||
if (!surface)
|
||||
return nullptr;
|
||||
m_cached_rendered_surfaces.set(size, *surface);
|
||||
return surface;
|
||||
}
|
||||
|
||||
void SVGDecodedImageData::paint(DisplayListRecordingContext& context, size_t, Gfx::IntRect dst_rect, Gfx::IntRect, Gfx::ScalingMode scaling_mode) const
|
||||
{
|
||||
auto surface = this->surface(0, dst_rect.size());
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
Gfx::IntRect src_rect(0, 0, dst_rect.width(), dst_rect.height());
|
||||
context.display_list_recorder().draw_painting_surface(dst_rect, *surface, src_rect, scaling_mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,22 @@ public:
|
|||
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||
|
||||
virtual Optional<Gfx::IntRect> frame_rect(size_t frame_index) const override;
|
||||
virtual void paint(DisplayListRecordingContext&, size_t frame_index, Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::ScalingMode scaling_mode) const override;
|
||||
|
||||
private:
|
||||
SVGDecodedImageData(GC::Ref<Page>, GC::Ref<SVGPageClient>, GC::Ref<DOM::Document>, GC::Ref<SVG::SVGSVGElement>);
|
||||
|
||||
RefPtr<Gfx::Bitmap> render(Gfx::IntSize) const;
|
||||
|
||||
RefPtr<Gfx::PaintingSurface> surface(size_t frame_index, Gfx::IntSize) const;
|
||||
RefPtr<Gfx::PaintingSurface> render_to_surface(Gfx::IntSize) const;
|
||||
|
||||
// FIXME: Remove this once everything is using surfaces instead.
|
||||
mutable HashMap<Gfx::IntSize, NonnullRefPtr<Gfx::ImmutableBitmap>> m_cached_rendered_bitmaps;
|
||||
|
||||
mutable HashMap<Gfx::IntSize, NonnullRefPtr<Gfx::PaintingSurface>> m_cached_rendered_surfaces;
|
||||
|
||||
GC::Ref<Page> m_page;
|
||||
GC::Ref<SVGPageClient> m_page_client;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue