Pārlūkot izejas kodu

LibWeb: Resolve CSS transform properties during layout commit

Now, instead of resolving "transform" and "transform-origin" during the
construction of the stacking context tree, we do so during the layout
commit.

This is part of a refactoring effort to make the paintable tree
independent from the layout tree.
Aliaksandr Kalenik 1 gadu atpakaļ
vecāks
revīzija
ef0c390b79

+ 33 - 0
Userland/Libraries/LibWeb/Layout/LayoutState.cpp

@@ -390,6 +390,38 @@ void LayoutState::resolve_text_shadows(Vector<Painting::PaintableWithLines&> con
     }
 }
 
+void LayoutState::resolve_css_transform()
+{
+    for (auto& it : used_values_per_layout_node) {
+        auto& used_values = *it.value;
+        if (!used_values.node().is_box())
+            continue;
+
+        auto const& box = static_cast<Layout::Box const&>(used_values.node());
+        if (!box.paintable_box())
+            continue;
+
+        auto& paintable_box = const_cast<Painting::PaintableBox&>(*box.paintable_box());
+        auto const& transformations = box.computed_values().transformations();
+        if (transformations.is_empty())
+            continue;
+
+        auto matrix = Gfx::FloatMatrix4x4::identity();
+        for (auto const& transform : transformations)
+            matrix = matrix * transform.to_matrix(paintable_box).release_value();
+
+        paintable_box.set_transform(matrix);
+
+        auto const& style_value = box.computed_values().transform_origin();
+        // FIXME: respect transform-box property
+        auto const& reference_box = paintable_box.absolute_border_box_rect();
+        auto x = reference_box.left() + style_value.x.to_px(box, reference_box.width());
+        auto y = reference_box.top() + style_value.y.to_px(box, reference_box.height());
+
+        paintable_box.set_transform_origin({ x, y });
+    }
+}
+
 void LayoutState::commit(Box& root)
 {
     // Only the top-level LayoutState should ever be committed.
@@ -502,6 +534,7 @@ void LayoutState::commit(Box& root)
     resolve_border_radii();
     resolve_box_shadow_data();
     resolve_text_shadows(paintables_with_lines);
+    resolve_css_transform();
 }
 
 void LayoutState::UsedValues::set_node(NodeWithStyle& node, UsedValues const* containing_block_used_values)

+ 1 - 0
Userland/Libraries/LibWeb/Layout/LayoutState.h

@@ -189,6 +189,7 @@ private:
     void resolve_border_radii();
     void resolve_box_shadow_data();
     void resolve_text_shadows(Vector<Painting::PaintableWithLines&> const& paintables_with_lines);
+    void resolve_css_transform();
 };
 
 }

+ 8 - 0
Userland/Libraries/LibWeb/Painting/PaintableBox.h

@@ -185,6 +185,12 @@ public:
     void set_box_shadow_data(Vector<ShadowData> box_shadow_data) { m_box_shadow_data = move(box_shadow_data); }
     Vector<ShadowData> const& box_shadow_data() const { return m_box_shadow_data; }
 
+    void set_transform(Gfx::FloatMatrix4x4 transform) { m_transform = transform; }
+    Gfx::FloatMatrix4x4 const& transform() const { return m_transform; }
+
+    void set_transform_origin(CSSPixelPoint transform_origin) { m_transform_origin = transform_origin; }
+    CSSPixelPoint const& transform_origin() const { return m_transform_origin; }
+
 protected:
     explicit PaintableBox(Layout::Box const&);
 
@@ -219,6 +225,8 @@ private:
 
     BorderRadiiData m_border_radii_data;
     Vector<ShadowData> m_box_shadow_data;
