googlefonts / googlefonts/fontc

[COLR] IR stores resolved colors instead of palette indices, lossy for duplicate CPAL entries

Open
#1,905 5 comments 0 reactions 0 assignees View on GitHub

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) colorPalette layer 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.