googlefonts / googlefonts/fontc
[COLR] IR stores resolved colors instead of palette indices, lossy for duplicate CPAL entries
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 193
- Forks
- 21
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 65
Description
The IR represents color layer fills as resolved Color values (PaintSolid { color: Option<Color> }), not palette indices. Both frontends (glyphs2fontir and soon ufo2fontir via #1904) resolve palette indices to colors by looking up the first palette 0, and the backend reconstructs indices via ColorPalettes::index_of() which similarly searches palette 0 for a matching color.
This round-trip is lossy if palette 0 contains duplicate color values at different indices. The CPAL spec does not require entries to be unique. A font may intentionally use the same color at two indices in palette 0 if they diverge in alternate palettes (e.g., a "monochrome" palette where distinct colors collapse).
In that case, index_of returns the first match, silently remapping the original index.
I propose we fix this by storing palette indices in the IR instead of resolved colors. PaintSolid would hold an Option<u16> (palette index, None for 0xFFFF foreground) instead of Option<Color>. Similarly for gradient ColorStop.
This would move palette construction entirely into the frontends:
- UFO: already has indices in the source, pass through directly
- Glyphs with explicit (COLRv0)
colorPalettelayer attribute: already has indices, pass through - Glyphs (COLRv1) with inline colors: frontend builds the palette from unique colors, then maps each color -> index. Unambiguous since the palette itself is derived from those exact colors.
The backend would consume indices directly, eliminating the lossy index_of color matching.
A trade off is the Glyphs ColorGlyphsWork which currently has no dependency on ColorPaletteWork would need to read the palette from context to resolve inline colors -> indices, adding a work graph dependency. Negligible perf impact since palette construction is fast.
Alternatives are validating that palette 0 has no duplicate colors and error early (rejects spec-valid fonts) or accept the limitation and document it (pragmatic given real-world fonts with duplicate palette 0 entries are rare).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace PaintSolid and ColorStop through glyphs2fontir, ufo2fontir, and the backend, then inspect ColorPalettes::index_of() and the ColorGlyphsWork/ColorPaletteWork relationship. Confirm how duplicate palette-0 colors are remapped and identify the affected IR and round-trip tests. Done means palette indices, including foreground and gradient stops, survive frontend-to-backend processing without color-based reconstruction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100