1use crate::{Animated, ClipId, Vec2};
4use serde::{Deserialize, Serialize};
5
6pub const MAX_CLIP_MASKS: usize = 8;
8
9#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
17#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
18#[serde(deny_unknown_fields)]
19pub struct ShapeMask {
20 pub shape: MaskShape,
22 pub center: Animated<Vec2>,
24 pub size: Animated<Vec2>,
26 #[serde(default = "zero")]
28 pub rotation_degrees: Animated<f32>,
29 #[serde(default = "zero")]
32 pub feather: Animated<f32>,
33 #[serde(default)]
35 pub invert: bool,
36 #[serde(default)]
38 pub mode: MaskMode,
39}
40
41fn zero() -> Animated<f32> {
42 Animated::Constant { value: 0.0 }
43}
44
45#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
47#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
48#[serde(tag = "type", rename_all = "snake_case", from = "MaskShapeWire")]
49pub enum MaskShape {
50 Rectangle {
52 corner_radius: Animated<f32>,
55 },
56 Ellipse,
58}
59
60#[derive(Deserialize)]
63#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
64#[cfg_attr(feature = "schema", schemars(rename = "MaskShape"))]
65#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
66pub(crate) enum MaskShapeWire {
67 Rectangle {
69 #[serde(default = "zero")]
72 corner_radius: Animated<f32>,
73 },
74 Ellipse {},
76}
77
78impl From<MaskShapeWire> for MaskShape {
79 fn from(wire: MaskShapeWire) -> Self {
80 match wire {
81 MaskShapeWire::Rectangle { corner_radius } => Self::Rectangle { corner_radius },
82 MaskShapeWire::Ellipse {} => Self::Ellipse,
83 }
84 }
85}
86
87#[derive(Copy, Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
92#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
93#[serde(rename_all = "snake_case")]
94pub enum MaskMode {
95 #[default]
97 Add,
98 Subtract,
100 Intersect,
102}
103
104#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
110#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
111#[serde(deny_unknown_fields)]
112pub struct Matte {
113 pub source: ClipId,
115 pub mode: MatteMode,
117}
118
119#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
121#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
122#[serde(rename_all = "snake_case")]
123pub enum MatteMode {
124 Alpha,
126 AlphaInverted,
128 Luma,
130 LumaInverted,
132}
133
134impl ShapeMask {
135 pub(crate) fn is_valid(&self) -> bool {
136 let non_negative = |animation: &Animated<f32>| {
137 crate::animation::valid_scalar_values(animation, |value| value >= 0.0)
138 };
139 let sizes = crate::animation::valid_vec2_values(&self.size, |value| {
140 value.x >= 0.0 && value.y >= 0.0
141 });
142 let shape = match &self.shape {
143 MaskShape::Rectangle { corner_radius } => non_negative(corner_radius),
144 MaskShape::Ellipse => true,
145 };
146 shape
147 && sizes
148 && crate::animation::valid_vec2_values(&self.center, |_| true)
149 && crate::animation::valid_scalar_values(&self.rotation_degrees, |_| true)
150 && non_negative(&self.feather)
151 }
152}