diff options
author | chocolate-pie <106949016+chocolate-pie@users.noreply.github.com> | 2025-03-14 23:46:20 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-14 14:46:20 +0000 |
commit | 455f4202c60724cd50e31e38818d966adeaa075a (patch) | |
tree | fe99f4cd51bcd506d0c88af55cadb20439325de0 | |
parent | 7f2f51b59d1a7e647627eb4fe71d9acf6987b76a (diff) | |
download | servo-455f4202c60724cd50e31e38818d966adeaa075a.tar.gz servo-455f4202c60724cd50e31e38818d966adeaa075a.zip |
layout: Add support for basic transform css properties (#35926)
Signed-off-by: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
12 files changed, 57 insertions, 63 deletions
diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index a0d8a9f12e0..01fdedd0f7c 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -19,10 +19,11 @@ use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; use style::computed_values::overflow_x::T as ComputedOverflow; use style::computed_values::position::T as ComputedPosition; use style::properties::ComputedValues; +use style::values::computed::angle::Angle; use style::values::computed::basic_shape::ClipPath; use style::values::computed::{ClipRectOrAuto, Length}; use style::values::generics::box_::Perspective; -use style::values::generics::transform; +use style::values::generics::transform::{self, GenericRotate, GenericScale, GenericTranslate}; use style::values::specified::box_::DisplayOutside; use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D}; use webrender_api::{self as wr, BorderRadius}; @@ -39,7 +40,7 @@ use crate::fragment_tree::{ PositioningFragment, SpecificLayoutInfo, }; use crate::geom::{AuOrAuto, PhysicalRect, PhysicalSides}; -use crate::style_ext::ComputedValuesExt; +use crate::style_ext::{ComputedValuesExt, TransformExt}; #[derive(Clone)] pub(crate) struct ContainingBlock { @@ -1635,17 +1636,37 @@ impl BoxFragment { /// Returns the 4D matrix representing this fragment's transform. pub fn calculate_transform_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> { let list = &self.style.get_box().transform; + let length_rect = au_rect_to_length_rect(border_rect); + // https://drafts.csswg.org/css-transforms-2/#individual-transforms + let rotate = match self.style.clone_rotate() { + GenericRotate::Rotate(angle) => (0., 0., 1., angle), + GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle), + GenericRotate::None => (0., 0., 1., Angle::zero()), + }; + let scale = match self.style.clone_scale() { + GenericScale::Scale(sx, sy, sz) => (sx, sy, sz), + GenericScale::None => (1., 1., 1.), + }; + let translation = match self.style.clone_translate() { + GenericTranslate::Translate(x, y, z) => LayoutTransform::translation( + x.resolve(length_rect.size.width).px(), + y.resolve(length_rect.size.height).px(), + z.px(), + ), + GenericTranslate::None => LayoutTransform::identity(), + }; - let transform = LayoutTransform::from_untyped( - &list - .to_transform_3d_matrix(Some(&au_rect_to_length_rect(border_rect))) - .ok()? - .0, - ); + let angle = euclid::Angle::radians(rotate.3.radians()); + let transform_base = list.to_transform_3d_matrix(Some(&length_rect)).ok()?; + let transform = LayoutTransform::from_untyped(&transform_base.0) + .then_rotate(rotate.0, rotate.1, rotate.2, angle) + .then_scale(scale.0, scale.1, scale.2) + .then(&translation); // WebRender will end up dividing by the scale value of this transform, so we // want to ensure we don't feed it a divisor of 0. - assert_ne!(transform.m11, 0.); - assert_ne!(transform.m22, 0.); + if transform.m11 == 0. || transform.m22 == 0. { + return Some(LayoutTransform::identity()); + } let transform_origin = &self.style.get_box().transform_origin; let transform_origin_x = transform_origin @@ -1658,18 +1679,7 @@ impl BoxFragment { .to_f32_px(); let transform_origin_z = transform_origin.depth.px(); - let pre_transform = LayoutTransform::translation( - transform_origin_x, - transform_origin_y, - transform_origin_z, - ); - let post_transform = LayoutTransform::translation( - -transform_origin_x, - -transform_origin_y, - -transform_origin_z, - ); - - Some(post_transform.then(&transform).then(&pre_transform)) + Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z)) } /// Returns the 4D matrix representing this fragment's perspective. @@ -1688,20 +1698,15 @@ impl BoxFragment { .px(), ); - let pre_transform = - LayoutTransform::translation(perspective_origin.x, perspective_origin.y, 0.0); - let post_transform = - LayoutTransform::translation(-perspective_origin.x, -perspective_origin.y, 0.0); - let perspective_matrix = LayoutTransform::from_untyped( &transform::create_perspective_matrix(length.px()), ); - Some( - post_transform - .then(&perspective_matrix) - .then(&pre_transform), - ) + Some(perspective_matrix.change_basis( + perspective_origin.x, + perspective_origin.y, + 0.0, + )) }, Perspective::None => None, } diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 2d52303129c..92c7b1f3314 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -24,9 +24,11 @@ use style::values::computed::image::Image as ComputedImageLayer; use style::values::computed::{AlignItems, BorderStyle, Color, Inset, LengthPercentage, Margin}; use style::values::generics::box_::Perspective; use style::values::generics::position::{GenericAspectRatio, PreferredRatio}; +use style::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate}; use style::values::specified::align::AlignFlags; use style::values::specified::{Overflow, WillChangeBits, box_ as stylo}; use webrender_api as wr; +use webrender_api::units::LayoutTransform; use crate::dom_traversal::Contents; use crate::fragment_tree::FragmentFlags; @@ -501,6 +503,9 @@ impl ComputedValuesExt for ComputedValues { fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool { self.is_transformable(fragment_flags) && (!self.get_box().transform.0.is_empty() || + self.get_box().scale != GenericScale::None || + self.get_box().rotate != GenericRotate::None || + self.get_box().translate != GenericTranslate::None || self.get_box().perspective != Perspective::None) } @@ -1181,3 +1186,16 @@ impl Clamp for Au { self.clamp_below_max(max).max(min) } } + +pub(crate) trait TransformExt { + fn change_basis(&self, x: f32, y: f32, z: f32) -> Self; +} + +impl TransformExt for LayoutTransform { + /// <https://drafts.csswg.org/css-transforms/#transformation-matrix-computation> + fn change_basis(&self, x: f32, y: f32, z: f32) -> Self { + let pre_translation = Self::translation(x, y, z); + let post_translation = Self::translation(-x, -y, -z); + post_translation.then(self).then(&pre_translation) + } +} diff --git a/tests/wpt/meta/css/css-flexbox/flexbox-safe-overflow-position-006.html.ini b/tests/wpt/meta/css/css-flexbox/flexbox-safe-overflow-position-006.html.ini new file mode 100644 index 00000000000..9edcdef8700 --- /dev/null +++ b/tests/wpt/meta/css/css-flexbox/flexbox-safe-overflow-position-006.html.ini @@ -0,0 +1,2 @@ +[flexbox-safe-overflow-position-006.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/change-translate-property.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/change-translate-property.html.ini deleted file mode 100644 index f0c9923d7ec..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/change-translate-property.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[change-translate-property.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-1.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-1.html.ini deleted file mode 100644 index f10a8cf7451..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-1.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[individual-transform-1.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2a.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2a.html.ini deleted file mode 100644 index 7d934b36ae2..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2a.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[individual-transform-2a.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2b.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2b.html.ini deleted file mode 100644 index bf8df442cb5..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2b.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[individual-transform-2b.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2c.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2c.html.ini deleted file mode 100644 index fb9e5d4d3a9..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2c.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[individual-transform-2c.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2d.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2d.html.ini deleted file mode 100644 index 414a8fd4963..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2d.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[individual-transform-2d.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2e.html.ini b/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2e.html.ini deleted file mode 100644 index b20094b7cc1..00000000000 --- a/tests/wpt/meta/css/css-transforms/individual-transform/individual-transform-2e.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[individual-transform-2e.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/scale-animation-with-var-001.html.ini b/tests/wpt/meta/css/css-transforms/scale-animation-with-var-001.html.ini deleted file mode 100644 index fbf0a6afbd1..00000000000 --- a/tests/wpt/meta/css/css-transforms/scale-animation-with-var-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[scale-animation-with-var-001.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-transforms/transform-hit-testing.html.ini b/tests/wpt/meta/css/css-transforms/transform-hit-testing.html.ini deleted file mode 100644 index 7d836afc25c..00000000000 --- a/tests/wpt/meta/css/css-transforms/transform-hit-testing.html.ini +++ /dev/null @@ -1,15 +0,0 @@ -[transform-hit-testing.html] - [hit testing of rectangle with 'translate' and 'rotate'] - expected: FAIL - - [hit testing of rectangle with 'transform'] - expected: FAIL - - [hit testing of rectangle with 'translate' and 'rotate' and 'scale' and 'transform'] - expected: FAIL - - [hit testing of square with 'rotate'] - expected: FAIL - - [hit testing of square with 'scale'] - expected: FAIL |