[ports] CoreText: honor SkFontArguments COLR/CPAL palette selection#270
Draft
mattleibow wants to merge 1 commit into
Draft
[ports] CoreText: honor SkFontArguments COLR/CPAL palette selection#270mattleibow wants to merge 1 commit into
mattleibow wants to merge 1 commit into
Conversation
CoreText exposes no public API to select a COLR/CPAL palette and always renders the font's default palette (index 0); the only color input it honors is the CGContext fill color, which it maps to the COLR foreground entry. As a result SkTypeface::makeClone() with a non-default SkFontArguments::Palette (selected index and/or per-entry overrides) had no visual effect on macOS/iOS, while FreeType and DirectWrite honor it. Honor the requested palette using public API only: at clone/load time, bake the resolved palette (selected index + per-entry overrides) into palette 0 of an in-memory copy of the font's CPAL table and build the CTFont from those bytes. CoreText then renders the requested colors because it still performs all of the drawing. Out-of-range indices fall back to palette 0, matching CSS Fonts 4 / FreeType semantics. Scope: CoreText renders COLRv0 (layered solid colors) and honors the edited palette, but does not paint COLRv1 (gradients) at all -- it falls back to a monochrome outline regardless of palette. Full COLRv1 palette support on Apple platforms would require Skia's own COLR engine. A previous attempt to use the private kCTFontPaletteAttribute SPI was reverted upstream because it is not App Store safe; this approach uses only public CoreText API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Collaborator
Author
|
Consumed by mono/SkiaSharp#4265, which bumps the |
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
CoreText exposes no public API to select a COLR/CPAL palette and always renders the font's default palette (index 0); the only color input it honors is the
CGContextfill color, which it maps to the COLR foreground entry. As a resultSkTypeface::makeClone()with a non-defaultSkFontArguments::Palette(selected index and/or per-entry overrides) had no visual effect on macOS/iOS, while FreeType (Linux) and DirectWrite (Windows) honor it.This is the CoreText half of SkiaSharp's
SKTypeface.Clone(paletteIndex)/SKFontArguments.PaletteOverridesbeing silently ignored on Apple platforms.Fix
Honor the requested palette using public API only. At clone/load time (
onMakeCloneandMakeFromStream):CPALtable and bake the resolved palette (selected index + per-entry overrides, BGRA, out-of-range index falls back to palette 0) into palette 0's color records of an in-memory copy.CTFontManagerCreateFontDescriptorFromData.CoreText then renders the requested colors because it still performs all of the drawing. The overwrite is in place (same size, no sfnt relayout); only the
CPALbytes and that table's directory checksum change. When there is no usableCPALtable or the request is the identity, the original font is kept.Scope / known limitation (COLRv1)
CoreText renders COLRv0 (layered solid colors) and honors the edited palette, but does not paint COLRv1 (gradients) at all on current macOS — it falls back to a monochrome outline regardless of palette. So this fix makes palette selection work for COLRv0 fonts; full COLRv1 palette support on Apple platforms would require Skia's own COLR engine (out of scope here). Verified empirically with standalone CoreText probes (no Skia): Apple Color Emoji paints; a COLRv0 font paints and follows edited palette-0 bytes; the official
test_glyphs-COLRv1.ttfalways renders black.Why not the private SPI
A previous upstream attempt used the private
kCTFontPaletteAttributeSPI (CL 867836) and was reverted (CL 918416) because it is not App Store safe (see flutter/flutter#158423). This approach uses only public CoreText API and carries no App Store risk.Testing
Verified on macOS arm64 (macOS 26.5) via SkiaSharp's managed test suite (
SKColorFontPaletteRenderingTest):Clone(0/1/2)of a 3-palette COLRv0 font now render distinct, correct colors and per-entry overrides recolor the targeted entry; typeface/font regression slice clean (162 tests). The consuming SkiaSharp PR links here.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com