mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibGfx+LibWeb: Reuse DisplayListPlayer and PaintingSurface when possible
Previously, we were reinstantiating the DisplayListPlayer and PaintingSurface on every paint.
This commit is contained in:
parent
a4639b3d8e
commit
342cb7addf
Notes:
github-actions[bot]
2025-01-31 12:29:12 +00:00
Author: https://github.com/gmta
Commit: 342cb7addf
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3400
Reviewed-by: https://github.com/kalenikaliaksandr
12 changed files with 107 additions and 104 deletions
|
@ -24,7 +24,6 @@ struct PaintingSurface::Impl {
|
|||
IntSize size;
|
||||
sk_sp<SkSurface> surface;
|
||||
RefPtr<Bitmap> bitmap;
|
||||
RefPtr<SkiaBackendContext> context;
|
||||
};
|
||||
|
||||
NonnullRefPtr<PaintingSurface> PaintingSurface::create_with_size(RefPtr<SkiaBackendContext> context, IntSize size, BitmapFormat color_type, AlphaType alpha_type)
|
||||
|
@ -37,12 +36,12 @@ NonnullRefPtr<PaintingSurface> PaintingSurface::create_with_size(RefPtr<SkiaBack
|
|||
auto bitmap = Bitmap::create(color_type, alpha_type, size).value();
|
||||
auto surface = SkSurfaces::WrapPixels(image_info, bitmap->begin(), bitmap->pitch());
|
||||
VERIFY(surface);
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, bitmap, context)));
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, bitmap)));
|
||||
}
|
||||
|
||||
auto surface = SkSurfaces::RenderTarget(context->sk_context(), skgpu::Budgeted::kNo, image_info);
|
||||
VERIFY(surface);
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, nullptr, context)));
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, nullptr)));
|
||||
}
|
||||
|
||||
NonnullRefPtr<PaintingSurface> PaintingSurface::wrap_bitmap(Bitmap& bitmap)
|
||||
|
@ -52,7 +51,7 @@ NonnullRefPtr<PaintingSurface> PaintingSurface::wrap_bitmap(Bitmap& bitmap)
|
|||
auto size = bitmap.size();
|
||||
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), color_type, alpha_type, SkColorSpace::MakeSRGB());
|
||||
auto surface = SkSurfaces::WrapPixels(image_info, bitmap.begin(), bitmap.pitch());
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, bitmap, nullptr)));
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, bitmap)));
|
||||
}
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
|
@ -76,7 +75,7 @@ NonnullRefPtr<PaintingSurface> PaintingSurface::wrap_iosurface(Core::IOSurfaceHa
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
auto surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), backend_render_target, sk_origin, kBGRA_8888_SkColorType, nullptr, nullptr);
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, nullptr, context)));
|
||||
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, nullptr)));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -136,11 +135,10 @@ sk_sp<SkImage> PaintingSurface::sk_image_snapshot() const
|
|||
return m_impl->surface->makeImageSnapshot();
|
||||
}
|
||||
|
||||
void PaintingSurface::flush() const
|
||||
void PaintingSurface::flush()
|
||||
{
|
||||
if (auto context = m_impl->context) {
|
||||
context->flush_and_submit(m_impl->surface.get());
|
||||
}
|
||||
if (on_flush)
|
||||
on_flush(*this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
|
@ -29,6 +30,8 @@ public:
|
|||
BottomLeft,
|
||||
};
|
||||
|
||||
Function<void(PaintingSurface&)> on_flush;
|
||||
|
||||
static NonnullRefPtr<PaintingSurface> create_with_size(RefPtr<SkiaBackendContext> context, IntSize size, BitmapFormat color_type, AlphaType alpha_type);
|
||||
static NonnullRefPtr<PaintingSurface> wrap_bitmap(Bitmap&);
|
||||
|
||||
|
@ -50,7 +53,7 @@ public:
|
|||
template<typename T>
|
||||
T sk_image_snapshot() const;
|
||||
|
||||
void flush() const;
|
||||
void flush();
|
||||
|
||||
~PaintingSurface();
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/SkiaBackendContext.h>
|
||||
|
||||
#include <core/SkSurface.h>
|
||||
|
|
|
@ -170,7 +170,7 @@ public:
|
|||
|
||||
CSSPixelPoint viewport_scroll_offset() const { return m_viewport_scroll_offset; }
|
||||
CSSPixelRect viewport_rect() const { return { m_viewport_scroll_offset, m_size }; }
|
||||
void set_viewport_size(CSSPixelSize);
|
||||
virtual void set_viewport_size(CSSPixelSize);
|
||||
void perform_scroll_of_viewport(CSSPixelPoint position);
|
||||
|
||||
void set_needs_display(InvalidateDisplayList = InvalidateDisplayList::Yes);
|
||||
|
|
|
@ -53,8 +53,12 @@ TraversableNavigable::TraversableNavigable(GC::Ref<Page> page)
|
|||
, m_session_history_traversal_queue(vm().heap().allocate<SessionHistoryTraversalQueue>())
|
||||
{
|
||||
auto display_list_player_type = page->client().display_list_player_type();
|
||||
if (display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable)
|
||||
if (display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable) {
|
||||
m_skia_backend_context = get_skia_backend_context();
|
||||
m_skia_player = make<Painting::DisplayListPlayerSkia>(m_skia_backend_context);
|
||||
} else {
|
||||
m_skia_player = make<Painting::DisplayListPlayerSkia>();
|
||||
}
|
||||
}
|
||||
|
||||
TraversableNavigable::~TraversableNavigable() = default;
|
||||
|
@ -1384,6 +1388,45 @@ GC::Ptr<DOM::Node> TraversableNavigable::currently_focused_area()
|
|||
return candidate;
|
||||
}
|
||||
|
||||
void TraversableNavigable::set_viewport_size(CSSPixelSize size)
|
||||
{
|
||||
Navigable::set_viewport_size(size);
|
||||
|
||||
// Invalidate the surface cache if the traversable changed size.
|
||||
m_bitmap_to_surface.clear();
|
||||
}
|
||||
|
||||
NonnullRefPtr<Gfx::PaintingSurface> TraversableNavigable::painting_surface_for_backing_store(Painting::BackingStore& backing_store)
|
||||
{
|
||||
auto& bitmap = backing_store.bitmap();
|
||||
auto cached_surface = m_bitmap_to_surface.find(&bitmap);
|
||||
if (cached_surface != m_bitmap_to_surface.end())
|
||||
return cached_surface->value;
|
||||
|
||||
RefPtr<Gfx::PaintingSurface> new_surface;
|
||||
if (page().client().display_list_player_type() == DisplayListPlayerType::SkiaGPUIfAvailable && m_skia_backend_context) {
|
||||
#ifdef USE_VULKAN
|
||||
// Vulkan: Try to create an accelerated surface.
|
||||
new_surface = Gfx::PaintingSurface::create_with_size(m_skia_backend_context, backing_store.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
|
||||
new_surface->on_flush = [&bitmap = bitmap](auto& surface) { surface.read_into_bitmap(bitmap); };
|
||||
#endif
|
||||
#ifdef AK_OS_MACOS
|
||||
// macOS: Wrap an IOSurface if available.
|
||||
if (is<Painting::IOSurfaceBackingStore>(backing_store)) {
|
||||
auto& iosurface_backing_store = static_cast<Painting::IOSurfaceBackingStore&>(backing_store);
|
||||
new_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface_backing_store.iosurface_handle(), *m_skia_backend_context);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// CPU and fallback: wrap the backing store bitmap directly.
|
||||
if (!new_surface)
|
||||
new_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
|
||||
|
||||
m_bitmap_to_surface.set(&bitmap, *new_surface);
|
||||
return *new_surface;
|
||||
}
|
||||
|
||||
void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::BackingStore& target, PaintOptions paint_options)
|
||||
{
|
||||
auto document = active_document();
|
||||
|
@ -1391,10 +1434,9 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::
|
|||
return;
|
||||
|
||||
for (auto& navigable : all_navigables()) {
|
||||
if (auto active_document = navigable->active_document(); active_document && active_document->paintable()) {
|
||||
if (auto active_document = navigable->active_document(); active_document && active_document->paintable())
|
||||
active_document->paintable()->refresh_scroll_state();
|
||||
}
|
||||
}
|
||||
|
||||
DOM::Document::PaintConfig paint_config;
|
||||
paint_config.paint_overlay = paint_options.paint_overlay == PaintOptions::PaintOverlay::Yes;
|
||||
|
@ -1402,43 +1444,12 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::
|
|||
paint_config.has_focus = paint_options.has_focus;
|
||||
paint_config.canvas_fill_rect = Gfx::IntRect { {}, content_rect.size() };
|
||||
auto display_list = document->record_display_list(paint_config);
|
||||
if (!display_list) {
|
||||
if (!display_list)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (page().client().display_list_player_type()) {
|
||||
case DisplayListPlayerType::SkiaGPUIfAvailable: {
|
||||
#ifdef USE_VULKAN
|
||||
if (m_skia_backend_context) {
|
||||
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, target.bitmap());
|
||||
player.execute(*display_list);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
if (m_skia_backend_context && is<Painting::IOSurfaceBackingStore>(target)) {
|
||||
auto& iosurface_backing_store = static_cast<Painting::IOSurfaceBackingStore&>(target);
|
||||
auto painting_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface_backing_store.iosurface_handle(), *m_skia_backend_context);
|
||||
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, painting_surface);
|
||||
player.execute(*display_list);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fallback to CPU backend if GPU is not available
|
||||
Painting::DisplayListPlayerSkia player(target.bitmap());
|
||||
player.execute(*display_list);
|
||||
break;
|
||||
}
|
||||
case DisplayListPlayerType::SkiaCPU: {
|
||||
Painting::DisplayListPlayerSkia player(target.bitmap());
|
||||
player.execute(*display_list);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
auto painting_surface = painting_surface_for_backing_store(target);
|
||||
m_skia_player->set_surface(painting_surface);
|
||||
m_skia_player->execute(*display_list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,15 +14,16 @@
|
|||
#include <LibWeb/Page/Page.h>
|
||||
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
|
||||
#include <LibWeb/StorageAPI/StorageShed.h>
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
# include <LibGfx/VulkanContext.h>
|
||||
#endif
|
||||
#include <WebContent/BackingStoreManager.h>
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
# include <LibGfx/MetalContext.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
# include <LibGfx/VulkanContext.h>
|
||||
#endif
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#traversable-navigable
|
||||
|
@ -110,6 +111,8 @@ public:
|
|||
StorageAPI::StorageShed& storage_shed() { return m_storage_shed; }
|
||||
StorageAPI::StorageShed const& storage_shed() const { return m_storage_shed; }
|
||||
|
||||
void set_viewport_size(CSSPixelSize) override;
|
||||
|
||||
private:
|
||||
TraversableNavigable(GC::Ref<Page>);
|
||||
|
||||
|
@ -131,6 +134,8 @@ private:
|
|||
|
||||
[[nodiscard]] bool can_go_forward() const;
|
||||
|
||||
NonnullRefPtr<Gfx::PaintingSurface> painting_surface_for_backing_store(Painting::BackingStore&);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-current-session-history-step
|
||||
int m_current_session_history_step { 0 };
|
||||
|
||||
|
@ -154,6 +159,8 @@ private:
|
|||
String m_window_handle;
|
||||
|
||||
RefPtr<Gfx::SkiaBackendContext> m_skia_backend_context;
|
||||
OwnPtr<Painting::DisplayListPlayerSkia> m_skia_player;
|
||||
HashMap<Gfx::Bitmap*, NonnullRefPtr<Gfx::PaintingSurface>> m_bitmap_to_surface;
|
||||
};
|
||||
|
||||
struct BrowsingContextAndDocument {
|
||||
|
|
|
@ -41,6 +41,8 @@ void DisplayListPlayer::execute(DisplayList& display_list)
|
|||
auto const& scroll_state = display_list.scroll_state();
|
||||
auto device_pixels_per_css_pixel = display_list.device_pixels_per_css_pixel();
|
||||
|
||||
VERIFY(m_surface);
|
||||
|
||||
size_t next_command_index = 0;
|
||||
while (next_command_index < commands.size()) {
|
||||
auto scroll_frame_id = commands[next_command_index].scroll_frame_id;
|
||||
|
@ -128,6 +130,8 @@ void DisplayListPlayer::execute(DisplayList& display_list)
|
|||
else VERIFY_NOT_REACHED();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -25,9 +26,14 @@ class DisplayListPlayer {
|
|||
public:
|
||||
virtual ~DisplayListPlayer() = default;
|
||||
|
||||
void execute(DisplayList& display_list);
|
||||
void execute(DisplayList&);
|
||||
void set_surface(NonnullRefPtr<Gfx::PaintingSurface> surface) { m_surface = surface; }
|
||||
|
||||
protected:
|
||||
Gfx::PaintingSurface& surface() const { return *m_surface; }
|
||||
|
||||
private:
|
||||
virtual void flush() = 0;
|
||||
virtual void draw_glyph_run(DrawGlyphRun const&) = 0;
|
||||
virtual void fill_rect(FillRect const&) = 0;
|
||||
virtual void draw_painting_surface(DrawPaintingSurface const&) = 0;
|
||||
|
@ -65,6 +71,8 @@ private:
|
|||
virtual void apply_transform(ApplyTransform const&) = 0;
|
||||
virtual void apply_mask_bitmap(ApplyMaskBitmap const&) = 0;
|
||||
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
|
||||
|
||||
RefPtr<Gfx::PaintingSurface> m_surface;
|
||||
};
|
||||
|
||||
class DisplayList : public RefCounted<DisplayList> {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -29,36 +30,13 @@
|
|||
|
||||
namespace Web::Painting {
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, Gfx::Bitmap& bitmap)
|
||||
DisplayListPlayerSkia::DisplayListPlayerSkia(RefPtr<Gfx::SkiaBackendContext> context)
|
||||
: m_context(context)
|
||||
{
|
||||
m_surface = Gfx::PaintingSurface::create_with_size(m_context, bitmap.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
|
||||
m_flush_context = [&bitmap, surface = m_surface] mutable {
|
||||
surface->read_into_bitmap(bitmap);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, NonnullRefPtr<Gfx::PaintingSurface> surface)
|
||||
: m_context(context)
|
||||
, m_surface(move(surface))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::Bitmap& bitmap)
|
||||
{
|
||||
m_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
|
||||
}
|
||||
|
||||
DisplayListPlayerSkia::~DisplayListPlayerSkia()
|
||||
DisplayListPlayerSkia::DisplayListPlayerSkia()
|
||||
{
|
||||
m_surface->flush();
|
||||
if (m_flush_context) {
|
||||
m_flush_context();
|
||||
}
|
||||
}
|
||||
|
||||
static SkRRect to_skia_rrect(auto const& rect, CornerRadii const& corner_radii)
|
||||
|
@ -88,9 +66,11 @@ static SkMatrix to_skia_matrix(Gfx::AffineTransform const& affine_transform)
|
|||
return matrix;
|
||||
}
|
||||
|
||||
Gfx::PaintingSurface& DisplayListPlayerSkia::surface() const
|
||||
void DisplayListPlayerSkia::flush()
|
||||
{
|
||||
return *m_surface;
|
||||
if (m_context)
|
||||
m_context->flush_and_submit(&surface().sk_surface());
|
||||
surface().flush();
|
||||
}
|
||||
|
||||
void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command)
|
||||
|
@ -850,10 +830,10 @@ void DisplayListPlayerSkia::add_mask(AddMask const& command)
|
|||
|
||||
auto mask_surface = Gfx::PaintingSurface::create_with_size(m_context, rect.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
|
||||
|
||||
auto previous_surface = move(m_surface);
|
||||
m_surface = mask_surface;
|
||||
NonnullRefPtr old_surface = surface();
|
||||
set_surface(mask_surface);
|
||||
execute(*command.display_list);
|
||||
m_surface = move(previous_surface);
|
||||
set_surface(old_surface);
|
||||
|
||||
SkMatrix mask_matrix;
|
||||
mask_matrix.setTranslate(rect.x(), rect.y());
|
||||
|
|
|
@ -14,21 +14,13 @@ class GrDirectContext;
|
|||
|
||||
namespace Web::Painting {
|
||||
|
||||
class DisplayListPlayerSkia : public DisplayListPlayer {
|
||||
class DisplayListPlayerSkia final : public DisplayListPlayer {
|
||||
public:
|
||||
DisplayListPlayerSkia(Gfx::Bitmap&);
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
DisplayListPlayerSkia(Gfx::SkiaBackendContext&, Gfx::Bitmap&);
|
||||
#endif
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
DisplayListPlayerSkia(Gfx::SkiaBackendContext&, NonnullRefPtr<Gfx::PaintingSurface>);
|
||||
#endif
|
||||
|
||||
virtual ~DisplayListPlayerSkia() override;
|
||||
DisplayListPlayerSkia(RefPtr<Gfx::SkiaBackendContext>);
|
||||
DisplayListPlayerSkia();
|
||||
|
||||
private:
|
||||
void flush() override;
|
||||
void draw_glyph_run(DrawGlyphRun const&) override;
|
||||
void fill_rect(FillRect const&) override;
|
||||
void draw_painting_surface(DrawPaintingSurface const&) override;
|
||||
|
@ -68,12 +60,7 @@ private:
|
|||
|
||||
bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;
|
||||
|
||||
Gfx::PaintingSurface& surface() const;
|
||||
|
||||
RefPtr<Gfx::SkiaBackendContext> m_context {};
|
||||
RefPtr<Gfx::PaintingSurface> m_surface {};
|
||||
|
||||
Function<void()> m_flush_context;
|
||||
RefPtr<Gfx::SkiaBackendContext> m_context;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -90,7 +90,9 @@ RefPtr<Gfx::ImmutableBitmap> SVGMaskable::calculate_mask_of_svg(PaintContext& co
|
|||
paint_context.set_svg_transform(graphics_element.get_transform());
|
||||
paint_context.set_draw_svg_geometry_for_clip_path(is<SVGClipPaintable>(paintable));
|
||||
StackingContext::paint_svg(paint_context, paintable, PaintPhase::Foreground);
|
||||
DisplayListPlayerSkia display_list_player { *mask_bitmap };
|
||||
auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(*mask_bitmap);
|
||||
DisplayListPlayerSkia display_list_player;
|
||||
display_list_player.set_surface(painting_surface);
|
||||
display_list_player.execute(display_list);
|
||||
return mask_bitmap;
|
||||
};
|
||||
|
|
|
@ -103,7 +103,9 @@ RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
|
|||
switch (painting_command_executor_type) {
|
||||
case DisplayListPlayerType::SkiaGPUIfAvailable:
|
||||
case DisplayListPlayerType::SkiaCPU: {
|
||||
Painting::DisplayListPlayerSkia display_list_player { *bitmap };
|
||||
auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(*bitmap);
|
||||
Painting::DisplayListPlayerSkia display_list_player;
|
||||
display_list_player.set_surface(painting_surface);
|
||||
display_list_player.execute(*display_list);
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue