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
This commit is contained in:
Simon Wanner 2022-03-21 19:38:00 +01:00 committed by Andreas Kling
parent 63055ff5ad
commit 145efbe07a
6 changed files with 46 additions and 4 deletions

View file

@ -75,6 +75,11 @@ struct Transformation {
Vector<Variant<CSS::Length, float>> values;
};
struct TransformOrigin {
CSS::LengthPercentage x { Percentage(50) };
CSS::LengthPercentage y { Percentage(50) };
};
struct FlexBasisData {
CSS::FlexBasis type { CSS::FlexBasis::Auto };
Optional<CSS::LengthPercentage> length_percentage;
@ -169,6 +174,7 @@ public:
Optional<LengthPercentage> const& stroke_width() const { return m_inherited.stroke_width; }
Vector<CSS::Transformation> 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<BoxShadowData> box_shadow {};
Vector<CSS::Transformation> transformations {};
CSS::TransformOrigin transform_origin {};
CSS::BoxSizing box_sizing { InitialValues::box_sizing() };
CSS::ContentData content;
Variant<CSS::VerticalAlign, CSS::LengthPercentage> 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<BoxShadowData>&& value) { m_noninherited.box_shadow = move(value); }
void set_transformations(Vector<CSS::Transformation> 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<CSS::VerticalAlign, CSS::LengthPercentage> value) { m_noninherited.vertical_align = value; }
void set_visibility(CSS::Visibility value) { m_inherited.visibility = value; }

View file

@ -347,6 +347,29 @@ Vector<CSS::Transformation> StyleProperties::transformations() const
return transformations;
}
static Optional<LengthPercentage> 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<CSS::AlignItems> StyleProperties::align_items() const
{
auto value = property(CSS::PropertyID::AlignItems);

View file

@ -77,6 +77,7 @@ public:
Variant<CSS::VerticalAlign, CSS::LengthPercentage> vertical_align() const;
Vector<CSS::Transformation> transformations() const;
CSS::TransformOrigin transform_origin() const;
Gfx::Font const& computed_font() const
{

View file

@ -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,

View file

@ -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<HitTestResult> 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;

View file

@ -45,6 +45,7 @@ private:
Gfx::FloatMatrix4x4 get_transformation_matrix(CSS::Transformation const& transformation) const;
Gfx::FloatMatrix4x4 combine_transformations(Vector<CSS::Transformation> const& transformations) const;
Gfx::AffineTransform combine_transformations_2d(Vector<CSS::Transformation> const& transformations) const;
Gfx::FloatPoint transform_origin() const;
};
}