From 91c4d70d3f8d8c32ed0cf4d9856d1c2acb99a2d7 Mon Sep 17 00:00:00 2001 From: Evgeny Tvorun Date: Tue, 2 Jun 2026 07:09:38 -0700 Subject: [PATCH 1/2] Add DirectWrite color glyph (COLR v0) rendering support --- .../CPP/DWriteWrapper/FontFace.cpp | 29 +++ .../CPP/DWriteWrapper/FontFace.h | 8 + .../Fonts/GlobalUserInterface.CompositeFont | 224 ++++++++++++++--- .../Interop/DWrite/DWriteColorGlyphStructs.cs | 47 ++++ .../DWrite/IDWriteColorGlyphRunEnumerator.cs | 31 +++ .../Text/TextInterface/ColorGlyphLayer.cs | 22 ++ .../MS/internal/Text/TextInterface/Factory.cs | 198 +++++++++++++++ .../PresentationCore/PresentationCore.csproj | 3 + .../Windows/Media/DrawingDrawingContext.cs | 9 + .../Generated/RenderDataDrawingContext.cs | 14 ++ .../System/Windows/Media/GlyphRun.cs | 225 ++++++++++++++++++ .../System/Windows/Media/GlyphTypeface.cs | 36 +++ .../System/Windows/Media/ColorEmojiTests.cs | 165 +++++++++++++ 13 files changed, 980 insertions(+), 31 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWriteColorGlyphStructs.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteColorGlyphRunEnumerator.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/ColorGlyphLayer.cs create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Media/ColorEmojiTests.cs diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.cpp index 6e082906052..a0ab8802cb6 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.cpp @@ -259,4 +259,33 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface return success; } + + /// Checks whether the font face contains a COLR table by probing via + /// IDWriteFontFace::TryGetFontTable. The table data is immediately released + /// since we only need to know whether the table exists, not its contents. + /// TranslateColorGlyphRun does its own deep parsing of COLR/CPAL. + __declspec(noinline) bool FontFace::HasColorGlyphs() + { + const void* tableData; + void* tableContext; + UINT32 tableSize = 0; + BOOL exists = FALSE; + + HRESULT hr = _fontFace->Value->TryGetFontTable( + DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R'), + &tableData, + &tableSize, + &tableContext, + &exists + ); + + if (SUCCEEDED(hr) && exists) + { + _fontFace->Value->ReleaseFontTable(tableContext); + } + + System::GC::KeepAlive(_fontFace); + return (!!exists); + } + }}}}//MS::Internal::Text::TextInterface diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.h b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.h index 76f6b49dfc8..fce996e6c9c 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.h +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFace.h @@ -217,6 +217,14 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface /// True if os2 table exists and the FontEmbeddingRights was read successfully. bool ReadFontEmbeddingRights([System::Runtime::InteropServices::Out] unsigned short% fsType); + /// + /// Returns true if the font face contains a COLR (Color) OpenType table, + /// indicating it has color glyph layer definitions. Only checks for table + /// presence, not validity -- a corrupt COLR table would return true here + /// but fail during TranslateColorGlyphRun. + /// + bool HasColorGlyphs(); + /// /// dtor. /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/Fonts/GlobalUserInterface.CompositeFont b/src/Microsoft.DotNet.Wpf/src/PresentationCore/Fonts/GlobalUserInterface.CompositeFont index 215b71ae385..37b8e6be49d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/Fonts/GlobalUserInterface.CompositeFont +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/Fonts/GlobalUserInterface.CompositeFont @@ -395,7 +395,7 @@ Box Drawing 2500-257F Block Elements 2580-259F Geometric Shapes 25A0-25FF - Geometric Shapes Extended 1F780-1F7FF + Geometric Shapes Extended 1F780-1F7FF (excluding 1F7E0-1F7EB and 1F7F0, which are emoji; see below) --> + + + + + + + + + + + + + + + + + + + + + + + + - @@ -1681,11 +1744,12 @@ Ornamental Dingbats 1F650-1F67F Transport And Map Symbols 1F680-1F6FF Supplemental Symbols and Pictographs 1F900-1F9FF + Symbols and Pictographs Extended-A 1FA70-1FAFF --> - + @@ -1701,10 +1765,11 @@ - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -3395,11 +3523,12 @@ Ornamental Dingbats 1F650-1F67F Transport And Map Symbols 1F680-1F6FF Supplemental Symbols and Pictographs 1F900-1F9FF + Symbols and Pictographs Extended-A 1FA70-1FAFF --> - + @@ -4009,6 +4138,38 @@ Target = "Segoe UI Emoji, Segoe UI, Segoe UI Symbol, Yu Gothic UI, Meiryo UI, Meiryo, Microsoft JhengHei UI, Microsoft JhengHei, Microsoft YaHei UI, Microsoft YaHei, Malgun Gothic, Tahoma, Lucida Sans Unicode, Arial, MS Gothic, MingLiU, Arial Unicode MS, MingLiU" Scale = "1.0" /> + + + + + + + + + + + + - @@ -5027,18 +5187,20 @@ Emoticons 1F600-1F64F Ornamental Dingbats 1F650-1F67F Transport And Map Symbols 1F680-1F6FF + Supplemental Symbols and Pictographs 1F900-1F9FF + Symbols and Pictographs Extended-A 1FA70-1FAFF --> - + - +