Bug 1725207 - Implement transform: perspective(none). r=mattwoodrow

Differential Revision: https://phabricator.services.mozilla.com/D123350
This commit is contained in:
Emilio Cobos Álvarez 2021-08-24 00:32:24 +00:00
Родитель b47f6e64d7
Коммит 380d55802a
8 изменённых файлов: 94 добавлений и 33 удалений

Просмотреть файл

@ -374,9 +374,14 @@ static void ProcessRotate3D(Matrix4x4& aMatrix, float aX, float aY, float aZ,
aMatrix = temp * aMatrix;
}
static void ProcessPerspective(Matrix4x4& aMatrix, const Length& aLength) {
float depth = aLength.ToCSSPixels();
ApplyPerspectiveToMatrix(aMatrix, depth);
static void ProcessPerspective(Matrix4x4& aMatrix, const StyleGenericPerspectiveFunction<Length>& aPerspective) {
if (aPerspective.IsNone()) {
return;
}
float p = aPerspective.AsLength().ToCSSPixels();
if (!std::isinf(p)) {
aMatrix.Perspective(std::max(p, 1.0f));
}
}
static void MatrixForTransformFunction(Matrix4x4& aMatrix,

Просмотреть файл

@ -34,17 +34,6 @@ namespace nsStyleTransformMatrix {
// The operator passed to Servo backend.
enum class MatrixTransformOperator : uint8_t { Interpolate, Accumulate };
// Function for applying perspective() transform function.
inline void ApplyPerspectiveToMatrix(mozilla::gfx::Matrix4x4& aMatrix,
float aDepth) {
// TODO(Bug 1725207): Infinite perspective values from stylo are being
// serialized as FLOAT_MAX, so we have to explicitly check for that case
// and treat it as infinity (which is the identity matrix).
if (!std::isinf(aDepth) && aDepth != std::numeric_limits<float>::max()) {
aMatrix.Perspective(std::max(aDepth, 1.0f));
}
}
/**
* This class provides on-demand access to the 'reference box' for CSS
* transforms (needed to resolve percentage values in 'transform',

Просмотреть файл

@ -1105,8 +1105,8 @@ impl Animate for ComputedTransformOperation {
// interpolated as defined in section Interpolation of
// Matrices afterwards.
//
let from = create_perspective_matrix(fd.px());
let to = create_perspective_matrix(td.px());
let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
@ -1115,15 +1115,17 @@ impl Animate for ComputedTransformOperation {
// Clamp results outside of the -1 to 0 range so that we get perspective
// function values between 1 and infinity.
let used_value = if perspective_z >= 0. {
std::f32::INFINITY
} else if perspective_z <= -1. {
1.
transform::PerspectiveFunction::None
} else {
-1. / perspective_z
transform::PerspectiveFunction::Length(CSSPixelLength::new(
if perspective_z <= -1. {
1.
} else {
-1. / perspective_z
}
))
};
Ok(TransformOperation::Perspective(CSSPixelLength::new(
used_value,
)))
Ok(TransformOperation::Perspective(used_value))
},
_ if self.is_translate() && other.is_translate() => self
.to_translate_3d()
@ -1202,14 +1204,18 @@ impl ComputeSquaredDistance for ComputedTransformOperation {
(
&TransformOperation::Perspective(ref fd),
&TransformOperation::Perspective(ref td),
) => fd.compute_squared_distance(td),
) => {
fd.infinity_or(|l| l.px())
.compute_squared_distance(&td.infinity_or(|l| l.px()))
},
(&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m)) |
(&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
// FIXME(emilio): Is this right? Why interpolating this with
// Perspective but not with anything else?
let mut p_matrix = Matrix3D::identity();
if p.px() >= 0. {
p_matrix.m34 = -1. / p.px().max(1.);
let p = p.infinity_or(|p| p.px());
if p >= 0. {
p_matrix.m34 = -1. / p.max(1.);
}
p_matrix.compute_squared_distance(&m)
},

Просмотреть файл

@ -24,6 +24,9 @@ pub type Transform = generic::GenericTransform<TransformOperation>;
pub type TransformOrigin =
generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
/// The computed value of the `perspective()` transform function.
pub type PerspectiveFunction = generic::PerspectiveFunction<Length>;
/// A vector to represent the direction vector (rotate axis) for Rotate3D.
pub type DirectionVector = Vector3D<CSSFloat>;
@ -517,7 +520,7 @@ impl ToAnimatedZero for TransformOperation {
Ok(generic::TransformOperation::Rotate(Angle::zero()))
},
generic::TransformOperation::Perspective(_) => Ok(
generic::TransformOperation::Perspective(Length::new(std::f32::INFINITY))
generic::TransformOperation::Perspective(generic::PerspectiveFunction::None)
),
generic::TransformOperation::AccumulateMatrix { .. } |
generic::TransformOperation::InterpolateMatrix { .. } => {

Просмотреть файл

@ -141,6 +141,41 @@ fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
x == y
}
/// A value for the `perspective()` transform function, which is either a
/// non-negative `<length>` or `none`.
#[derive(
Clone,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericPerspectiveFunction<L> {
/// `none`
None,
/// A `<length>`.
Length(L),
}
impl<L> GenericPerspectiveFunction<L> {
/// Returns `f32::INFINITY` or the result of a function on the length value.
pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
match *self {
Self::None => std::f32::INFINITY,
Self::Length(ref l) => f(l),
}
}
}
pub use self::GenericPerspectiveFunction as PerspectiveFunction;
#[derive(
Clone,
Debug,
@ -240,7 +275,7 @@ where
///
/// The value must be greater than or equal to zero.
#[css(function)]
Perspective(Length),
Perspective(GenericPerspectiveFunction<Length>),
/// A intermediate type for interpolation of mismatched transform lists.
#[allow(missing_docs)]
#[css(comma, function = "interpolatematrix")]
@ -469,9 +504,12 @@ where
let theta = euclid::Angle::radians(theta.radians64());
Transform3D::rotation(0., 0., 1., theta)
},
Perspective(ref d) => {
let m = create_perspective_matrix(d.to_pixel_length(None)?);
m.cast()
Perspective(ref p) => {
let px = match p {
PerspectiveFunction::None => std::f32::INFINITY,
PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
};
create_perspective_matrix(px).cast()
},
Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),

Просмотреть файл

@ -238,8 +238,14 @@ impl Transform {
Ok(generic::TransformOperation::SkewY(theta))
},
"perspective" => {
let d = specified::Length::parse_non_negative(context, input)?;
Ok(generic::TransformOperation::Perspective(d))
let p = match input.try_parse(|input| specified::Length::parse_non_negative(context, input)) {
Ok(p) => generic::PerspectiveFunction::Length(p),
Err(..) => {
input.expect_ident_matching("none")?;
generic::PerspectiveFunction::None
}
};
Ok(generic::TransformOperation::Perspective(p))
},
_ => Err(()),
};

Просмотреть файл

@ -123,6 +123,17 @@ test_interpolation({
{at: 1, expect: `scaleZ(2) perspective(500px)`},
{at: 2, expect: `scaleZ(2) perspective(250px)`},
]);
test_interpolation({
property: 'transform',
from: 'perspective(none)',
to: 'perspective(500px)',
}, [
{at: -1, expect: `perspective(none)`},
{at: 0, expect: `perspective(none)`},
{at: 0.5, expect: `perspective(1000px)`},
{at: 1, expect: `perspective(500px)`},
{at: 2, expect: `perspective(250px)`},
]);
// Rotate
test_interpolation({

Просмотреть файл

@ -67,6 +67,9 @@ test_valid_value("transform", "skewX(90deg)");
test_valid_value("transform", "skewY(0)", "skewY(0deg)");
test_valid_value("transform", "skewY(-90deg)");
test_valid_value("transform", "perspective(10px)");
test_valid_value("transform", "perspective(none)");
test_valid_value("transform", "translate(1px, 2%) scale(3, 4) rotate(-90deg)");
</script>
</body>