Text Rendering: Add DirectWrite color glyph (COLR v0) rendering support#11684
Text Rendering: Add DirectWrite color glyph (COLR v0) rendering support#11684etvorun wants to merge 2 commits into
Conversation
|
Pinging participants from #91 who may be interested in reviewing this implementation: @dotMorten (original issue author), @mikedn, @be5invis, @fdwr (DirectWrite expertise), @samhocevar (Emoji.Wpf author), @fcharlie, @pchaurasia14, @Bart-Verdonck, @alexinea, @gusmanb, @Lorymi, @virzak, @XamDR, @gusmanb This PR adds native COLR v0 color glyph support directly in WPF's rendering pipeline via |
There was a problem hiding this comment.
Pull request overview
This PR adds COLR v0 color glyph (emoji) rendering to WPF by decomposing DirectWrite color glyph runs into per-layer geometries, and updates the composite font fallback so newer emoji ranges select Segoe UI Emoji (enabling full-color emoji instead of monochrome/boxes).
Changes:
- Add color-font detection (
GlyphTypeface.IsColorFont) and a color-layer drawing path (GlyphRun.TryBuildColorGlyphDrawing) backed byIDWriteFactory2::TranslateColorGlyphRun. - Hook color-layer rendering into managed drawing contexts (
DrawingDrawingContextandRenderDataDrawingContext) while preserving the existing monochrome pipeline as fallback. - Extend
GlobalUserInterface.CompositeFontemoji mappings and add unit tests for color glyph detection/layering/fallback.
Show a summary per file
| File | Description |
|---|---|
| src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Media/ColorEmojiTests.cs | Adds unit tests for color font detection, layer decomposition, and color interpretation. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/GlyphTypeface.cs | Adds cached color-font detection via COLR table presence. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/GlyphRun.cs | Adds translation of glyph runs into color layers and builds a DrawingGroup from per-layer geometries. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/RenderDataDrawingContext.cs | Intercepts DrawGlyphRun to draw color-layer drawings when available. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/DrawingDrawingContext.cs | Intercepts DrawGlyphRun to add color-layer drawings when available. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj | Includes new DirectWrite interop and layer types in the build. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/Factory.cs | QI’s IDWriteFactory2 and exposes a managed wrapper for TranslateColorGlyphRun. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/ColorGlyphLayer.cs | Introduces a managed struct representing a translated color layer. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteColorGlyphRunEnumerator.cs | Adds interop for IDWriteColorGlyphRunEnumerator. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWriteColorGlyphStructs.cs | Adds interop structs used by TranslateColorGlyphRun. |
| src/Microsoft.DotNet.Wpf/src/PresentationCore/Fonts/GlobalUserInterface.CompositeFont | Expands emoji unicode ranges and adds explicit mappings for emoji in mixed blocks. |
| src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.h | Declares a native helper to probe for COLR table presence. |
| src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.cpp | Implements COLR table probing via IDWriteFontFace::TryGetFontTable. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 12/13 changed files
- Comments generated: 12
|
I think this is fine for Visual Studio but not for WPF itself. We should have a proper model for color glyph runs, not a render-time hack. If there is an interest to have this interim solution, it should at least be opt-in. |
There was a problem hiding this comment.
Thanks ET - this is cool. Looks functionally correct, with minor feedback.
Regarding Jan's concern, is there a way to render monochromatically if desired, or is this always-on if the font has color in it? For UI controls like labels and text boxes, opting in by default makes sense, but for low level glyph rendering, there might be cases where we want to enable users to configure this. For Direct2D's DrawTextLayout, we didn't automatically enable it at the rendering layers because people could be doing custom highlight selection logic that wasn't prepared for full color yet, but we made it really easy to enable (just pass D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT). Maybe the DrawingDrawingContext (what a weird name) and RenderDataDrawingContext could have a property.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This PR a port of a fix used by Visual Studio 2026 (release 18.8). We can treat it as a starting point and improve upon it. If you can recommend specific changes to this PR I can include them :) |
|
The issue #91 you referenced discussed some options. Introducing |
There was a problem hiding this comment.
LGTM sir 🫡. Like you say, it's a great starting point to improve upon. I'll defer to the WPF-side folks for policy decisions like whether to opt-in explicitly or opt-in by default like UWP does. (now we'll be able to use color emoji in Windows PIX which uses WPF 😉)
Summary
Add DirectWrite color glyph (COLR v0) rendering support to WPF and fix emoji font fallback so that emoji characters render in full color instead of monochrome outlines.
Before and after
Text like the following now renders in full color instead of monochrome outlines:
Before: Emoji show as monochrome outlines or empty boxes.
After: Emoji render with full COLR v0 palette colors.
What changed
Color glyph rendering
GlyphTypeface.IsColorFont— New property that detects whether a font has a COLR OpenType table (lazy-cached).GlyphRun.TryBuildColorGlyphDrawing()— CallsIDWriteFactory2::TranslateColorGlyphRunto decompose a glyph run into per-layer sub-runs, each with a CPAL palette color. Builds a frozenDrawingGroupofGeometryDrawinglayers.DrawingDrawingContext/RenderDataDrawingContext— InterceptDrawGlyphRunto render color layers when available, falling through to the existing monochrome path for non-color fonts.Factory.TranslateColorGlyphRun()— Managed wrapper aroundIDWriteFactory2::TranslateColorGlyphRunwith input validation and proper COM lifetime management. Queries forIDWriteFactory2at factory creation with graceful fallback on pre-Windows 8.1.FontFace.HasColorGlyphs()— Native helper that probes for the COLR OpenType table.IDWriteColorGlyphRunEnumerator,DWRITE_COLOR_GLYPH_RUN,ColorGlyphLayerfor managed/native interop.Font fallback fixes
FontFamilyMapsections ofGlobalUserInterface.CompositeFontfrom1F900-1F9FFto1F900-1FAFF(adds Symbols and Pictographs Extended-A coverage).Tests
ColorEmojiTestscoveringIsColorFontdetection, color glyph layer decomposition, and font fallback for previously missing emoji code points.Why
WPF renders all emoji as monochrome outlines because the glyph pipeline produces single-channel coverage bitmaps with no per-layer color support. Additionally, newer Unicode emoji ranges are not mapped to Segoe UI Emoji in the composite font, causing them to show as empty boxes.
This fix decomposes COLR v0 color glyphs via DirectWrite into individually colored geometry layers, and extends font fallback to cover all standard emoji Unicode ranges.
Limitations
Validation
Fixes #91
Microsoft Reviewers: Open in CodeFlow