mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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>
This commit is contained in:
parent
7f2f51b59d
commit
455f4202c6
12 changed files with 57 additions and 63 deletions
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
2
tests/wpt/meta/css/css-flexbox/flexbox-safe-overflow-position-006.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-flexbox/flexbox-safe-overflow-position-006.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[flexbox-safe-overflow-position-006.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[change-translate-property.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[individual-transform-1.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[individual-transform-2a.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[individual-transform-2b.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[individual-transform-2c.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[individual-transform-2d.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[individual-transform-2e.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[scale-animation-with-var-001.html]
|
||||
expected: FAIL
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue