Skip to content

Fix legacy 0-255 Bodymovin colours and clamp colour narrowing - #601

Open
gindemit wants to merge 1 commit into
Samsung:masterfrom
gindemit:fix/legacy-colors-and-shape-budget
Open

Fix legacy 0-255 Bodymovin colours and clamp colour narrowing#601
gindemit wants to merge 1 commit into
Samsung:masterfrom
gindemit:fix/legacy-colors-and-shape-budget

Conversation

@gindemit

@gindemit gindemit commented Aug 15, 2026

Copy link
Copy Markdown

Test file

body_movin.json

Legacy 0-255 Bodymovin colours

Bodymovin exporters older than 4.1.9 emit colour components in the 0-255 range instead of the normalized 0-1 range the schema mandates. LottieParserImpl::getValue(model::Color&) stored those values raw, and Color::toColor() then narrowed values such as 255 * 88.0f to uint8_t. That out-of-range float-to-integer conversion is undefined behaviour and renders differently across toolchains.

The bundled body_movin.json sample demonstrates the problem: its banner fill is [88, 214, 112, 255]. Before this fix it renders magenta on Android and purple on Apple platforms instead of the intended green.

This change:

  • normalizes legacy colour values during parsing when an RGB component exceeds 1.0;
  • clamps conversion to uint8_t, including deterministic handling of NaN and out-of-range values.

Verified against body_movin.json: the banner renders RGB(88, 214, 112) with RGB(255, 219, 67) lettering, matching lottie-web 5.12.2.

Gradient stops are not covered by this change because no tested sample combines legacy 0-255 colours with gradients.

The shape-content-budget fix previously included in this draft has been moved into a separate pull request so the two independent changes can be reviewed independently.

@gindemit
gindemit force-pushed the fix/legacy-colors-and-shape-budget branch from 2ced270 to 133ff15 Compare August 16, 2026 05:19
@gindemit gindemit changed the title Fix legacy 0-255 Bodymovin colours and over-charged shape-content budget Fix legacy 0-255 Bodymovin colours and clamp colour narrowing Aug 16, 2026
Bodymovin exporters older than 4.1.9 emit colours in the 0-255 range instead of
the normalized 0-1 range the Lottie schema mandates. The parser stored those raw
values, and Color::toColor() then evaluated uint8_t(255 * r) on them - an
out-of-range float->integer conversion, which is undefined behaviour.

The two toolchains materialized that UB differently, so the same file rendered
different colours per platform: the Android NDK build truncated mod 256 (giving
an inverted palette, since v*255 mod 256 == 256-v), while the Apple clang build
optimized the narrowing away and let each channel's high byte bleed into the
next. Neither matched the intended artwork.

Detect the legacy encoding in getValue(model::Color) and normalize it, matching
what lottie-web and lottie-android do. Additionally clamp in toColor() so any
remaining out-of-range value degrades identically and deterministically on every
target instead of diverging by compiler.

Verified against the bundled body_movin.json (Bodymovin 4.0.0): the banner now
renders RGB(88,214,112) with RGB(255,219,67) lettering, matching lottie-web
5.12.2 exactly; before the fix it was (168,42,144) on Android and (168,127,213)
on macOS.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gindemit
gindemit force-pushed the fix/legacy-colors-and-shape-budget branch from 133ff15 to 9474225 Compare August 16, 2026 11:21
@gindemit
gindemit marked this pull request as ready for review August 16, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant