gioser

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 660Markdown 302TOML 157RON 312382 Sutichaykuna

⎇ main shared/foreign-lottie/src/schema/layers/mod.rs Rust · 349 siq'ikuna · 12.8 KB Ñawpa willay Pi imata llamkarqan ch'uya

chore: refresco desde el monorepo (pin tawasuyu.git v0.3.0) b575c63f1 · Sergio ·

1// Copyright 2024 the Velato Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4pub mod image;
5pub mod layer;
6pub mod null;
7pub mod precomposition;
8pub mod shape;
9pub mod solid;
10pub mod visual;
11
12use self::solid::SolidLayer;
13use self::visual::VisualLayer;
14use image::ImageLayer;
15use null::NullLayer;
16use precomposition::PrecompositionLayer;
17use serde::{Deserialize, Deserializer, Serialize};
18use shape::ShapeLayer;
19
20/// There are several layer types, which is specified by the 'ty' attribute. All
21/// layers share the properties in `layers::common::Properties`.
22#[derive(Serialize, Debug, Clone, PartialEq)]
23#[serde(untagged)]
24pub enum AnyLayer {
25    /// Renders a Precomposition
26    Precomposition(PrecompositionLayer),
27    /// Static rectangle filling the canvas with a single color
28    Solid(SolidLayer),
29    /// No contents, only used for parenting.
30    /// Has an array of shapes
31    Shape(ShapeLayer),
32    /// Null
33    Null(NullLayer),
34    // unimplemented - Text(TextLayer),
35    Image(ImageLayer),
36    /// FORK tawasuyu: cualquier `ty` que este schema no modela — texto (5),
37    /// cámara, luz, ajuste, guía, audio, vídeo. Upstream devolvía
38    /// `Err("unknown layer type")` desde el deserializador, y como serde
39    /// aborta el documento entero al primer error, **un solo layer de texto
40    /// tumbaba el `.json` completo**: no se perdía el rótulo, se perdía la
41    /// animación. Eso contradecía lo que el README de este fork ya prometía
42    /// («degradan a omisión, no a panic») y es el caso más común de un export
43    /// de After Effects, donde una cámara o una luz vienen de fábrica. Ahora
44    /// la capa se reconoce como no soportada y `conv_layer` la saltea, igual
45    /// que a una capa oculta.
46    Unsupported {
47        /// El `ty` que traía, para que un diagnóstico pueda nombrarlo.
48        ty: u64,
49    },
50    // unimplemented - Audio(AudioLayer),
51    // unimplemented - VideoPlaceholder(VideoPlaceholderLayer)
52    // unimplemented - Video(VideoLayer)
53    // unimplemented - ImagePlaceholder(ImagePlaceholderLayer)
54    // unimplemented - Guide(GuideLayer)
55    // unimplemented - Adjustment(AdjustmentLayer)
56    // unimplemented - Camera(CameraLayer)
57    // unimplemented - Light(LightLayer)
58    // unimplemented - Data(DataLayer)
59}
60
61impl<'de> Deserialize<'de> for AnyLayer {
62    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63    where
64        D: Deserializer<'de>,
65    {
66        use serde::de::Error;
67
68        let value = serde_json::Value::deserialize(deserializer)?;
69
70        let layer_type = value
71            .get("ty")
72            .and_then(|v| v.as_u64())
73            .ok_or_else(|| D::Error::missing_field("ty"))?;
74
75        match layer_type {
76            0 => serde_json::from_value(value)
77                .map(AnyLayer::Precomposition)
78                .map_err(D::Error::custom),
79            1 => serde_json::from_value(value)
80                .map(AnyLayer::Solid)
81                .map_err(D::Error::custom),
82            2 => serde_json::from_value(value)
83                .map(AnyLayer::Image)
84                .map_err(D::Error::custom),
85            3 => serde_json::from_value(value)
86                .map(AnyLayer::Null)
87                .map_err(D::Error::custom),
88            4 => serde_json::from_value(value)
89                .map(AnyLayer::Shape)
90                .map_err(D::Error::custom),
91            // FORK tawasuyu: omitir la capa en vez de rechazar el documento.
92            // Ver el comentario de `AnyLayer::Unsupported`.
93            otro => Ok(AnyLayer::Unsupported { ty: otro }),
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100
101    use crate::schema::{
102        animated_properties::{
103            animated_property::{AnimatedProperty, AnimatedPropertyK},
104            multi_dimensional::MultiDimensional,
105            position::{Position, PositionValueK},
106            value::FloatValue,
107        },
108        helpers::{
109            int_boolean::BoolInt,
110            transform::{AnyTransformP, AnyTransformR, Transform},
111            visual_object::VisualObject,
112        },
113        layers::layer::Layer,
114        shapes::{
115            AnyShape, ellipse::EllipseShape, graphic_element::GraphicElementShape,
116            group::GroupShape, shape::Shape,
117        },
118    };
119
120    use super::{AnyLayer, shape::ShapeLayer, visual::VisualLayer};
121    use serde_json::json;
122    use std::sync::LazyLock;
123
124    static JSON: LazyLock<serde_json::Value> = LazyLock::new(|| {
125        json!(
126            {
127                "ddd": 0,
128                "ty": 4,
129                "ind": 1,
130                "st": 0,
131                "ip": 0,
132                "op": 180,
133                "nm": "Ellipse",
134                "mn": "{0a36d01c-18e1-48d3-8e8f-cc093b3f24ba}",
135                "ks": {
136                    "a": {
137                        "a": 0,
138                        "k": [
139                            256,
140                            256
141                        ]
142                    },
143                    "p": {
144                        "a": 0,
145                        "k": [
146                            256,
147                            256
148                        ]
149                    },
150                    "s": {
151                        "a": 0,
152                        "k": [
153                            100,
154                            100
155                        ]
156                    },
157                    "r": {
158                        "a": 0,
159                        "k": 0
160                    },
161                    "o": {
162                        "a": 0,
163                        "k": 100
164                    }
165                },
166                "shapes": [
167                    {
168                        "ty": "gr",
169                        "nm": "Group",
170                        "mn": "{f1becc2a-49f0-4f0c-918f-bdffe4c6870f}",
171                        "it": [
172                            {
173                                "ty": "el",
174                                "nm": "Ellipse",
175                                "mn": "{2aabac6e-1dd8-41b0-b60b-baf75ccb6318}",
176                                "p": {
177                                    "a": 0,
178                                    "k": [
179                                        303.9044776119403,
180                                        324.9671641791045
181                                    ]
182                                },
183                                "s": {
184                                    "a": 0,
185                                    "k": [
186                                        205.46865671641788,
187                                        204.6089552238806
188                                    ]
189                                }
190                            }
191                        ]
192                    }
193                ]
194            }
195        )
196    });
197
198    #[expect(deprecated, reason = "Uses deprecated attributes")]
199    static LAYER: LazyLock<AnyLayer> = LazyLock::new(|| {
200        AnyLayer::Shape(ShapeLayer {
201            visual_layer: VisualLayer {
202                layer: Layer {
203                    visual_object: VisualObject {
204                        name: Some("Ellipse".to_string()),
205                        match_name: Some("{0a36d01c-18e1-48d3-8e8f-cc093b3f24ba}".to_string()),
206                    },
207                    layer_type: 4,
208                    three_dimensional: Some(BoolInt::False),
209                    index: Some(1),
210                    start_time: Some(0.0),
211                    in_point: 0.0,
212                    out_point: 180.0,
213                    hidden: None,
214                    parent_index: None,
215                    time_stretch: None,
216                },
217                transform: Transform {
218                    anchor_point: Some(Position {
219                        property_index: None,
220                        animated: Some(BoolInt::False),
221                        expression: None,
222                        length: None,
223                        value: PositionValueK::Static(vec![256.0, 256.0]),
224                    }),
225                    position: AnyTransformP::Position(Position {
226                        property_index: None,
227                        animated: Some(BoolInt::False),
228                        expression: None,
229                        length: None,
230                        value: PositionValueK::Static(vec![256.0, 256.0]),
231                    }),
232                    scale: Some(MultiDimensional {
233                        animated_property: AnimatedProperty {
234                            animated: Some(BoolInt::False),
235                            property_index: None,
236                            expression: None,
237                            slot_id: None,
238                            value: AnimatedPropertyK::Static(vec![100.0, 100.0]),
239                        },
240                        length: None,
241                    }),
242                    rotation: Some(AnyTransformR::Rotation(FloatValue {
243                        animated_property: AnimatedProperty {
244                            animated: Some(BoolInt::False),
245                            property_index: None,
246                            expression: None,
247                            slot_id: None,
248                            value: AnimatedPropertyK::Static(0.0),
249                        },
250                    })),
251                    opacity: Some(FloatValue {
252                        animated_property: AnimatedProperty {
253                            animated: Some(BoolInt::False),
254                            property_index: None,
255                            expression: None,
256                            slot_id: None,
257                            value: AnimatedPropertyK::Static(100.0),
258                        },
259                    }),
260                    skew: None,
261                    skew_axis: None,
262                },
263                matte_mode: None,
264                matte_target: None,
265                masks_properties: None,
266                rotate_to_match_anim_pos_path: None,
267                matte_layer_index: None,
268                has_mask: None,
269                motion_blur: None,
270                blend_mode: None,
271                css_class: None,
272                id: None,
273                tag_name: None,
274                tranform_before_mask_deprecated: None,
275                transform_before_mask: None,
276            },
277            shapes: vec![AnyShape::Group(GroupShape {
278                graphic_element: GraphicElementShape {
279                    visual_object: VisualObject {
280                        name: Some("Group".to_string()),
281                        match_name: Some("{f1becc2a-49f0-4f0c-918f-bdffe4c6870f}".to_string()),
282                    },
283                    index: None,
284                    hidden: None,
285                    blend_mode: None,
286                    property_index: None,
287                    css_class: None,
288                    xml_id: None,
289                },
290                num_properties: None,
291                property_index: None,
292                shapes: vec![AnyShape::Ellipse(EllipseShape {
293                    shape: Shape {
294                        graphic_element: GraphicElementShape {
295                            visual_object: VisualObject {
296                                name: Some("Ellipse".to_string()),
297                                match_name: Some(
298                                    "{2aabac6e-1dd8-41b0-b60b-baf75ccb6318}".to_string(),
299                                ),
300                            },
301                            index: None,
302                            hidden: None,
303                            blend_mode: None,
304                            property_index: None,
305                            css_class: None,
306                            xml_id: None,
307                        },
308                        direction: None,
309                    },
310                    position: Position {
311                        property_index: None,
312                        animated: Some(BoolInt::False),
313                        expression: None,
314                        length: None,
315                        value: PositionValueK::Static(vec![303.9044776119403, 324.9671641791045]),
316                    },
317                    size: MultiDimensional {
318                        animated_property: AnimatedProperty {
319                            animated: Some(BoolInt::False),
320                            property_index: None,
321                            expression: None,
322                            slot_id: None,
323                            value: AnimatedPropertyK::Static(vec![
324                                205.46865671641788,
325                                204.6089552238806,
326                            ]),
327                        },
328                        length: None,
329                    },
330                })],
331            })],
332        })
333    });
334
335    #[test]
336    fn test_deserialize() {
337        let actual = serde_json::from_value(JSON.to_owned());
338
339        match actual {
340            Ok(actual) => assert_eq!(*LAYER, actual),
341            Err(e) => panic!("{e}"),
342        }
343    }
344
345    #[test]
346    fn test_can_serialize() {
347        serde_json::to_value(&*LAYER).unwrap();
348    }
349}