Explorar o código

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.
Aliaksandr Kalenik hai 1 ano
pai
achega
c28f6828c9

+ 30 - 10
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<Gfx::AffineTransform> affine_transform)
 {
-    blit_canvas(dst_rect.to_type<float>(), canvas, opacity);
+    blit_canvas(dst_rect.to_type<float>(), 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<Gfx::AffineTransform> 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<Gfx::AffineTransform> 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<Gfx::FloatPoint, 4> 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<float>(m_target_canvas->size().width());
+    auto const viewport_height = static_cast<float>(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<GLfloat> 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);

+ 3 - 3
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<Gfx::AffineTransform> affine_transform = {});
+    void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional<Gfx::AffineTransform> affine_transform = {});
 
     void update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>&);
 
@@ -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<Gfx::AffineTransform> affine_transform = {});
     void bind_target_canvas();
 
     [[nodiscard]] Gfx::FloatRect to_clip_space(Gfx::FloatRect const& screen_rect) const;

+ 18 - 12
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<float>());
         painter->clear(Color::Transparent);
-
-        auto source_rect = source_paintable_rect.to_type<float>().translated(-transform.origin);
-        auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform.origin);
-        auto destination_rect = transformed_destination_rect.to_rounded<int>();
-
         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<float>());
+        painter().translate(stacking_context_transform.translation() + post_transform_translation.to_type<float>());
         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--;

+ 1 - 0
Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h

@@ -67,6 +67,7 @@ private:
         MaybeOwned<AccelGfx::Painter> painter;
         float opacity;
         Gfx::IntRect destination;
+        Gfx::AffineTransform transform;
         int stacking_context_depth { 0 };
     };