diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 6b1929e0d99..3d5eb3e14fb 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -53,6 +53,7 @@ set(SOURCES ImageFormats/AVIFLoader.cpp ImmutableBitmap.cpp MedianCut.cpp + PaintingSurface.cpp Palette.cpp Path.cpp PathSkia.cpp diff --git a/Userland/Libraries/LibGfx/PaintingSurface.cpp b/Userland/Libraries/LibGfx/PaintingSurface.cpp new file mode 100644 index 00000000000..3d31a1cd02c --- /dev/null +++ b/Userland/Libraries/LibGfx/PaintingSurface.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef AK_OS_MACOS +# include +# include +# include +#endif + +namespace Gfx { + +struct PaintingSurface::Impl { + IntSize size; + sk_sp surface; + RefPtr bitmap; + RefPtr context; +}; + +static SkColorType to_skia_color_type(Gfx::BitmapFormat format) +{ + switch (format) { + case Gfx::BitmapFormat::Invalid: + return kUnknown_SkColorType; + case Gfx::BitmapFormat::BGRA8888: + case Gfx::BitmapFormat::BGRx8888: + return kBGRA_8888_SkColorType; + case Gfx::BitmapFormat::RGBA8888: + return kRGBA_8888_SkColorType; + default: + return kUnknown_SkColorType; + } +} + +NonnullRefPtr PaintingSurface::create_with_size(RefPtr context, Gfx::IntSize size, Gfx::BitmapFormat color_type, Gfx::AlphaType alpha_type) +{ + auto sk_color_type = to_skia_color_type(color_type); + auto sk_alpha_type = alpha_type == Gfx::AlphaType::Premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + auto image_info = SkImageInfo::Make(size.width(), size.height(), sk_color_type, sk_alpha_type); + + if (!context) { + auto bitmap = Gfx::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(size, surface, bitmap, context))); + } + + auto surface = SkSurfaces::RenderTarget(context->sk_context(), skgpu::Budgeted::kNo, image_info); + VERIFY(surface); + return adopt_ref(*new PaintingSurface(make(size, surface, nullptr, context))); +} + +NonnullRefPtr PaintingSurface::wrap_bitmap(Bitmap& bitmap) +{ + auto color_type = to_skia_color_type(bitmap.format()); + auto alpha_type = bitmap.alpha_type() == Gfx::AlphaType::Premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + auto size = bitmap.size(); + auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), color_type, alpha_type); + auto surface = SkSurfaces::WrapPixels(image_info, bitmap.begin(), bitmap.pitch()); + return adopt_ref(*new PaintingSurface(make(size, surface, bitmap, nullptr))); +} + +#ifdef AK_OS_MACOS +NonnullRefPtr PaintingSurface::wrap_metal_surface(Core::MetalTexture& metal_texture, RefPtr context) +{ + IntSize const size { metal_texture.width(), metal_texture.height() }; + auto image_info = SkImageInfo::Make(size.width(), size.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); + GrMtlTextureInfo mtl_info; + mtl_info.fTexture = sk_ret_cfp(metal_texture.texture()); + auto backend_render_target = GrBackendRenderTargets::MakeMtl(metal_texture.width(), metal_texture.height(), mtl_info); + auto surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), backend_render_target, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr); + return adopt_ref(*new PaintingSurface(make(size, surface, nullptr, context))); +} +#endif + +PaintingSurface::PaintingSurface(NonnullOwnPtr&& impl) + : m_impl(move(impl)) +{ +} + +PaintingSurface::~PaintingSurface() = default; + +RefPtr PaintingSurface::create_snapshot() const +{ + auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size()).value(); + auto image_info = SkImageInfo::Make(bitmap->width(), bitmap->height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); + SkPixmap const pixmap(image_info, bitmap->begin(), bitmap->pitch()); + sk_surface().readPixels(pixmap, 0, 0); + return bitmap; +} + +void PaintingSurface::read_into_bitmap(Gfx::Bitmap& bitmap) +{ + auto color_type = to_skia_color_type(bitmap.format()); + auto alpha_type = bitmap.alpha_type() == Gfx::AlphaType::Premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), color_type, alpha_type); + SkPixmap const pixmap(image_info, bitmap.begin(), bitmap.pitch()); + m_impl->surface->readPixels(pixmap, 0, 0); +} + +IntSize PaintingSurface::size() const +{ + return m_impl->size; +} + +IntRect PaintingSurface::rect() const +{ + return { {}, m_impl->size }; +} + +SkCanvas& PaintingSurface::canvas() const +{ + return *m_impl->surface->getCanvas(); +} + +SkSurface& PaintingSurface::sk_surface() const +{ + return *m_impl->surface; +} + +void PaintingSurface::flush() const +{ + if (auto context = m_impl->context) { + context->flush_and_submit(m_impl->surface.get()); + } +} + +} diff --git a/Userland/Libraries/LibGfx/PaintingSurface.h b/Userland/Libraries/LibGfx/PaintingSurface.h new file mode 100644 index 00000000000..00449996132 --- /dev/null +++ b/Userland/Libraries/LibGfx/PaintingSurface.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef AK_OS_MACOS +# include +#endif + +class SkCanvas; +class SkSurface; + +namespace Gfx { + +class PaintingSurface : public RefCounted { +public: + static NonnullRefPtr create_with_size(RefPtr context, Gfx::IntSize size, Gfx::BitmapFormat color_type, Gfx::AlphaType alpha_type); + static NonnullRefPtr wrap_bitmap(Bitmap&); + +#ifdef AK_OS_MACOS + static NonnullRefPtr wrap_metal_surface(Core::MetalTexture&, RefPtr); +#endif + + RefPtr create_snapshot() const; + void read_into_bitmap(Bitmap&); + + IntSize size() const; + IntRect rect() const; + + SkCanvas& canvas() const; + SkSurface& sk_surface() const; + + void flush() const; + + ~PaintingSurface(); + +private: + struct Impl; + + PaintingSurface(NonnullOwnPtr&&); + + NonnullOwnPtr m_impl; +}; + +} diff --git a/Userland/Libraries/LibGfx/SkiaBackendContext.cpp b/Userland/Libraries/LibGfx/SkiaBackendContext.cpp index 52f5e76bff6..389e460d81c 100644 --- a/Userland/Libraries/LibGfx/SkiaBackendContext.cpp +++ b/Userland/Libraries/LibGfx/SkiaBackendContext.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #ifdef USE_VULKAN @@ -41,18 +42,13 @@ public: ~SkiaVulkanBackendContext() override { } - void flush_and_submit() override + void flush_and_submit(SkSurface* surface) override { - m_context->flush(); + GrFlushInfo const flush_info {}; + m_context->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, flush_info); m_context->submit(GrSyncCpu::kYes); } - sk_sp create_surface(int width, int height) - { - auto image_info = SkImageInfo::Make(width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType); - return SkSurfaces::RenderTarget(m_context.get(), skgpu::Budgeted::kYes, image_info); - } - skgpu::VulkanExtensions const* extensions() const { return m_extensions.ptr(); } GrDirectContext* sk_context() const override { return m_context.get(); } @@ -100,9 +96,10 @@ public: ~SkiaMetalBackendContext() override { } - void flush_and_submit() override + void flush_and_submit(SkSurface* surface) override { - m_context->flush(); + GrFlushInfo const flush_info {}; + m_context->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, flush_info); m_context->submit(GrSyncCpu::kYes); } diff --git a/Userland/Libraries/LibGfx/SkiaBackendContext.h b/Userland/Libraries/LibGfx/SkiaBackendContext.h index 33f076e842c..e6dea00fc7d 100644 --- a/Userland/Libraries/LibGfx/SkiaBackendContext.h +++ b/Userland/Libraries/LibGfx/SkiaBackendContext.h @@ -18,6 +18,7 @@ #endif class GrDirectContext; +class SkSurface; namespace Gfx { @@ -37,7 +38,7 @@ public: SkiaBackendContext() {}; virtual ~SkiaBackendContext() {}; - virtual void flush_and_submit() {}; + virtual void flush_and_submit(SkSurface*) {}; virtual GrDirectContext* sk_context() const = 0; }; diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 0a3aeee7590..bcff3db18f0 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -1402,7 +1402,8 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting:: if (m_metal_context && m_skia_backend_context && is(target)) { auto& iosurface_backing_store = static_cast(target); auto texture = m_metal_context->create_texture_from_iosurface(iosurface_backing_store.iosurface_handle()); - Painting::DisplayListPlayerSkia player(*m_skia_backend_context, *texture); + auto painting_surface = Gfx::PaintingSurface::wrap_metal_surface(*texture, m_skia_backend_context); + Painting::DisplayListPlayerSkia player(*m_skia_backend_context, painting_surface); player.execute(*display_list); return; } diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index 73e08fbf2ad..840449b3f96 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -30,92 +30,38 @@ #include #include -#ifdef USE_VULKAN -# include -# include -# include -#endif - -#ifdef AK_OS_MACOS -# include -# include -# include -# include -#endif - namespace Web::Painting { -class DisplayListPlayerSkia::SkiaSurface { -public: - SkCanvas& canvas() const { return *m_surface->getCanvas(); } - - SkiaSurface(sk_sp surface) - : m_surface(move(surface)) - { - } - - void read_into_bitmap(Gfx::Bitmap& bitmap) - { - auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); - SkPixmap pixmap(image_info, bitmap.begin(), bitmap.pitch()); - m_surface->readPixels(pixmap, 0, 0); - } - - sk_sp make_surface(int width, int height) - { - return m_surface->makeSurface(width, height); - } - -private: - sk_sp m_surface; -}; - #ifdef USE_VULKAN DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, Gfx::Bitmap& bitmap) + : m_context(context) { - VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888); - auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); - auto surface = SkSurfaces::RenderTarget(context.sk_context(), skgpu::Budgeted::kYes, image_info); - m_surface = make(surface); - m_flush_context = [&bitmap, &surface = m_surface, &context] { - context.flush_and_submit(); + 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, Core::MetalTexture& metal_texture) +DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, NonnullRefPtr surface) + : m_context(context) + , m_surface(move(surface)) { - auto image_info = SkImageInfo::Make(metal_texture.width(), metal_texture.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); - GrMtlTextureInfo mtl_info; - mtl_info.fTexture = sk_ret_cfp(metal_texture.texture()); - auto backend_render_target = GrBackendRenderTargets::MakeMtl(metal_texture.width(), metal_texture.height(), mtl_info); - auto surface = SkSurfaces::WrapBackendRenderTarget(context.sk_context(), backend_render_target, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr); - if (!surface) { - dbgln("Failed to create Skia surface from Metal texture"); - VERIFY_NOT_REACHED(); - } - m_surface = make(surface); - m_flush_context = [&context] mutable { - context.flush_and_submit(); - }; } #endif DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::Bitmap& bitmap) { - VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888); - auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); - auto surface = SkSurfaces::WrapPixels(image_info, bitmap.begin(), bitmap.pitch()); - VERIFY(surface); - m_surface = make(surface); + m_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap); } DisplayListPlayerSkia::~DisplayListPlayerSkia() { - if (m_flush_context) + m_surface->flush(); + if (m_flush_context) { m_flush_context(); + } } static SkPoint to_skia_point(auto const& point) @@ -357,9 +303,9 @@ static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode) } } -DisplayListPlayerSkia::SkiaSurface& DisplayListPlayerSkia::surface() const +Gfx::PaintingSurface& DisplayListPlayerSkia::surface() const { - return static_cast(*m_surface); + return *m_surface; } void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command) @@ -1145,16 +1091,16 @@ void DisplayListPlayerSkia::add_mask(AddMask const& command) if (rect.is_empty()) return; - auto mask_surface = m_surface->make_surface(rect.width(), rect.height()); + 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 = make(mask_surface); + m_surface = mask_surface; execute(*command.display_list); m_surface = move(previous_surface); SkMatrix mask_matrix; mask_matrix.setTranslate(rect.x(), rect.y()); - auto image = mask_surface->makeImageSnapshot(); + auto image = mask_surface->sk_surface().makeImageSnapshot(); auto shader = image->makeShader(SkSamplingOptions(), mask_matrix); surface().canvas().clipShader(shader); } diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h index 8e5488a825c..f9276928b8a 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -23,7 +24,7 @@ public: #endif #ifdef AK_OS_MACOS - DisplayListPlayerSkia(Gfx::SkiaBackendContext&, Core::MetalTexture&); + DisplayListPlayerSkia(Gfx::SkiaBackendContext&, NonnullRefPtr); #endif virtual ~DisplayListPlayerSkia() override; @@ -67,10 +68,11 @@ private: bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override; - class SkiaSurface; - SkiaSurface& surface() const; + Gfx::PaintingSurface& surface() const; + + RefPtr m_context {}; + RefPtr m_surface {}; - OwnPtr m_surface; Function m_flush_context; };