+    Gfx::FloatMatrix4x4 m_transform { Gfx::FloatMatrix4x4::identity() };
+    CSSPixelPoint m_transform_origin;
 };
 
 class PaintableWithLines : public PaintableBox {

+ 15 - 36
Userland/Libraries/LibWeb/Painting/StackingContext.cpp

@@ -33,8 +33,6 @@ static void paint_node(Paintable const& paintable, PaintContext& context, PaintP
 
 StackingContext::StackingContext(Paintable& paintable, StackingContext* parent, size_t index_in_tree_order)
     : m_paintable(paintable)
-    , m_transform(combine_transformations(paintable.computed_values().transformations()))
-    , m_transform_origin(compute_transform_origin())
     , m_parent(parent)
     , m_index_in_tree_order(index_in_tree_order)
 {
@@ -291,28 +289,13 @@ void StackingContext::paint_internal(PaintContext& context) const
     }
 }
 
-Gfx::FloatMatrix4x4 StackingContext::combine_transformations(Vector<CSS::Transformation> const& transformations) const
-{
-    // https://drafts.csswg.org/css-transforms-1/#WD20171130 says:
-    // "No transform on non-replaced inline boxes, table-column boxes, and table-column-group boxes."
-    // and https://www.w3.org/TR/css-transforms-2/ does not say anything about what to do with inline boxes.
-
-    auto matrix = Gfx::FloatMatrix4x4::identity();
-    if (paintable().is_paintable_box()) {
-        for (auto const& transform : transformations)
-            matrix = matrix * transform.to_matrix(paintable_box()).release_value();
-
-        return matrix;
-    }
-
-    return matrix;
-}
-
 // FIXME: This extracts the affine 2D part of the full transformation matrix.
 //  Use the whole matrix when we get better transformation support in LibGfx or use LibGL for drawing the bitmap
 Gfx::AffineTransform StackingContext::affine_transform_matrix() const
 {
-    return Gfx::extract_2d_affine_transform(m_transform);
+    if (paintable().is_paintable_box())
+        return Gfx::extract_2d_affine_transform(paintable_box().transform());
+    return Gfx::AffineTransform {};
 }
 
 static Gfx::FloatMatrix4x4 matrix_with_scaled_translation(Gfx::FloatMatrix4x4 matrix, float scale)
@@ -342,14 +325,21 @@ void StackingContext::paint(PaintContext& context) const
         VERIFY_NOT_REACHED();
     }
 
+    auto transform_matrix = Gfx::FloatMatrix4x4::identity();
+    Gfx::FloatPoint transform_origin;
+    if (paintable().is_paintable_box()) {
+        transform_matrix = paintable_box().transform();
+        transform_origin = paintable_box().transform_origin().to_type<float>();
+    }
+
     RecordingPainter::PushStackingContextParams push_stacking_context_params {
         .opacity = opacity,
         .is_fixed_position = paintable().is_fixed_position(),
         .source_paintable_rect = source_paintable_rect,
         .image_rendering = paintable().computed_values().image_rendering(),
         .transform = {
-            .origin = transform_origin().scaled(to_device_pixels_scale),
-            .matrix = matrix_with_scaled_translation(transform_matrix(), to_device_pixels_scale),
+            .origin = transform_origin.scaled(to_device_pixels_scale),
+            .matrix = matrix_with_scaled_translation(transform_matrix, to_device_pixels_scale),
         },
     };
 
@@ -374,19 +364,6 @@ void StackingContext::paint(PaintContext& context) const
     context.recording_painter().pop_stacking_context();
 }
 
-Gfx::FloatPoint StackingContext::compute_transform_origin() const
-{
-    if (!paintable().is_paintable_box())
-        return {};
-
-    auto style_value = paintable().computed_values().transform_origin();
-    // FIXME: respect transform-box property
-    auto reference_box = paintable_box().absolute_border_box_rect();
-    auto x = reference_box.left() + style_value.x.to_px(paintable().layout_node(), reference_box.width());
-    auto y = reference_box.top() + style_value.y.to_px(paintable().layout_node(), reference_box.height());
-    return { x.to_float(), y.to_float() };
-}
-
 template<typename Callback>
 static TraversalDecision for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(Paintable const& paintable, Callback callback)
 {
@@ -420,7 +397,9 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
     if (!paintable().is_visible())
         return {};
 
-    auto transform_origin = this->transform_origin().to_type<CSSPixels>();
+    CSSPixelPoint transform_origin { 0, 0 };
+    if (paintable().is_paintable_box())
+        transform_origin = paintable_box().transform_origin();
     // NOTE: This CSSPixels -> Float -> CSSPixels conversion is because we can't AffineTransform::map() a CSSPixelPoint.
     Gfx::FloatPoint offset_position {
         (position.x() - transform_origin.x()).to_float(),

+ 0 - 6
Userland/Libraries/LibWeb/Painting/StackingContext.h

@@ -37,7 +37,6 @@ public:
     void paint(PaintContext&) const;
     Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
 
-    Gfx::FloatMatrix4x4 const& transform_matrix() const { return m_transform; }
     Gfx::AffineTransform affine_transform_matrix() const;
 
     void dump(int indent = 0) const;
@@ -46,17 +45,12 @@ public:
 
 private:
     JS::NonnullGCPtr<Paintable> m_paintable;
-    Gfx::FloatMatrix4x4 m_transform;
-    Gfx::FloatPoint m_transform_origin;
     StackingContext* const m_parent { nullptr };
     Vector<StackingContext*> m_children;
     size_t m_index_in_tree_order { 0 };
 
     static void paint_child(PaintContext&, StackingContext const&);
     void paint_internal(PaintContext&) const;
-    Gfx::FloatMatrix4x4 combine_transformations(Vector<CSS::Transformation> const& transformations) const;
-    Gfx::FloatPoint transform_origin() const { return m_transform_origin; }
-    Gfx::FloatPoint compute_transform_origin() const;
 };
 
 }