From c28f6828c9683da1d7dd504194a0ec332ac38750 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 30 Nov 2023 20:54:20 +0100 Subject: [PATCH] LibAccelGfx+LibWeb: Support non-translation transforms in GPU painter This change introduces GPU painter support for rotate(), skew(), scale(), and other transformations that could be applied to stacking context. --- Userland/Libraries/LibAccelGfx/Painter.cpp | 40 ++++++++++++++----- Userland/Libraries/LibAccelGfx/Painter.h | 6 +-- .../Painting/PaintingCommandExecutorGPU.cpp | 30 ++++++++------ .../Painting/PaintingCommandExecutorGPU.h | 1 + 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index d463787fa4..577a820291 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -586,24 +586,44 @@ void Painter::flush(Gfx::Bitmap& bitmap) GL::read_pixels({ 0, 0, bitmap.width(), bitmap.height() }, bitmap); } -void Painter::blit_canvas(Gfx::IntRect const& dst_rect, Canvas const& canvas, float opacity) +void Painter::blit_canvas(Gfx::IntRect const& dst_rect, Canvas const& canvas, float opacity, Optional affine_transform) { - blit_canvas(dst_rect.to_type(), canvas, opacity); + blit_canvas(dst_rect.to_type(), canvas, opacity, move(affine_transform)); } -void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, float opacity) +void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, float opacity, Optional affine_transform) { auto texture = GL::Texture(canvas.framebuffer().texture); - blit_scaled_texture(dst_rect, texture, { { 0, 0 }, canvas.size() }, Painter::ScalingMode::NearestNeighbor, opacity); + blit_scaled_texture(dst_rect, texture, { { 0, 0 }, canvas.size() }, Painter::ScalingMode::NearestNeighbor, opacity, move(affine_transform)); } -void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const& texture, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode, float opacity) +void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const& texture, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode, float opacity, Optional affine_transform) { bind_target_canvas(); m_blit_program.use(); - auto dst_rect_in_clip_space = to_clip_space(transform().map(dst_rect)); + auto dst_rect_rotated = dst_rect; + Array dst_rect_vertices = { + dst_rect_rotated.top_left(), + dst_rect_rotated.bottom_left(), + dst_rect_rotated.bottom_right(), + dst_rect_rotated.top_right(), + }; + + if (affine_transform.has_value()) { + for (auto& point : dst_rect_vertices) + point = affine_transform->map(point); + } + + auto const viewport_width = static_cast(m_target_canvas->size().width()); + auto const viewport_height = static_cast(m_target_canvas->size().height()); + for (auto& point : dst_rect_vertices) { + point = transform().map(point); + point.set_x(2.0f * point.x() / viewport_width - 1.0f); + point.set_y(-1.0f + 2.0f * point.y() / viewport_height); + } + auto src_rect_in_texture_space = to_texture_space(src_rect, *texture.size); Vector vertices; @@ -616,10 +636,10 @@ void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture co vertices.append(s.y()); }; - add_vertex(dst_rect_in_clip_space.top_left(), src_rect_in_texture_space.top_left()); - add_vertex(dst_rect_in_clip_space.bottom_left(), src_rect_in_texture_space.bottom_left()); - add_vertex(dst_rect_in_clip_space.bottom_right(), src_rect_in_texture_space.bottom_right()); - add_vertex(dst_rect_in_clip_space.top_right(), src_rect_in_texture_space.top_right()); + add_vertex(dst_rect_vertices[0], src_rect_in_texture_space.top_left()); + add_vertex(dst_rect_vertices[1], src_rect_in_texture_space.bottom_left()); + add_vertex(dst_rect_vertices[2], src_rect_in_texture_space.bottom_right()); + add_vertex(dst_rect_vertices[3], src_rect_in_texture_space.top_right()); auto vbo = GL::create_buffer(); GL::upload_to_buffer(vbo, vertices); diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index adf25beb2f..a2fc83b8da 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -80,8 +80,8 @@ public: void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius); void fill_rect_with_rounded_corners(Gfx::FloatRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius); - void blit_canvas(Gfx::IntRect const& dst_rect, Canvas const&, float opacity = 1.0f); - void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f); + void blit_canvas(Gfx::IntRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional affine_transform = {}); + void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional affine_transform = {}); void update_immutable_bitmap_texture_cache(HashMap&); @@ -96,7 +96,7 @@ private: [[nodiscard]] State& state() { return m_state_stack.last(); } [[nodiscard]] State const& state() const { return m_state_stack.last(); } - void blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const&, Gfx::FloatRect const& src_rect, ScalingMode, float opacity = 1.0f); + void blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const&, Gfx::FloatRect const& src_rect, ScalingMode, float opacity = 1.0f, Optional affine_transform = {}); void bind_target_canvas(); [[nodiscard]] Gfx::FloatRect to_clip_space(Gfx::FloatRect const& screen_rect) const; diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index e46ce23400..29fbd4aa4c 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -18,7 +18,8 @@ PaintingCommandExecutorGPU::PaintingCommandExecutorGPU(Gfx::Bitmap& bitmap) m_stacking_contexts.append({ .canvas = canvas, .painter = move(painter), .opacity = 1.0f, - .destination = {} }); + .destination = {}, + .transform = {} }); } PaintingCommandExecutorGPU::~PaintingCommandExecutorGPU() @@ -99,29 +100,34 @@ CommandResult PaintingCommandExecutorGPU::push_stacking_context(float opacity, b painter().translate(-translation); } - auto affine_transform = Gfx::extract_2d_affine_transform(transform.matrix); + auto stacking_context_transform = Gfx::extract_2d_affine_transform(transform.matrix); - if (opacity < 1) { + Gfx::AffineTransform inverse_origin_translation; + inverse_origin_translation.translate(-transform.origin); + Gfx::AffineTransform origin_translation; + origin_translation.translate(transform.origin); + + Gfx::AffineTransform final_transform = origin_translation; + final_transform.multiply(stacking_context_transform); + final_transform.multiply(inverse_origin_translation); + if (opacity < 1 || !stacking_context_transform.is_identity_or_translation()) { auto painter = AccelGfx::Painter::create(); auto canvas = AccelGfx::Canvas::create(source_paintable_rect.size()); painter->set_target_canvas(canvas); painter->translate(-source_paintable_rect.location().to_type()); painter->clear(Color::Transparent); - - auto source_rect = source_paintable_rect.to_type().translated(-transform.origin); - auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform.origin); - auto destination_rect = transformed_destination_rect.to_rounded(); - m_stacking_contexts.append({ .canvas = canvas, .painter = move(painter), .opacity = opacity, - .destination = destination_rect }); + .destination = source_paintable_rect, + .transform = final_transform }); } else { - painter().translate(affine_transform.translation() + post_transform_translation.to_type()); + painter().translate(stacking_context_transform.translation() + post_transform_translation.to_type()); m_stacking_contexts.append({ .canvas = {}, .painter = MaybeOwned(painter()), .opacity = opacity, - .destination = {} }); + .destination = {}, + .transform = final_transform }); } return CommandResult::Continue; } @@ -131,7 +137,7 @@ CommandResult PaintingCommandExecutorGPU::pop_stacking_context() auto stacking_context = m_stacking_contexts.take_last(); VERIFY(stacking_context.stacking_context_depth == 0); if (stacking_context.painter.is_owned()) { - painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity); + painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity, stacking_context.transform); } painter().restore(); m_stacking_contexts.last().stacking_context_depth--; diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h index 2989a92b64..3f1dd2ce4c 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h @@ -67,6 +67,7 @@ private: MaybeOwned painter; float opacity; Gfx::IntRect destination; + Gfx::AffineTransform transform; int stacking_context_depth { 0 }; };