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