diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp index 4ee85f7c64..a7bff841a9 100644 --- a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp +++ b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp @@ -390,6 +390,38 @@ void LayoutState::resolve_text_shadows(Vector 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(used_values.node()); + if (!box.paintable_box()) + continue; + + auto& paintable_box = const_cast(*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) diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.h b/Userland/Libraries/LibWeb/Layout/LayoutState.h index 1c168b855b..a499c60e3c 100644 --- a/Userland/Libraries/LibWeb/Layout/LayoutState.h +++ b/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 const& paintables_with_lines); + void resolve_css_transform(); }; } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 8e89f09209..f040d5ae6e 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -185,6 +185,12 @@ public: void set_box_shadow_data(Vector box_shadow_data) { m_box_shadow_data = move(box_shadow_data); } Vector 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 m_box_shadow_data; + Gfx::FloatMatrix4x4 m_transform { Gfx::FloatMatrix4x4::identity() }; + CSSPixelPoint m_transform_origin; }; class PaintableWithLines : public PaintableBox { diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index 6a72456332..a0573583c0 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/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 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(); + } + 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 static TraversalDecision for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(Paintable const& paintable, Callback callback) { @@ -420,7 +397,9 @@ Optional StackingContext::hit_test(CSSPixelPoint position, HitTes if (!paintable().is_visible()) return {}; - auto transform_origin = this->transform_origin().to_type(); + 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(), diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h index 86d4113c66..3f722f5bb9 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.h +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h @@ -37,7 +37,6 @@ public: void paint(PaintContext&) const; Optional 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 m_paintable; - Gfx::FloatMatrix4x4 m_transform; - Gfx::FloatPoint m_transform_origin; StackingContext* const m_parent { nullptr }; Vector 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 const& transformations) const; - Gfx::FloatPoint transform_origin() const { return m_transform_origin; } - Gfx::FloatPoint compute_transform_origin() const; }; }