diff --git a/Libraries/LibGfx/CanvasCommandList.cpp b/Libraries/LibGfx/CanvasCommandList.cpp index 03a0199784..bdbdb329db 100644 --- a/Libraries/LibGfx/CanvasCommandList.cpp +++ b/Libraries/LibGfx/CanvasCommandList.cpp @@ -207,6 +207,33 @@ ErrorOr decode(Decoder& decoder) }; } +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::DrawCanvas const& command) +{ + TRY(encoder.encode(command.source_canvas_id)); + TRY(encoder.encode(command.dst_rect)); + TRY(encoder.encode(command.src_rect)); + TRY(encoder.encode(command.scaling_mode)); + TRY(encoder.encode(command.filter)); + TRY(encoder.encode(command.global_alpha)); + TRY(encoder.encode(command.compositing_and_blending_operator)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::DrawCanvas { + .source_canvas_id = TRY(decoder.decode()), + .dst_rect = TRY(decoder.decode()), + .src_rect = TRY(decoder.decode()), + .scaling_mode = TRY(decoder.decode()), + .filter = TRY(decoder.decode>()), + .global_alpha = TRY(decoder.decode()), + .compositing_and_blending_operator = TRY(decoder.decode()), + }; +} + template<> ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::FillPath const& command) { diff --git a/Libraries/LibGfx/CanvasCommandList.h b/Libraries/LibGfx/CanvasCommandList.h index 85518e0a45..68bf875a19 100644 --- a/Libraries/LibGfx/CanvasCommandList.h +++ b/Libraries/LibGfx/CanvasCommandList.h @@ -82,6 +82,16 @@ struct DrawBitmap { CompositingAndBlendingOperator compositing_and_blending_operator { CompositingAndBlendingOperator::SourceOver }; }; +struct DrawCanvas { + u64 source_canvas_id { 0 }; + FloatRect dst_rect; + IntRect src_rect; + ScalingMode scaling_mode { ScalingMode::NearestNeighbor }; + Optional filter; + float global_alpha { 1 }; + CompositingAndBlendingOperator compositing_and_blending_operator { CompositingAndBlendingOperator::SourceOver }; +}; + struct FillPath { Path path; CanvasPaintStyle style; @@ -128,6 +138,7 @@ using CanvasCommand = Variant< CanvasCommands::ClearRect, CanvasCommands::FillRect, CanvasCommands::DrawBitmap, + CanvasCommands::DrawCanvas, CanvasCommands::FillPath, CanvasCommands::StrokePath, CanvasCommands::SetTransform, @@ -196,6 +207,11 @@ ErrorOr encode(Encoder&, Gfx::CanvasCommands::DrawBitmap const&); template<> ErrorOr decode(Decoder&); +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::DrawCanvas const&); +template<> +ErrorOr decode(Decoder&); + template<> ErrorOr encode(Encoder&, Gfx::CanvasCommands::FillPath const&); template<> diff --git a/Libraries/LibGfx/CanvasCommandPlayer.cpp b/Libraries/LibGfx/CanvasCommandPlayer.cpp index e75da2a8f7..e980fbf636 100644 --- a/Libraries/LibGfx/CanvasCommandPlayer.cpp +++ b/Libraries/LibGfx/CanvasCommandPlayer.cpp @@ -10,12 +10,16 @@ #include #include #include +#include +#include +#include namespace Gfx { -CanvasCommandPlayer::CanvasCommandPlayer(RefPtr skia_backend_context, IntSize size, BitmapFormat format, AlphaType alpha_type) +CanvasCommandPlayer::CanvasCommandPlayer(RefPtr skia_backend_context, IntSize size, BitmapFormat format, AlphaType alpha_type, CanvasSurfaceResolver canvas_surface_resolver) : m_surface(PaintingSurface::create_with_size(size, format, alpha_type, move(skia_backend_context))) , m_painter(make(*m_surface)) + , m_canvas_surface_resolver(move(canvas_surface_resolver)) { } @@ -52,6 +56,34 @@ void CanvasCommandPlayer::play_command(CanvasCommands::DrawBitmap const& command m_painter->draw_bitmap(command.dst_rect, command.frame, command.src_rect, command.scaling_mode, command.filter, command.global_alpha, command.compositing_and_blending_operator); } +void CanvasCommandPlayer::play_command(CanvasCommands::DrawCanvas const& command) +{ + if (!m_canvas_surface_resolver) + return; + + auto* source_surface = m_canvas_surface_resolver(command.source_canvas_id); + if (!source_surface) + return; + + auto image = source_surface->sk_image_snapshot>(); + if (!image) + return; + + SkPaint paint; + if (command.filter.has_value()) + paint.setImageFilter(to_skia_image_filter(command.filter.value())); + paint.setAlpha(static_cast(command.global_alpha * 255)); + paint.setBlender(to_skia_blender(command.compositing_and_blending_operator)); + + m_surface->canvas().drawImageRect( + image.get(), + to_skia_rect(command.src_rect), + to_skia_rect(command.dst_rect), + to_skia_sampling_options(command.scaling_mode), + &paint, + SkCanvas::kStrict_SrcRectConstraint); +} + void CanvasCommandPlayer::play_command(CanvasCommands::FillPath const& command) { // Shadows are recorded as blurred solid-color fills; everything else goes through the general paint-style overload. diff --git a/Libraries/LibGfx/CanvasCommandPlayer.h b/Libraries/LibGfx/CanvasCommandPlayer.h index a5d5c2aa24..eaee0c1b6e 100644 --- a/Libraries/LibGfx/CanvasCommandPlayer.h +++ b/Libraries/LibGfx/CanvasCommandPlayer.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -20,7 +21,9 @@ class CanvasCommandPlayer { AK_MAKE_NONMOVABLE(CanvasCommandPlayer); public: - CanvasCommandPlayer(RefPtr, IntSize, BitmapFormat, AlphaType); + using CanvasSurfaceResolver = Function; + + CanvasCommandPlayer(RefPtr, IntSize, BitmapFormat, AlphaType, CanvasSurfaceResolver = {}); ~CanvasCommandPlayer(); NonnullRefPtr surface() const; @@ -33,6 +36,7 @@ private: void play_command(CanvasCommands::ClearRect const&); void play_command(CanvasCommands::FillRect const&); void play_command(CanvasCommands::DrawBitmap const&); + void play_command(CanvasCommands::DrawCanvas const&); void play_command(CanvasCommands::FillPath const&); void play_command(CanvasCommands::StrokePath const&); void play_command(CanvasCommands::SetTransform const&); @@ -45,6 +49,7 @@ private: NonnullRefPtr m_surface; NonnullOwnPtr m_painter; + CanvasSurfaceResolver m_canvas_surface_resolver; }; } diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 608c53f724..489f1bf3e6 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -175,10 +175,7 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm if (usability == CanvasImageSourceUsability::Bad) return {}; - auto frame = canvas_image_source_frame(image); - if (!frame.has_value()) - return {}; - auto source_bitmap_rect = frame->rect(); + auto source_bitmap_rect = Gfx::IntRect { {}, canvas_image_source_dimensions(image) }; // 4. Establish the source and destination rectangles as follows: // If not specified, the dw and dh arguments must default to the values of sw and sh, interpreted such that one CSS pixel in the image is treated as one unit in the output bitmap's coordinate space. @@ -230,6 +227,34 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm } if (auto* canvas_command_list = this->canvas_command_list()) { + if (auto const* source_canvas = image.get_pointer>()) { + (*source_canvas)->ensure_backing_storage(); + (*source_canvas)->prepare_for_compositing(); + if (auto source_canvas_id = (*source_canvas)->canvas_id(); source_canvas_id.has_value()) { + canvas_command_list->append(Gfx::CanvasCommands::DrawCanvas { + .source_canvas_id = source_canvas_id->value(), + .dst_rect = destination_rect, + .src_rect = source_rect.to_rounded(), + .scaling_mode = scaling_mode, + .filter = drawing_state().filter, + .global_alpha = drawing_state().global_alpha, + .compositing_and_blending_operator = drawing_state().current_compositing_and_blending_operator, + }); + did_draw(destination_rect); + flush_recorded_commands(); + + // 7. If image is not origin-clean, then set the CanvasRenderingContext2D's origin-clean flag to false. + if (image_is_not_origin_clean(image)) + m_origin_clean = false; + + return {}; + } + } + + auto frame = canvas_image_source_frame(image); + if (!frame.has_value()) + return {}; + canvas_command_list->append(Gfx::CanvasCommands::DrawBitmap { .frame = *frame, .dst_rect = destination_rect, diff --git a/Services/Compositor/CanvasHost.cpp b/Services/Compositor/CanvasHost.cpp index b1822733a0..d945f034d8 100644 --- a/Services/Compositor/CanvasHost.cpp +++ b/Services/Compositor/CanvasHost.cpp @@ -32,7 +32,9 @@ OwnPtr CanvasHost::create_2d_command_player(Gfx::IntSi return nullptr; auto format = alpha ? Gfx::BitmapFormat::BGRA8888 : Gfx::BitmapFormat::BGRx8888; - auto player = make(m_skia_backend_context, size, format, Gfx::AlphaType::Premultiplied); + auto player = make(m_skia_backend_context, size, format, Gfx::AlphaType::Premultiplied, [this](u64 canvas_id) -> Gfx::PaintingSurface const* { + return m_canvas_surface_registry.canvas_surface(Web::Painting::CanvasId { canvas_id }); + }); // https://html.spec.whatwg.org/multipage/canvas.html#the-canvas-settings:concept-canvas-alpha // "Thus, the bitmap of such a context starts off as opaque black instead of transparent black"