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 ToTyped,
120)]
121#[repr(C)]
122pub struct GenericTransformOrigin<H, V, Depth> {
123 pub horizontal: H,
125 pub vertical: V,
127 pub depth: Depth,
129}
130
131pub use self::GenericTransformOrigin as TransformOrigin;
132
133impl<H, V, D> TransformOrigin<H, V, D> {
134 pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
136 Self {
137 horizontal,
138 vertical,
139 depth,
140 }
141 }
142}
143
144fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
145 x == y
146}
147
148#[derive(
151 Clone,
152 Debug,
153 Deserialize,
154 MallocSizeOf,
155 PartialEq,
156 Serialize,
157 SpecifiedValueInfo,
158 ToAnimatedValue,
159 ToComputedValue,
160 ToCss,
161 ToResolvedValue,
162 ToShmem,
163)]
164#[repr(C, u8)]
165pub enum GenericPerspectiveFunction<L> {
166 None,
168 Length(L),
170}
171
172impl<L> GenericPerspectiveFunction<L> {
173 pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
175 match *self {
176 Self::None => f32::INFINITY,
177 Self::Length(ref l) => f(l),
178 }
179 }
180}
181
182pub use self::GenericPerspectiveFunction as PerspectiveFunction;
183
184#[derive(
185 Clone,
186 Debug,
187 Deserialize,
188 MallocSizeOf,
189 PartialEq,
190 Serialize,
191 SpecifiedValueInfo,
192 ToAnimatedValue,
193 ToComputedValue,
194 ToCss,
195 ToResolvedValue,
196 ToShmem,
197)]
198#[repr(C, u8)]
199pub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>
201where
202 Angle: Zero,
203 LengthPercentage: Zero + ZeroNoPercent,
204 Number: PartialEq,
205{
206 Matrix(GenericMatrix<Number>),
208 Matrix3D(GenericMatrix3D<Number>),
210 #[css(comma, function)]
216 Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
217 #[css(function = "skewX")]
219 SkewX(Angle),
220 #[css(function = "skewY")]
222 SkewY(Angle),
223 #[css(comma, function)]
225 Translate(
226 LengthPercentage,
227 #[css(skip_if = "ZeroNoPercent::is_zero_no_percent")] LengthPercentage,
228 ),
229 #[css(function = "translateX")]
231 TranslateX(LengthPercentage),
232 #[css(function = "translateY")]
234 TranslateY(LengthPercentage),
235 #[css(function = "translateZ")]
237 TranslateZ(Length),
238 #[css(comma, function = "translate3d")]
240 Translate3D(LengthPercentage, LengthPercentage, Length),
241 #[css(comma, function)]
245 Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
246 #[css(function = "scaleX")]
248 ScaleX(Number),
249 #[css(function = "scaleY")]
251 ScaleY(Number),
252 #[css(function = "scaleZ")]
254 ScaleZ(Number),
255 #[css(comma, function = "scale3d")]
257 Scale3D(Number, Number, Number),
258 #[css(function)]
262 Rotate(Angle),
263 #[css(function = "rotateX")]
265 RotateX(Angle),
266 #[css(function = "rotateY")]
268 RotateY(Angle),
269 #[css(function = "rotateZ")]
271 RotateZ(Angle),
272 #[css(comma, function = "rotate3d")]
276 Rotate3D(Number, Number, Number, Angle),
277 #[css(function)]
284 Perspective(GenericPerspectiveFunction<Length>),
285 #[allow(missing_docs)]
287 #[css(comma, function = "interpolatematrix")]
288 InterpolateMatrix {
289 from_list: GenericTransform<
290 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
291 >,
292 to_list: GenericTransform<
293 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
294 >,
295 progress: computed::Percentage,
296 },
297 #[allow(missing_docs)]
299 #[css(comma, function = "accumulatematrix")]
300 AccumulateMatrix {
301 from_list: GenericTransform<
302 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
303 >,
304 to_list: GenericTransform<
305 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
306 >,
307 count: Integer,
308 },
309}
310
311pub use self::GenericTransformOperation as TransformOperation;
312
313#[derive(
314 Clone,
315 Debug,
316 Deserialize,
317 MallocSizeOf,
318 PartialEq,
319 Serialize,
320 SpecifiedValueInfo,
321 ToAnimatedValue,
322 ToComputedValue,
323 ToCss,
324 ToResolvedValue,
325 ToShmem,
326 ToTyped,
327)]
328#[repr(C)]
329pub struct GenericTransform<T>(#[css(if_empty = "none", iterable)] pub crate::OwnedSlice<T>);
331
332pub use self::GenericTransform as Transform;
333
334impl<Angle, Number, Length, Integer, LengthPercentage>
335 TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
336where
337 Angle: Zero,
338 LengthPercentage: Zero + ZeroNoPercent,
339 Number: PartialEq,
340{
341 pub fn is_rotate(&self) -> bool {
343 use self::TransformOperation::*;
344 matches!(
345 *self,
346 Rotate(..) | Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..)
347 )
348 }
349
350 pub fn is_translate(&self) -> bool {
352 use self::TransformOperation::*;
353 match *self {
354 Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {
355 true
356 },
357 _ => false,
358 }
359 }
360
361 pub fn is_scale(&self) -> bool {
363 use self::TransformOperation::*;
364 match *self {
365 Scale(..) | Scale3D(..) | ScaleX(..) | ScaleY(..) | ScaleZ(..) => true,
366 _ => false,
367 }
368 }
369}
370
371pub trait ToAbsoluteLength {
373 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;
375}
376
377impl ToAbsoluteLength for SpecifiedLength {
378 #[inline]
382 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
383 match *self {
384 SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),
385 SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
386 }
387 }
388}
389
390impl ToAbsoluteLength for SpecifiedLengthPercentage {
391 #[inline]
395 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
396 use self::SpecifiedLengthPercentage::*;
397 match *self {
398 Length(len) => len.to_computed_pixel_length_without_context(),
399 Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
400 Percentage(..) => Err(()),
401 }
402 }
403}
404
405impl ToAbsoluteLength for ComputedLength {
406 #[inline]
407 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
408 Ok(self.px())
409 }
410}
411
412impl ToAbsoluteLength for ComputedLengthPercentage {
413 #[inline]
414 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
415 Ok(self
416 .maybe_percentage_relative_to(containing_len)
417 .ok_or(())?
418 .px())
419 }
420}
421
422pub trait ToMatrix {
424 fn is_3d(&self) -> bool;
426
427 fn to_3d_matrix(
429 &self,
430 reference_box: Option<&Rect<ComputedLength>>,
431 ) -> Result<Transform3D<f64>, ()>;
432}
433
434pub trait ToRadians {
436 fn radians64(&self) -> f64;
438}
439
440impl ToRadians for computed::angle::Angle {
441 #[inline]
442 fn radians64(&self) -> f64 {
443 computed::angle::Angle::radians64(self)
444 }
445}
446
447impl ToRadians for SpecifiedAngle {
448 #[inline]
449 fn radians64(&self) -> f64 {
450 computed::angle::Angle::from_degrees(self.degrees()).radians64()
451 }
452}
453
454impl<Angle, Number, Length, Integer, LoP> ToMatrix
455 for TransformOperation<Angle, Number, Length, Integer, LoP>
456where
457 Angle: Zero + ToRadians + Copy,
458 Number: PartialEq + Copy + Into<f32> + Into<f64>,
459 Length: ToAbsoluteLength,
460 LoP: Zero + ToAbsoluteLength + ZeroNoPercent,
461{
462 #[inline]
463 fn is_3d(&self) -> bool {
464 use self::TransformOperation::*;
465 match *self {
466 Translate3D(..) | TranslateZ(..) | Rotate3D(..) | RotateX(..) | RotateY(..)
467 | RotateZ(..) | Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,
468 _ => false,
469 }
470 }
471
472 #[inline]
477 fn to_3d_matrix(
478 &self,
479 reference_box: Option<&Rect<ComputedLength>>,
480 ) -> Result<Transform3D<f64>, ()> {
481 use self::TransformOperation::*;
482
483 let reference_width = reference_box.map(|v| v.size.width);
484 let reference_height = reference_box.map(|v| v.size.height);
485 let matrix = match *self {
486 Rotate3D(ax, ay, az, theta) => {
487 let theta = theta.radians64();
488 let (ax, ay, az, theta) =
489 get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
490 Transform3D::rotation(
491 ax as f64,
492 ay as f64,
493 az as f64,
494 euclid::Angle::radians(theta),
495 )
496 },
497 RotateX(theta) => {
498 let theta = euclid::Angle::radians(theta.radians64());
499 Transform3D::rotation(1., 0., 0., theta)
500 },
501 RotateY(theta) => {
502 let theta = euclid::Angle::radians(theta.radians64());
503 Transform3D::rotation(0., 1., 0., theta)
504 },
505 RotateZ(theta) | Rotate(theta) => {
506 let theta = euclid::Angle::radians(theta.radians64());
507 Transform3D::rotation(0., 0., 1., theta)
508 },
509 Perspective(ref p) => {
510 let px = match p {
511 PerspectiveFunction::None => f32::INFINITY,
512 PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
513 };
514 create_perspective_matrix(px).cast()
515 },
516 Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
517 Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
518 ScaleX(s) => Transform3D::scale(s.into(), 1., 1.),
519 ScaleY(s) => Transform3D::scale(1., s.into(), 1.),
520 ScaleZ(s) => Transform3D::scale(1., 1., s.into()),
521 Translate3D(ref tx, ref ty, ref tz) => {
522 let tx = tx.to_pixel_length(reference_width)? as f64;
523 let ty = ty.to_pixel_length(reference_height)? as f64;
524 Transform3D::translation(tx, ty, tz.to_pixel_length(None)? as f64)
525 },
526 Translate(ref tx, ref ty) => {
527 let tx = tx.to_pixel_length(reference_width)? as f64;
528 let ty = ty.to_pixel_length(reference_height)? as f64;
529 Transform3D::translation(tx, ty, 0.)
530 },
531 TranslateX(ref t) => {
532 let t = t.to_pixel_length(reference_width)? as f64;
533 Transform3D::translation(t, 0., 0.)
534 },
535 TranslateY(ref t) => {
536 let t = t.to_pixel_length(reference_height)? as f64;
537 Transform3D::translation(0., t, 0.)
538 },
539 TranslateZ(ref z) => Transform3D::translation(0., 0., z.to_pixel_length(None)? as f64),
540 Skew(theta_x, theta_y) => Transform3D::skew(
541 euclid::Angle::radians(theta_x.radians64()),
542 euclid::Angle::radians(theta_y.radians64()),
543 ),
544 SkewX(theta) => Transform3D::skew(
545 euclid::Angle::radians(theta.radians64()),
546 euclid::Angle::radians(0.),
547 ),
548 SkewY(theta) => Transform3D::skew(
549 euclid::Angle::radians(0.),
550 euclid::Angle::radians(theta.radians64()),
551 ),
552 Matrix3D(m) => m.into(),
553 Matrix(m) => m.into(),
554 InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
555 Transform3D::identity()
562 },
563 };
564 Ok(matrix)
565 }
566}
567
568impl<T> Transform<T> {
569 pub fn none() -> Self {
571 Transform(Default::default())
572 }
573}
574
575impl<T: ToMatrix> Transform<T> {
576 #[cfg_attr(rustfmt, rustfmt_skip)]
581 pub fn to_transform_3d_matrix(
582 &self,
583 reference_box: Option<&Rect<ComputedLength>>
584 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
585 Self::components_to_transform_3d_matrix(&self.0, reference_box)
586 }
587
588 #[cfg_attr(rustfmt, rustfmt_skip)]
590 pub fn components_to_transform_3d_matrix(
591 ops: &[T],
592 reference_box: Option<&Rect<ComputedLength>>,
593 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
594 let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
595 use std::{f32, f64};
596 let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;
597 Transform3D::new(
598 cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
599 cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
600 cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
601 cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
602 )
603 };
604
605 let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;
606 Ok((cast_3d_transform(m), is_3d))
607 }
608
609 pub fn to_transform_3d_matrix_f64(
611 &self,
612 reference_box: Option<&Rect<ComputedLength>>,
613 ) -> Result<(Transform3D<f64>, bool), ()> {
614 Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)
615 }
616
617 fn components_to_transform_3d_matrix_f64(
619 ops: &[T],
620 reference_box: Option<&Rect<ComputedLength>>,
621 ) -> Result<(Transform3D<f64>, bool), ()> {
622 let mut transform = Transform3D::<f64>::identity();
629 let mut contain_3d = false;
630
631 for operation in ops {
632 let matrix = operation.to_3d_matrix(reference_box)?;
633 contain_3d = contain_3d || operation.is_3d();
634 transform = matrix.then(&transform);
635 }
636
637 Ok((transform, contain_3d))
638 }
639}
640
641#[inline]
643pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
644 if d.is_finite() {
645 Transform3D::perspective(d.max(1.))
646 } else {
647 Transform3D::identity()
648 }
649}
650
651pub fn get_normalized_vector_and_angle<T: Zero>(
653 x: CSSFloat,
654 y: CSSFloat,
655 z: CSSFloat,
656 angle: T,
657) -> (CSSFloat, CSSFloat, CSSFloat, T) {
658 use crate::values::computed::transform::DirectionVector;
659 use euclid::approxeq::ApproxEq;
660 let vector = DirectionVector::new(x, y, z);
661 if vector.square_length().approx_eq(&f32::zero()) {
662 (0., 0., 1., T::zero())
666 } else {
667 let vector = vector.robust_normalize();
668 (vector.x, vector.y, vector.z, angle)
669 }
670}
671
672#[derive(
673 Clone,
674 Copy,
675 Debug,
676 Deserialize,
677 MallocSizeOf,
678 PartialEq,
679 Serialize,
680 SpecifiedValueInfo,
681 ToAnimatedValue,
682 ToAnimatedZero,
683 ToComputedValue,
684 ToResolvedValue,
685 ToShmem,
686 ToTyped,
687)]
688#[repr(C, u8)]
689pub enum GenericRotate<Number, Angle> {
693 None,
695 Rotate(Angle),
697 Rotate3D(Number, Number, Number, Angle),
699}
700
701pub use self::GenericRotate as Rotate;
702
703pub trait IsParallelTo {
706 fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
708}
709
710impl<Number, Angle> ToCss for Rotate<Number, Angle>
711where
712 Number: Copy + PartialOrd + ToCss + Zero,
713 Angle: Copy + Neg<Output = Angle> + ToCss + Zero,
714 (Number, Number, Number): IsParallelTo,
715{
716 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
717 where
718 W: fmt::Write,
719 {
720 use crate::values::computed::transform::DirectionVector;
721 match *self {
722 Rotate::None => dest.write_str("none"),
723 Rotate::Rotate(ref angle) => angle.to_css(dest),
724 Rotate::Rotate3D(x, y, z, angle) => {
725 let v = (x, y, z);
734 let (axis, angle) = if x.is_zero() && y.is_zero() && z.is_zero() {
735 (None, angle)
742 } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
743 (
744 Some("x "),
745 if v.0 < Number::zero() { -angle } else { angle },
746 )
747 } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
748 (
749 Some("y "),
750 if v.1 < Number::zero() { -angle } else { angle },
751 )
752 } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
753 let angle = if v.2 < Number::zero() { -angle } else { angle };
755 return angle.to_css(dest);
756 } else {
757 (None, angle)
758 };
759 match axis {
760 Some(a) => dest.write_str(a)?,
761 None => {
762 x.to_css(dest)?;
763 dest.write_char(' ')?;
764 y.to_css(dest)?;
765 dest.write_char(' ')?;
766 z.to_css(dest)?;
767 dest.write_char(' ')?;
768 },
769 }
770 angle.to_css(dest)
771 },
772 }
773 }
774}
775
776#[derive(
777 Clone,
778 Copy,
779 Debug,
780 Deserialize,
781 MallocSizeOf,
782 PartialEq,
783 Serialize,
784 SpecifiedValueInfo,
785 ToAnimatedValue,
786 ToAnimatedZero,
787 ToComputedValue,
788 ToResolvedValue,
789 ToShmem,
790 ToTyped,
791)]
792#[repr(C, u8)]
793pub enum GenericScale<Number> {
797 None,
799 Scale(Number, Number, Number),
801}
802
803pub use self::GenericScale as Scale;
804
805impl<Number> ToCss for Scale<Number>
806where
807 Number: ToCss + PartialEq + Copy,
808 f32: From<Number>,
809{
810 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
811 where
812 W: fmt::Write,
813 f32: From<Number>,
814 {
815 match *self {
816 Scale::None => dest.write_str("none"),
817 Scale::Scale(ref x, ref y, ref z) => {
818 x.to_css(dest)?;
819
820 let is_3d = f32::from(*z) != 1.0;
821 if is_3d || x != y {
822 dest.write_char(' ')?;
823 y.to_css(dest)?;
824 }
825
826 if is_3d {
827 dest.write_char(' ')?;
828 z.to_css(dest)?;
829 }
830 Ok(())
831 },
832 }
833 }
834}
835
836#[inline]
837fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(
838 _: &LengthPercentage,
839 y: &LengthPercentage,
840 z: &Length,
841) -> bool {
842 y.is_zero_no_percent() && z.is_zero()
843}
844
845#[derive(
846 Clone,
847 Debug,
848 Deserialize,
849 MallocSizeOf,
850 PartialEq,
851 Serialize,
852 SpecifiedValueInfo,
853 ToAnimatedValue,
854 ToAnimatedZero,
855 ToComputedValue,
856 ToCss,
857 ToResolvedValue,
858 ToShmem,
859 ToTyped,
860)]
861#[repr(C, u8)]
862pub enum GenericTranslate<LengthPercentage, Length>
876where
877 LengthPercentage: Zero + ZeroNoPercent,
878 Length: Zero,
879{
880 None,
882 Translate(
884 LengthPercentage,
885 #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
886 #[css(skip_if = "Zero::is_zero")] Length,
887 ),
888}
889
890pub use self::GenericTranslate as Translate;
891
892#[allow(missing_docs)]
893#[derive(
894 Clone,
895 Copy,
896 Debug,
897 MallocSizeOf,
898 Parse,
899 PartialEq,
900 SpecifiedValueInfo,
901 ToComputedValue,
902 ToCss,
903 ToResolvedValue,
904 ToShmem,
905 ToTyped,
906)]
907#[repr(u8)]
908pub enum TransformStyle {
909 Flat,
910 #[css(keyword = "preserve-3d")]
911 Preserve3d,
912}