From 145efbe07a6337fdc58246552bde014c1008fd0f Mon Sep 17 00:00:00 2001 From: Simon Wanner Date: Mon, 21 Mar 2022 19:38:00 +0100 Subject: [PATCH] LibWeb: Apply the CSS transform-origin property We don't have transform-box yet, so this applies to the border-box for now. This also makes us pass a couple Web Platform Tests as well :^) For example: https://wpt.live/css/css-transforms/css3-transform-scale-002.html --- .../Libraries/LibWeb/CSS/ComputedValues.h | 8 +++++++ .../Libraries/LibWeb/CSS/StyleProperties.cpp | 23 +++++++++++++++++++ .../Libraries/LibWeb/CSS/StyleProperties.h | 1 + Userland/Libraries/LibWeb/Layout/Node.cpp | 1 + .../LibWeb/Painting/StackingContext.cpp | 16 +++++++++---- .../LibWeb/Painting/StackingContext.h | 1 + 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index 63dc4bfa0a8..d1bb3e11316 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -75,6 +75,11 @@ struct Transformation { Vector> values; }; +struct TransformOrigin { + CSS::LengthPercentage x { Percentage(50) }; + CSS::LengthPercentage y { Percentage(50) }; +}; + struct FlexBasisData { CSS::FlexBasis type { CSS::FlexBasis::Auto }; Optional length_percentage; @@ -169,6 +174,7 @@ public: Optional const& stroke_width() const { return m_inherited.stroke_width; } Vector transformations() const { return m_noninherited.transformations; } + CSS::TransformOrigin transform_origin() const { return m_noninherited.transform_origin; } float font_size() const { return m_inherited.font_size; } int font_weight() const { return m_inherited.font_weight; } @@ -241,6 +247,7 @@ protected: float opacity { InitialValues::opacity() }; Vector box_shadow {}; Vector transformations {}; + CSS::TransformOrigin transform_origin {}; CSS::BoxSizing box_sizing { InitialValues::box_sizing() }; CSS::ContentData content; Variant vertical_align { InitialValues::vertical_align() }; @@ -304,6 +311,7 @@ public: void set_justify_content(CSS::JustifyContent value) { m_noninherited.justify_content = value; } void set_box_shadow(Vector&& value) { m_noninherited.box_shadow = move(value); } void set_transformations(Vector value) { m_noninherited.transformations = move(value); } + void set_transform_origin(CSS::TransformOrigin value) { m_noninherited.transform_origin = value; } void set_box_sizing(CSS::BoxSizing value) { m_noninherited.box_sizing = value; } void set_vertical_align(Variant value) { m_noninherited.vertical_align = value; } void set_visibility(CSS::Visibility value) { m_inherited.visibility = value; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index aaf84c44a68..d9fdb39dcd6 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -347,6 +347,29 @@ Vector StyleProperties::transformations() const return transformations; } +static Optional length_percentage_for_style_value(StyleValue const& value) +{ + if (value.is_length()) + return value.to_length(); + if (value.is_percentage()) + return value.as_percentage().percentage(); + return {}; +} + +CSS::TransformOrigin StyleProperties::transform_origin() const +{ + auto value = property(CSS::PropertyID::TransformOrigin); + if (!value.has_value() || !value.value()->is_value_list() || value.value()->as_value_list().size() != 2) + return {}; + auto const& list = value.value()->as_value_list(); + auto x_value = length_percentage_for_style_value(list.values()[0]); + auto y_value = length_percentage_for_style_value(list.values()[1]); + if (!x_value.has_value() || !y_value.has_value()) { + return {}; + } + return { x_value.value(), y_value.value() }; +} + Optional StyleProperties::align_items() const { auto value = property(CSS::PropertyID::AlignItems); diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index f9648735009..2e58608544e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -77,6 +77,7 @@ public: Variant vertical_align() const; Vector transformations() const; + CSS::TransformOrigin transform_origin() const; Gfx::Font const& computed_font() const { diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index 49701d0e3c9..4755ced2704 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -493,6 +493,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style) computed_values.set_box_shadow(specified_style.box_shadow()); computed_values.set_transformations(specified_style.transformations()); + computed_values.set_transform_origin(specified_style.transform_origin()); auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) { // FIXME: The default border color value is `currentcolor`, but since we can't resolve that easily, diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index 308ca773543..db5c9471017 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -261,8 +261,7 @@ void StackingContext::paint(PaintContext& context) const PaintContext paint_context(painter, context.palette(), context.scroll_offset()); paint_internal(paint_context); - // FIXME: Use the transform origin specified in CSS or SVG - auto transform_origin = m_box.paint_box()->absolute_position(); + auto transform_origin = this->transform_origin(); auto source_rect = m_box.paint_box()->absolute_border_box_rect().translated(-transform_origin); auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform_origin); @@ -273,10 +272,19 @@ void StackingContext::paint(PaintContext& context) const } } +Gfx::FloatPoint StackingContext::transform_origin() const +{ + auto style_value = m_box.computed_values().transform_origin(); + // FIXME: respect transform-box property + auto reference_box = m_box.paint_box()->absolute_border_box_rect(); + auto x = reference_box.left() + style_value.x.resolved(m_box, CSS::Length::make_px(reference_box.width())).to_px(m_box); + auto y = reference_box.top() + style_value.y.resolved(m_box, CSS::Length::make_px(reference_box.height())).to_px(m_box); + return { x, y }; +} + Optional StackingContext::hit_test(Gfx::FloatPoint const& position, HitTestType type) const { - // FIXME: Use the transform origin specified in CSS or SVG - auto transform_origin = m_box.paint_box()->absolute_position(); + auto transform_origin = this->transform_origin(); auto affine_transform = combine_transformations_2d(m_box.computed_values().transformations()); auto transformed_position = affine_transform.inverse().value_or({}).map(position - transform_origin) + transform_origin; diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h index 4ebc420e624..9ea3e8ca712 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.h +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h @@ -45,6 +45,7 @@ private: Gfx::FloatMatrix4x4 get_transformation_matrix(CSS::Transformation const& transformation) const; Gfx::FloatMatrix4x4 combine_transformations(Vector const& transformations) const; Gfx::AffineTransform combine_transformations_2d(Vector const& transformations) const; + Gfx::FloatPoint transform_origin() const; }; }