Add Lottie/Bodymovin support to the build-time SVG transcoder#5066
Merged
Conversation
Collaborator
Author
|
Compared 47 screenshots: 47 matched. |
Contributor
Cloudflare Preview
|
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Contributor
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Collaborator
Author
|
Compared 117 screenshots: 117 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Collaborator
Author
|
Compared 117 screenshots: 117 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Collaborator
Author
|
Compared 117 screenshots: 117 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
7506756 to
89d6ec2
Compare
Lottie / Bodymovin JSON (.json, .lottie) files flow through the same
build-time pipeline as SVG: each source file is lowered into the
existing SVGDocument model, the SVG JavaCodeGenerator emits the
GeneratedSVGImage subclass, and the same SVGRegistry registers it
under its source filename. No new Image base class, no new registry,
no per-port wiring -- the SVG path's JavaSE reflective load and the
iOS / Android Stub weaving already cover the new format.
New maven/lottie-transcoder/ module parses Bodymovin JSON with no
external deps (built-in JSON parser): shape layers with grouped
rectangle / ellipse / bezier path primitives, solid fills and strokes,
layer transforms (anchor / position / scale / rotation / opacity),
and animated rotation / position / scale collapsed to a 2-keyframe
loop. Solid-color layers (ty:1) lower to a filled rect.
TranscodeSVGMojo now scans src/main/{svg,lottie,css}/ for
.svg / .json / .lottie and dispatches each file to the right
transcoder. Resources.registerGeneratedImage strips any extension
(not just .svg) so getImage("home") resolves regardless of source
format.
Hellocodenameone picks up sample lottie_spinner.json and
lottie_pulse.json (proper Bodymovin exports -- open and play in
lottieeditor.app) plus LottieAnimatedScreenshotTest registered next
to the SVG screenshot tests, with the same HTML5 skip list.
docs/developer-guide/SVG-Transcoder.asciidoc gains a Lottie section
with the feature matrix and troubleshooting. The initializr skill's
references/css.md picks up the same material so generated projects'
Claude Code skill stays accurate.
Lottie test coverage (17 cases):
- animated translate / scale / rotation
- bezier path "sh" shape with closed cycle
- multi-shape fill propagation within a single gr group
- stroke color + width extraction from "st"
- multi-layer paint order (Lottie back-to-front -> SVG front-to-back)
- solid-color layer (ty:1) lowered to a filled rect
- unsupported layer type producing an empty group
- opacity below 100% baked into the layer style as a 0..1 fraction
- RGBA 0..1 -> ARGB int normalization with edge values
- ellipse codegen path through gr/tr wrapping
- missing top-level dimensions fall back to 100x100
- static-only layers don't emit any animation entries
- realistic Bodymovin export (3D vectors, ix indices, gr/tr wrapping)
- JSON parser primitives
- transcodesToCompilableJava round-trip
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
89d6ec2 to
bf48b7b
Compare
Captured from the first CI run that included the new test. Android and iOS-Metal both show the expected animation: red rotating spinner and blue pulsing ellipse across the six progress samples (0%, 20%, 40%, 60%, 80%, 100%) the AbstractAnimationScreenshotTest grid renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 29, 2026
shai-almog
added a commit
that referenced
this pull request
May 29, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 29, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 29, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 29, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 29, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 29, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 30, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
shai-almog
added a commit
that referenced
this pull request
May 30, 2026
Final consolidated follow-up to the May 29 weekly index. Pulls together six PRs that share the same architectural shape: emit Java at build time, validate at build time, fail fast, and let R8 / ParparVM rename the generated code together with the rest of the app. - PR #5037: bytecode AnnotationProcessor SPI in the Maven plugin, the declarative router that is its first consumer (@route with guards, redirects, per-tab navigation shell, location listeners), the unified cold + warm DeepLink API, iOS Universal Links / Android App Links JSON generators, and the JavaScript-port window.history bridge. - PR #5047: three more processors on the same SPI -- SQLite ORM (@entity / @id / @column), JSON / XML mapping (@mapped / @JsonProperty / @xmlelement), and component binding (@bindable / @Bind with the new BindAttr enum). - PR #5062: validation annotations (@required, @Length, @regex, @Email, @url, @Numeric, @existin, @Validate) that compose with @Bind and surface through Binding.getValidator(). - PR #5055: the Immich-port baseline -- Map default methods, BiFunction, atomics, Rest.fetchAsJsonList / fetchAsMapped(List), URLImage.RequestDecorator / setDefaultBearerToken, JSONWriter, modern animated tab indicator + arc-spinner pull-to-refresh, MorphTransition.snapshotMode, WebSocket in core, and the new cn1:generate-openapi-client mojo. - PR #5042: build-time SVG transcoder that lowers SVG (and SMIL animations) into Codename One Image subclasses via the shape API. - PR #5066: Lottie / Bodymovin transcoder reusing the same SVGDocument model, JavaCodeGenerator, and SVGRegistry. - PR #5049: the iOS Metal stencil-clip + drawString and Android LinearGradientPaint fixes the SVG screenshot tests exposed. Calls out the Metal-only caveat on iOS for SVG / Lottie (the GL ES 2 path does not have the shape coverage) -- a non-issue on most apps now that Metal is the default.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.json,.lottie) files flow through the same build-time pipeline as SVG: each source file is lowered into the existingSVGDocumentmodel, the SVGJavaCodeGeneratoremits theGeneratedSVGImagesubclass, and the sameSVGRegistryregisters it under its source filename. No newImagebase class, no new registry, no per-port wiring — the SVG path's JavaSE reflective load and iOS / Android Stub weaving already cover the new format.maven/lottie-transcoder/module parses Bodymovin JSON with no external deps (built-in JSON parser): shape layers (rc/el/sh) with solid fills and strokes, layer transforms (anchor / position / scale / rotation / opacity), and animated rotation / position / scale collapsed to a 2-keyframe loop. Solid-color layers (ty:1) lower to a filled rect. Documented coverage matrix lives inSVG-Transcoder.asciidoc.TranscodeSVGMojonow scanssrc/main/{svg,lottie,css}/for.svg|.json|.lottieand dispatches each file to the right transcoder.Resources.registerGeneratedImagestrips any extension (not just.svg) sogetImage("home")resolves regardless of source format.lottie_spinner.jsonandlottie_pulse.json(proper Bodymovin exports — open and play in lottieeditor.app) plusLottieAnimatedScreenshotTestregistered next to the SVG screenshot tests (HTML5 skip list included).docs/developer-guide/SVG-Transcoder.asciidocgains a Lottie section with the feature matrix and troubleshooting. The initializr skill'sreferences/css.mdpicks up the same material so generated projects' Claude Code skill stays accurate.Test plan
mvn testonsvg-transcoder(74 passing, no regressions)mvn testonlottie-transcoder(17 tests passing: animated translate / scale / rotation, beziershpaths, multi-shape fill propagation, stroke extraction, multi-layer paint order, solid-color layers, unsupported-layer-type fallback, opacity baked into style, RGBA 0..1 → ARGB normalization, ellipse codegen throughgr/trwrapping, missing top-level dimensions, static-layer no-op, real Bodymovin export with 3D vectors +ixindices, JSON parser, and round-triptranscodesToCompilableJava)process-classes+package: transcoders ran on the two Lottie files plus existing SVGs, the generated Java compiled,bytecode-compliancepassed,SVGRegistry.installGlobal()registerslottie_pulse.jsonandlottie_spinner.jsonunder their CSS-declared mm dimensions.jsonso the CSS compiler'surl(...)resolution succeedsixindices,gr/trwrapping, layerind/ddd/bm/sr/ao/st, rootassets/markers)scripts/{ios,android}/screenshots/LottieAnimatedScreenshotTest.png— produced by the CI screenshot pipeline once the test is registered inCn1ssDeviceRunner🤖 Generated with Claude Code