1use crate::values::computed::length::Length as ComputedLength;
8use crate::values::computed::length::LengthPercentage as ComputedLengthPercentage;
9use crate::values::specified::angle::Angle as SpecifiedAngle;
10use crate::values::specified::length::Length as SpecifiedLength;
11use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
12use crate::values::{computed, CSSFloat};
13use crate::{Zero, ZeroNoPercent};
14use euclid::default::{Rect, Transform3D};
15use std::fmt::{self, Write};
16use std::ops::Neg;
17use style_traits::{CssWriter, ToCss};
18
19#[allow(missing_docs)]
21#[derive(
22 Clone,
23 Copy,
24 Debug,
25 Deserialize,
26 MallocSizeOf,
27 PartialEq,
28 Serialize,
29 SpecifiedValueInfo,
30 ToAnimatedValue,
31 ToComputedValue,
32 ToCss,
33 ToResolvedValue,
34 ToShmem,
35)]
36#[css(comma, function = "matrix")]
37#[repr(C)]
38pub struct GenericMatrix<T> {
39 pub a: T,
40 pub b: T,
41 pub c: T,
42 pub d: T,
43 pub e: T,
44 pub f: T,
45}
46
47pub use self::GenericMatrix as Matrix;
48
49#[allow(missing_docs)]
50#[cfg_attr(rustfmt, rustfmt_skip)]
51#[derive(
52 Clone,
53 Copy,
54 Debug,
55 Deserialize,
56 MallocSizeOf,
57 PartialEq,
58 Serialize,
59 SpecifiedValueInfo,
60 ToAnimatedValue,
61 ToComputedValue,
62 ToCss,
63 ToResolvedValue,
64 ToShmem,
65)]
66#[css(comma, function = "matrix3d")]
67#[repr(C)]
68pub struct GenericMatrix3D<T> {
69 pub m11: T, pub m12: T, pub m13: T, pub m14: T,
70 pub m21: T, pub m22: T, pub m23: T, pub m24: T,
71 pub m31: T, pub m32: T, pub m33: T, pub m34: T,
72 pub m41: T, pub m42: T, pub m43: T, pub m44: T,
73}
74
75pub use self::GenericMatrix3D as Matrix3D;
76
77#[cfg_attr(rustfmt, rustfmt_skip)]
78impl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {
79 #[inline]
80 fn from(m: Matrix<T>) -> Self {
81 Transform3D::new(
82 m.a.into(), m.b.into(), 0.0, 0.0,
83 m.c.into(), m.d.into(), 0.0, 0.0,
84 0.0, 0.0, 1.0, 0.0,
85 m.e.into(), m.f.into(), 0.0, 1.0,
86 )
87 }
88}
89
90#[cfg_attr(rustfmt, rustfmt_skip)]
91impl<T: Into<f64>> From<Matrix3D<T>> for Transform3D<f64> {
92 #[inline]
93 fn from(m: Matrix3D<T>) -> Self {
94 Transform3D::new(
95 m.m11.into(), m.m12.into(), m.m13.into(), m.m14.into(),
96 m.m21.into(), m.m22.into(), m.m23.into(), m.m24.into(),
97 m.m31.into(), m.m32.into(), m.m33.into(), m.m34.into(),
98 m.m41.into(), m.m42.into(), m.m43.into(), m.m44.into(),
99 )
100 }
101}
102
103#[derive(
105 Animate,
106 Clone,
107 ComputeSquaredDistance,
108 Copy,
109 Debug,
110 MallocSizeOf,
111 PartialEq,
112 SpecifiedValueInfo,
113 ToAnimatedValue,
114 ToAnimatedZero,
115 ToComputedValue,
116 ToCss,
117 ToResolvedValue,
118 ToShmem,
119)]
120#[repr(C)]
121pub struct GenericTransformOrigin<H, V, Depth> {
122 pub horizontal: H,
124 pub vertical: V,
126 pub depth: Depth,
128}
129
130pub use self::GenericTransformOrigin as TransformOrigin;
131
132impl<H, V, D> TransformOrigin<H, V, D> {
133 pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
135 Self {
136 horizontal,
137 vertical,
138 depth,
139 }
140 }
141}
142
143fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
144 x == y
145}
146
147#[derive(
150 Clone,
151 Debug,
152 Deserialize,
153 MallocSizeOf,
154 PartialEq,
155 Serialize,
156 SpecifiedValueInfo,
157 ToAnimatedValue,
158 ToComputedValue,
159 ToCss,
160 ToResolvedValue,
161 ToShmem,
162)]
163#[repr(C, u8)]
164pub enum GenericPerspectiveFunction<L> {
165 None,
167 Length(L),
169}
170
171impl<L> GenericPerspectiveFunction<L> {
172 pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
174 match *self {
175 Self::None => f32::INFINITY,
176 Self::Length(ref l) => f(l),
177 }
178 }
179}
180
181pub use self::GenericPerspectiveFunction as PerspectiveFunction;
182
183#[derive(
184 Clone,
185 Debug,
186 Deserialize,
187 MallocSizeOf,
188 PartialEq,
189 Serialize,
190 SpecifiedValueInfo,
191 ToAnimatedValue,
192 ToComputedValue,
193 ToCss,
194 ToResolvedValue,
195 ToShmem,
196)]
197#[repr(C, u8)]
198pub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>
200where
201 Angle: Zero,
202 LengthPercentage: Zero + ZeroNoPercent,
203 Number: PartialEq,
204{
205 Matrix(GenericMatrix<Number>),
207 Matrix3D(GenericMatrix3D<Number>),
209 #[css(comma, function)]
215 Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
216 #[css(function = "skewX")]
218 SkewX(Angle),
219 #[css(function = "skewY")]
221 SkewY(Angle),
222 #[css(comma, function)]
224 Translate(
225 LengthPercentage,
226 #[css(skip_if = "ZeroNoPercent::is_zero_no_percent")] LengthPercentage,
227 ),
228 #[css(function = "translateX")]
230 TranslateX(LengthPercentage),
231 #[css(function = "translateY")]
233 TranslateY(LengthPercentage),
234 #[css(function = "translateZ")]
236 TranslateZ(Length),
237 #[css(comma, function = "translate3d")]
239 Translate3D(LengthPercentage, LengthPercentage, Length),
240 #[css(comma, function)]
244 Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
245 #[css(function = "scaleX")]
247 ScaleX(Number),
248 #[css(function = "scaleY")]
250 ScaleY(Number),
251 #[css(function = "scaleZ")]
253 ScaleZ(Number),
254 #[css(comma, function = "scale3d")]
256 Scale3D(Number, Number, Number),
257 #[css(function)]
261 Rotate(Angle),
262 #[css(function = "rotateX")]
264 RotateX(Angle),
265 #[css(function = "rotateY")]
267 RotateY(Angle),
268 #[css(function = "rotateZ")]
270 RotateZ(Angle),
271 #[css(comma, function = "rotate3d")]
275 Rotate3D(Number, Number, Number, Angle),
276 #[css(function)]
283 Perspective(GenericPerspectiveFunction<Length>),
284 #[allow(missing_docs)]
286 #[css(comma, function = "interpolatematrix")]
287 InterpolateMatrix {
288 from_list: GenericTransform<
289 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
290 >,
291 to_list: GenericTransform<
292 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
293 >,
294 progress: computed::Percentage,
295 },
296 #[allow(missing_docs)]
298 #[css(comma, function = "accumulatematrix")]
299 AccumulateMatrix {
300 from_list: GenericTransform<
301 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
302 >,
303 to_list: GenericTransform<
304 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
305 >,
306 count: Integer,
307 },
308}
309
310pub use self::GenericTransformOperation as TransformOperation;
311
312#[derive(
313 Clone,
314 Debug,
315 Deserialize,
316 MallocSizeOf,
317 PartialEq,
318 Serialize,
319 SpecifiedValueInfo,
320 ToAnimatedValue,
321 ToComputedValue,
322 ToCss,
323 ToResolvedValue,
324 ToShmem,
325)]
326#[repr(C)]
327pub struct GenericTransform<T>(#[css(if_empty = "none", iterable)] pub crate::OwnedSlice<T>);
329
330pub use self::GenericTransform as Transform;
331
332impl<Angle, Number, Length, Integer, LengthPercentage>
333 TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
334where
335 Angle: Zero,
336 LengthPercentage: Zero + ZeroNoPercent,
337 Number: PartialEq,
338{
339 pub fn is_rotate(&self) -> bool {
341 use self::TransformOperation::*;
342 matches!(
343 *self,
344 Rotate(..) | Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..)
345 )
346 }
347
348 pub fn is_translate(&self) -> bool {
350 use self::TransformOperation::*;
351 match *self {
352 Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {
353 true
354 },
355 _ => false,
356 }
357 }
358
359 pub fn is_scale(&self) -> bool {
361 use self::TransformOperation::*;
362 match *self {
363 Scale(..) | Scale3D(..) | ScaleX(..) | ScaleY(..) | ScaleZ(..) => true,
364 _ => false,
365 }
366 }
367}
368
369pub trait ToAbsoluteLength {
371 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;
373}
374
375impl ToAbsoluteLength for SpecifiedLength {
376 #[inline]
380 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
381 match *self {
382 SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),
383 SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
384 }
385 }
386}
387
388impl ToAbsoluteLength for SpecifiedLengthPercentage {
389 #[inline]
393 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
394 use self::SpecifiedLengthPercentage::*;
395 match *self {
396 Length(len) => len.to_computed_pixel_length_without_context(),
397 Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
398 Percentage(..) => Err(()),
399 }
400 }
401}
402
403impl ToAbsoluteLength for ComputedLength {
404 #[inline]
405 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
406 Ok(self.px())
407 }
408}
409
410impl ToAbsoluteLength for ComputedLengthPercentage {
411 #[inline]
412 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
413 Ok(self
414 .maybe_percentage_relative_to(containing_len)
415 .ok_or(())?
416 .px())
417 }
418}
419
420pub trait ToMatrix {
422 fn is_3d(&self) -> bool;
424
425 fn to_3d_matrix(
427 &self,
428 reference_box: Option<&Rect<ComputedLength>>,
429 ) -> Result<Transform3D<f64>, ()>;
430}
431
432pub trait ToRadians {
434 fn radians64(&self) -> f64;
436}
437
438impl ToRadians for computed::angle::Angle {
439 #[inline]
440 fn radians64(&self) -> f64 {
441 computed::angle::Angle::radians64(self)
442 }
443}
444
445impl ToRadians for SpecifiedAngle {
446 #[inline]
447 fn radians64(&self) -> f64 {
448 computed::angle::Angle::from_degrees(self.degrees()).radians64()
449 }
450}
451
452impl<Angle, Number, Length, Integer, LoP> ToMatrix
453 for TransformOperation<Angle, Number, Length, Integer, LoP>
454where
455 Angle: Zero + ToRadians + Copy,
456 Number: PartialEq + Copy + Into<f32> + Into<f64>,
457 Length: ToAbsoluteLength,
458 LoP: Zero + ToAbsoluteLength + ZeroNoPercent,
459{
460 #[inline]
461 fn is_3d(&self) -> bool {
462 use self::TransformOperation::*;
463 match *self {
464 Translate3D(..) | TranslateZ(..) | Rotate3D(..) | RotateX(..) | RotateY(..)
465 | RotateZ(..) | Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,
466 _ => false,
467 }
468 }
469
470 #[inline]
475 fn to_3d_matrix(
476 &self,
477 reference_box: Option<&Rect<ComputedLength>>,
478 ) -> Result<Transform3D<f64>, ()> {
479 use self::TransformOperation::*;
480
481 let reference_width = reference_box.map(|v| v.size.width);
482 let reference_height = reference_box.map(|v| v.size.height);
483 let matrix = match *self {
484 Rotate3D(ax, ay, az, theta) => {
485 let theta = theta.radians64();
486 let (ax, ay, az, theta) =
487 get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
488 Transform3D::rotation(
489 ax as f64,
490 ay as f64,
491 az as f64,
492 euclid::Angle::radians(theta),
493 )
494 },
495 RotateX(theta) => {
496 let theta = euclid::Angle::radians(theta.radians64());
497 Transform3D::rotation(1., 0., 0., theta)
498 },
499 RotateY(theta) => {
500 let theta = euclid::Angle::radians(theta.radians64());
501 Transform3D::rotation(0., 1., 0., theta)
502 },
503 RotateZ(theta) | Rotate(theta) => {
504 let theta = euclid::Angle::radians(theta.radians64());
505 Transform3D::rotation(0., 0., 1., theta)
506 },
507 Perspective(ref p) => {
508 let px = match p {
509 PerspectiveFunction::None => f32::INFINITY,
510 PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
511 };
512 create_perspective_matrix(px).cast()
513 },
514 Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
515 Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
516 ScaleX(s) => Transform3D::scale(s.into(), 1., 1.),
517 ScaleY(s) => Transform3D::scale(1., s.into(), 1.),
518 ScaleZ(s) => Transform3D::scale(1., 1., s.into()),
519 Translate3D(ref tx, ref ty, ref tz) => {
520 let tx = tx.to_pixel_length(reference_width)? as f64;
521 let ty = ty.to_pixel_length(reference_height)? as f64;
522 Transform3D::translation(tx, ty, tz.to_pixel_length(None)? as f64)
523 },
524 Translate(ref tx, ref ty) => {
525 let tx = tx.to_pixel_length(reference_width)? as f64;
526 let ty = ty.to_pixel_length(reference_height)? as f64;
527 Transform3D::translation(tx, ty, 0.)
528 },
529 TranslateX(ref t) => {
530 let t = t.to_pixel_length(reference_width)? as f64;
531 Transform3D::translation(t, 0., 0.)
532 },
533 TranslateY(ref t) => {
534 let t = t.to_pixel_length(reference_height)? as f64;
535 Transform3D::translation(0., t, 0.)
536 },
537 TranslateZ(ref z) => Transform3D::translation(0., 0., z.to_pixel_length(None)? as f64),
538 Skew(theta_x, theta_y) => Transform3D::skew(
539 euclid::Angle::radians(theta_x.radians64()),
540 euclid::Angle::radians(theta_y.radians64()),
541 ),
542 SkewX(theta) => Transform3D::skew(
543 euclid::Angle::radians(theta.radians64()),
544 euclid::Angle::radians(0.),
545 ),
546 SkewY(theta) => Transform3D::skew(
547 euclid::Angle::radians(0.),
548 euclid::Angle::radians(theta.radians64()),
549 ),
550 Matrix3D(m) => m.into(),
551 Matrix(m) => m.into(),
552 InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
553 Transform3D::identity()
560 },
561 };
562 Ok(matrix)
563 }
564}
565
566impl<T> Transform<T> {
567 pub fn none() -> Self {
569 Transform(Default::default())
570 }
571}
572
573impl<T: ToMatrix> Transform<T> {
574 #[cfg_attr(rustfmt, rustfmt_skip)]
579 pub fn to_transform_3d_matrix(
580 &self,
581 reference_box: Option<&Rect<ComputedLength>>
582 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
583 Self::components_to_transform_3d_matrix(&self.0, reference_box)
584 }
585
586 #[cfg_attr(rustfmt, rustfmt_skip)]
588 pub fn components_to_transform_3d_matrix(
589 ops: &[T],
590 reference_box: Option<&Rect<ComputedLength>>,
591 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
592 let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
593 use std::{f32, f64};
594 let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;
595 Transform3D::new(
596 cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
597 cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
598 cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
599 cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
600 )
601 };
602
603 let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;
604 Ok((cast_3d_transform(m), is_3d))
605 }
606
607 pub fn to_transform_3d_matrix_f64(
609 &self,
610 reference_box: Option<&Rect<ComputedLength>>,
611 ) -> Result<(Transform3D<f64>, bool), ()> {
612 Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)
613 }
614
615 fn components_to_transform_3d_matrix_f64(
617 ops: &[T],
618 reference_box: Option<&Rect<ComputedLength>>,
619 ) -> Result<(Transform3D<f64>, bool), ()> {
620 let mut transform = Transform3D::<f64>::identity();
627 let mut contain_3d = false;
628
629 for operation in ops {
630 let matrix = operation.to_3d_matrix(reference_box)?;
631 contain_3d = contain_3d || operation.is_3d();
632 transform = matrix.then(&transform);
633 }
634
635 Ok((transform, contain_3d))
636 }
637}
638
639#[inline]
641pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
642 if d.is_finite() {
643 Transform3D::perspective(d.max(1.))
644 } else {
645 Transform3D::identity()
646 }
647}
648
649pub fn get_normalized_vector_and_angle<T: Zero>(
651 x: CSSFloat,
652 y: CSSFloat,
653 z: CSSFloat,
654 angle: T,
655) -> (CSSFloat, CSSFloat, CSSFloat, T) {
656 use crate::values::computed::transform::DirectionVector;
657 use euclid::approxeq::ApproxEq;
658 let vector = DirectionVector::new(x, y, z);
659 if vector.square_length().approx_eq(&f32::zero()) {
660 (0., 0., 1., T::zero())
664 } else {
665 let vector = vector.robust_normalize();
666 (vector.x, vector.y, vector.z, angle)
667 }
668}
669
670#[derive(
671 Clone,
672 Copy,
673 Debug,
674 Deserialize,
675 MallocSizeOf,
676 PartialEq,
677 Serialize,
678 SpecifiedValueInfo,
679 ToAnimatedValue,
680 ToAnimatedZero,
681 ToComputedValue,
682 ToResolvedValue,
683 ToShmem,
684)]
685#[repr(C, u8)]
686pub enum GenericRotate<Number, Angle> {
690 None,
692 Rotate(Angle),
694 Rotate3D(Number, Number, Number, Angle),
696}
697
698pub use self::GenericRotate as Rotate;
699
700pub trait IsParallelTo {
703 fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
705}
706
707impl<Number, Angle> ToCss for Rotate<Number, Angle>
708where
709 Number: Copy + PartialOrd + ToCss + Zero,
710 Angle: Copy + Neg<Output = Angle> + ToCss + Zero,
711 (Number, Number, Number): IsParallelTo,
712{
713 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
714 where
715 W: fmt::Write,
716 {
717 use crate::values::computed::transform::DirectionVector;
718 match *self {
719 Rotate::None => dest.write_str("none"),
720 Rotate::Rotate(ref angle) => angle.to_css(dest),
721 Rotate::Rotate3D(x, y, z, angle) => {
722 let v = (x, y, z);
731 let (axis, angle) = if x.is_zero() && y.is_zero() && z.is_zero() {
732 (None, angle)
739 } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
740 (Some("x "), if v.0 < Number::zero() { -angle } else { angle })
741 } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
742 (Some("y "), if v.1 < Number::zero() { -angle } else { angle })
743 } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
744 let angle = if v.2 < Number::zero() { -angle } else { angle };
746 return angle.to_css(dest);
747 } else {
748 (None, angle)
749 };
750 match axis {
751 Some(a) => dest.write_str(a)?,
752 None => {
753 x.to_css(dest)?;
754 dest.write_char(' ')?;
755 y.to_css(dest)?;
756 dest.write_char(' ')?;
757 z.to_css(dest)?;
758 dest.write_char(' ')?;
759 },
760 }
761 angle.to_css(dest)
762 },
763 }
764 }
765}
766
767#[derive(
768 Clone,
769 Copy,
770 Debug,
771 Deserialize,
772 MallocSizeOf,
773 PartialEq,
774 Serialize,
775 SpecifiedValueInfo,
776 ToAnimatedValue,
777 ToAnimatedZero,
778 ToComputedValue,
779 ToResolvedValue,
780 ToShmem,
781)]
782#[repr(C, u8)]
783pub enum GenericScale<Number> {
787 None,
789 Scale(Number, Number, Number),
791}
792
793pub use self::GenericScale as Scale;
794
795impl<Number> ToCss for Scale<Number>
796where
797 Number: ToCss + PartialEq + Copy,
798 f32: From<Number>,
799{
800 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
801 where
802 W: fmt::Write,
803 f32: From<Number>,
804 {
805 match *self {
806 Scale::None => dest.write_str("none"),
807 Scale::Scale(ref x, ref y, ref z) => {
808 x.to_css(dest)?;
809
810 let is_3d = f32::from(*z) != 1.0;
811 if is_3d || x != y {
812 dest.write_char(' ')?;
813 y.to_css(dest)?;
814 }
815
816 if is_3d {
817 dest.write_char(' ')?;
818 z.to_css(dest)?;
819 }
820 Ok(())
821 },
822 }
823 }
824}
825
826#[inline]
827fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(
828 _: &LengthPercentage,
829 y: &LengthPercentage,
830 z: &Length,
831) -> bool {
832 y.is_zero_no_percent() && z.is_zero()
833}
834
835#[derive(
836 Clone,
837 Debug,
838 Deserialize,
839 MallocSizeOf,
840 PartialEq,
841 Serialize,
842 SpecifiedValueInfo,
843 ToAnimatedValue,
844 ToAnimatedZero,
845 ToComputedValue,
846 ToCss,
847 ToResolvedValue,
848 ToShmem,
849)]
850#[repr(C, u8)]
851pub enum GenericTranslate<LengthPercentage, Length>
865where
866 LengthPercentage: Zero + ZeroNoPercent,
867 Length: Zero,
868{
869 None,
871 Translate(
873 LengthPercentage,
874 #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
875 #[css(skip_if = "Zero::is_zero")] Length,
876 ),
877}
878
879pub use self::GenericTranslate as Translate;
880
881#[allow(missing_docs)]
882#[derive(
883 Clone,
884 Copy,
885 Debug,
886 MallocSizeOf,
887 Parse,
888 PartialEq,
889 SpecifiedValueInfo,
890 ToComputedValue,
891 ToCss,
892 ToResolvedValue,
893 ToShmem,
894)]
895#[repr(u8)]
896pub enum TransformStyle {
897 Flat,
898 #[css(keyword = "preserve-3d")]
899 Preserve3d,
900}