Align Lottie graphic elements (grouping, geometry, and styles) with Lottie specifications - #2833
Align Lottie graphic elements (grouping, geometry, and styles) with Lottie specifications#2833MarkYav wants to merge 3 commits into
Conversation
### Impact Summary `Group` and `Transform` AST models now strictly align with the canonical Lottie 1.0.1 schema definitions (`#/$defs/shapes/group` and `#/$defs/shapes/transform`). Non-schema properties (`so`, `eo`, `ix`, `mn`, `cix`) and non-spec default property instantiations have been removed, enforcing pure data class declarations with empty bodies.
Establish a common sealed ancestor interface `GeometryShape : GraphicElement` for all parametric and freeform path-generating shapes (`Ellipse`, `Path`, `Rectangle`, `PolyStar`), and bring their AST property models into strict compliance with the Lottie 1.0.1 specification and JSON Schema. ### Motivation & Context Previously, geometry models implemented `GraphicElement` directly without a category abstraction, resulting in duplicate or missing definitions of the curve drawing direction attribute `"d"`.
Establish a common sealed ancestor interface `ShapeStyle : GraphicElement` for all style graphic elements (`Fill`, `Stroke`, `GradientFill`, `GradientStroke`), and align their AST models and serializers strictly with the canonical Lottie 1.0.1 specification and JSON Schema. ### Impact Summary - Introduces sealed `ShapeStyle` category interface exposing the mandatory `opacity: BaseScalarProperty` attribute shared by all fill and stroke styles. - Refactors `Fill` (`"ty": "fl"`): removes non-schema After Effects metadata fields (`ix`, `mn`, `cix`), non-spec blend modes, and enforces strict schema fields: name (`nm`), hidden (`hd`), opacity (`o`), color (`c`), and fill rule (`r` / `FillRule`). - Implements `Stroke` (`"ty": "st"`): supports solid strokes with line cap (`lc` / `LineCap`), line join (`lj` / `LineJoin`), miter limit (`ml`), miter limit alternative (`ml2`), stroke width (`w`), and dash patterns (`d` / `StrokeDash`, `StrokeDashType`). - Implements `GradientFill` (`"ty": "gf"`): supports linear and radial gradients (`t` / `GradientType`), start point (`s`), end point (`e`), gradient colors (`g` / `BaseGradientProperty`), fill rule (`r`), and highlight angle/length (`a`, `h`). - Implements `GradientStroke` (`"ty": "gs"`): combines stroke styling attributes with gradient color properties. - Registers style elements (`"st"`, `"gf"`, `"gs"`) in `GraphicElementSerializer`. - Updates `LottieDecoderResilienceTest` and `MediaLottieDiffScreenshotTest` to reflect stroke support and schema-mandated opacity. ### Motivation Shape style models previously lacked a unifying sealed abstraction, and stroke and gradient style elements (`Stroke`, `GradientFill`, `GradientStroke`) were unsupported in the AST, preventing animations with line strokes and gradients from loading. In addition, existing style models contained non-schema After Effects metadata attributes and non-spec defaults. ### External References - [Lottie Shape Style Specification](https://lottie.github.io/lottie-spec/latest/specs/shapes/#shape-style) - [Lottie Fill Specification](https://lottie.github.io/lottie-spec/1.0.1/specs/shapes/#fill) (`#/$defs/shapes/fill`) - [Lottie Stroke Specification](https://lottie.github.io/lottie-spec/1.0.1/specs/shapes/#stroke) (`#/$defs/shapes/stroke`) - [Lottie Gradient Fill Specification](https://lottie.github.io/lottie-spec/1.0.1/specs/shapes/#gradient-fill) (`#/$defs/shapes/gradient-fill`) - [Lottie Gradient Stroke Specification](https://lottie.github.io/lottie-spec/1.0.1/specs/shapes/#gradient-stroke) (`#/$defs/shapes/gradient-stroke`) - [Lottie Line Cap Specification](https://lottie.github.io/lottie-spec/1.0.1/specs/constants/#line-cap) (`#/$defs/constants/line-cap`) - [Lottie Line Join Specification](https://lottie.github.io/lottie-spec/1.0.1/specs/constants/#line-join) (`#/$defs/constants/line-join`) TAG=agy CONV=93d2d5dc-3a44-44b6-8aa1-9574ae6f2f12
Preview ChangesNote Compared against the baseline rendered from Warning 1 preview(s) failed to render in this PR's render run. The diff below covers only the previews that produced a PNG. Render Failures (1 variant(s) across 1 function(s))The render task completed but produced no PNG for these previews. Common causes: Robolectric sandbox crash,
Unchanged (387 function(s), 576 variant(s))
|
| * Serializer for [GradientType] supporting integer and float primitives with fallback to | ||
| * [GradientType.Linear]. | ||
| */ | ||
| internal object GradientTypeSerializer : KSerializer<GradientType> { |
There was a problem hiding this comment.
I wonder whether there is value in having a EnumTypeSerializer base class?
There was a problem hiding this comment.
That is an excellent idea! I will explore it.
There was a problem hiding this comment.
can be deferred, not essential
| ShapeType.Rectangle.value -> Rectangle.serializer() | ||
| ShapeType.Ellipse.value -> Ellipse.serializer() | ||
| ShapeType.PolyStar.value -> PolyStar.serializer() | ||
| else -> Group.serializer() |
There was a problem hiding this comment.
it this a safe default or does it have required fields? Should be Unknown Shape?
https://lottie.github.io/lottie-spec/latest/specs/schema/#/$defs/shapes/unknown-shape
| * child shapes array. | ||
| * | ||
| * Schema Specification: | ||
| * - Required Fields: |
There was a problem hiding this comment.
Are these required?
I don't see that in the schema?
https://lottie.github.io/lottie-spec/latest/specs/schema/#/$defs/helpers/transform
There was a problem hiding this comment.
It feels like a gap in the schema: these attributes are not required, but no default values are present either. lottie-android player uses default values.
But here I would like to start a discussion: if we decide to go with using default values, should we add them here (in this class), or do the check during rendering. Where should we put this "default" logic?
From the rigorous perspective, I lean towards working with the null values during the renderer. On the other hand, having them here in AST simplifies the logic.
There was a problem hiding this comment.
I think we agreed on defaults of 0
|
|
||
| val cornerRadius = animateScalar(rect.cornerRadius, animationSettings).constantValueOrNull ?: 0f | ||
| val cornerRadius = | ||
| rect.cornerRadius?.let { animateScalar(it, animationSettings).constantValueOrNull } ?: 0f |
There was a problem hiding this comment.
Can we have TODOs for the constantValueOrNull calls throughout?
There was a problem hiding this comment.
This logic is in the renderer/ folder, and the next PRs will be refactoring that. I wanted to make the minimal changes to the classes in the renderer/ folder, and that is the reason for this approach.
| @SerialName("hd") override val hidden: SerializableBoolean? = null, | ||
| @SerialName("ty") override val type: ShapeType = ShapeType.Group, | ||
| @SerialName("np") val numberOfProperties: Int? = null, | ||
| @SerialName("np") val numberOfProperties: SerializableRemoteFloat? = null, |
There was a problem hiding this comment.
Is this one of the index/length cases taht ew should discussed, does not need to be Remote?
There was a problem hiding this comment.
Good catch! Is it a good idea to have the post-hoc approach to keep as much values of Remote type as possible, and if later (during working on the renderer part) we see that we anyway have to work with the regular, non-Remote types -- we can change it back?
WHAT
Refactors and aligns all Lottie graphic elements (GraphicElement) in
:remotecompose:lottiewith the canonical Lottie 1.0.1 Specification and JSON Schema:#/$defs/shapes/groupand#/$defs/shapes/transform.ix,mn,cix,so,eo) and non-spec default object instantiations across all graphic elements.WHY
"ty": "st"), gradient fills ("ty": "gf"), and gradient strokes ("ty": "gs") were not supported in the AST or registered in GraphicElementSerializer. Animations containing stroked paths or gradients failed during parsing or omitted visible lines."d"on geometry shapes and required opacity"o"on shape styles).ix,mn,cix,so,eo) and default instances that did not conform to the Lottie 1.0.1 JSON Schema.HOW
val direction: Int?("d"), supported by ShapeDirection (Normal = 1,Reversed = 3) and fallback resolvershapeDirection.val opacity: BaseScalarProperty("o"), which is required across all style elements.nm,hd,np, and ordered child elementsit(shapes: List<GraphicElement>).a,p,r,s,o) and optional skew distortion (sk,sa), removing obsolete extra opacity properties (so,eo).p,s,d), Path (ks,d), Rectangle (p,s, optionalr,d), and PolyStar (sy,pt,p,r,or,os, optionalir,is,d) with exact schema requirements.nm,hd,o,c, and FillRule (NonZero = 1,EvenOdd = 2).ml,ml2), stroke width (w), and dash arrays (StrokeDash, StrokeDashType).hidden?.constantValue,shapeDirection, and nullablecornerRadius.Checklist 📋