Implement support for rendering color emoji on Windows

This change adds support to the DirectWrite scaler context
for detection of color fonts. If it detects a color font and
the glyph is a color glyph, it will use DirectWrite's
TranslateColorGlyphRun API and generate an image of the
glyph that can then be rendered.

Chromium tests: https://codereview.chromium.org/2003853002

GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1984943002

Review-Url: https://codereview.chromium.org/1984943002
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index 4609d04..6f1d6fe 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -10,6 +10,7 @@
 
 #undef GetGlyphIndices
 
+#include "SkDraw.h"
 #include "SkDWrite.h"
 #include "SkDWriteGeometrySink.h"
 #include "SkEndian.h"
@@ -23,6 +24,7 @@
 #include "SkOTTable_gasp.h"
 #include "SkOTTable_maxp.h"
 #include "SkPath.h"
+#include "SkRasterClip.h"
 #include "SkScalerContext.h"
 #include "SkScalerContext_win_dw.h"
 #include "SkSharedMutex.h"
@@ -210,6 +212,14 @@
         , fTypeface(SkRef(typeface))
         , fGlyphCount(-1) {
 
+#if SK_HAS_DWRITE_2_H
+    fTypeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2);
+
+    SkTScopedComPtr<IDWriteFontFace2> fontFace2;
+    fTypeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2);
+    fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont();
+#endif
+
     // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
     // except when bi-level rendering is requested or there are embedded
     // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
@@ -458,6 +468,47 @@
     return true;
 }
 
+bool SkScalerContext_DW::isColorGlyph(const SkGlyph& glyph) {
+#if SK_HAS_DWRITE_2_H
+    SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayer;
+    if (getColorGlyphRun(glyph, &colorLayer)) {
+        return true;
+    }
+#endif
+    return false;
+}
+
+#if SK_HAS_DWRITE_2_H
+bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph,
+                                          IDWriteColorGlyphRunEnumerator** colorGlyph)
+{
+    FLOAT advance = 0;
+    UINT16 glyphId = glyph.getGlyphID();
+
+    DWRITE_GLYPH_OFFSET offset;
+    offset.advanceOffset = 0.0f;
+    offset.ascenderOffset = 0.0f;
+
+    DWRITE_GLYPH_RUN run;
+    run.glyphCount = 1;
+    run.glyphAdvances = &advance;
+    run.fontFace = fTypeface->fDWriteFontFace.get();
+    run.fontEmSize = SkScalarToFloat(fTextSizeRender);
+    run.bidiLevel = 0;
+    run.glyphIndices = &glyphId;
+    run.isSideways = FALSE;
+    run.glyphOffsets = &offset;
+
+    HRESULT hr = fFactory2->TranslateColorGlyphRun(
+        0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph);
+    if (hr == DWRITE_E_NOCOLOR) {
+        return false;
+    }
+    HRBM(hr, "Failed to translate color glyph run");
+    return true;
+}
+#endif
+
 void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
     glyph->fWidth = 0;
     glyph->fHeight = 0;
@@ -466,6 +517,12 @@
 
     this->generateAdvance(glyph);
 
+#if SK_HAS_DWRITE_2_H
+    if (fIsColorFont && isColorGlyph(*glyph)) {
+        glyph->fMaskFormat = SkMask::kARGB32_Format;
+    }
+#endif
+
     RECT bbox;
     HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox),
          "Requested bounding box could not be determined.");
@@ -710,6 +767,77 @@
     return fBits.begin();
 }
 
+#if SK_HAS_DWRITE_2_H
+void SkScalerContext_DW::generateColorGlyphImage(const SkGlyph& glyph) {
+    SkASSERT(isColorGlyph(glyph));
+    SkASSERT(glyph.fMaskFormat == SkMask::Format::kARGB32_Format);
+
+    memset(glyph.fImage, 0, glyph.computeImageSize());
+
+    SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
+    getColorGlyphRun(glyph, &colorLayers);
+    SkASSERT(colorLayers.get());
+
+    SkMatrix matrix = fSkXform;
+    matrix.postTranslate(-SkIntToScalar(glyph.fLeft), -SkIntToScalar(glyph.fTop));
+    SkRasterClip rc(SkIRect::MakeWH(glyph.fWidth, glyph.fHeight));
+    SkDraw draw;
+    draw.fDst = SkPixmap(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
+                         glyph.fImage,
+                         glyph.ComputeRowBytes(glyph.fWidth, SkMask::Format::kARGB32_Format));
+    draw.fMatrix = &matrix;
+    draw.fRC = &rc;
+
+    SkPaint paint;
+    if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
+        paint.setFlags(SkPaint::Flags::kAntiAlias_Flag);
+    }
+
+    BOOL hasNextRun = FALSE;
+    while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
+        const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
+        HRVM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
+
+        SkColor color;
+        if (colorGlyph->paletteIndex != 0xffff) {
+            color = SkColorSetARGB(SkFloatToIntRound(colorGlyph->runColor.a * 255),
+                                   SkFloatToIntRound(colorGlyph->runColor.r * 255),
+                                   SkFloatToIntRound(colorGlyph->runColor.g * 255),
+                                   SkFloatToIntRound(colorGlyph->runColor.b * 255));
+        } else {
+            // If all components of runColor are 0 or (equivalently) paletteIndex is 0xFFFF then
+            // the 'current brush' is used. fRec.getLuminanceColor() is kinda sorta what is wanted
+            // here, but not really, it will often be the wrong value because it wan't designed for
+            // this.
+            // In practice, I've not encountered a color glyph that uses the current brush color.
+            // If this assert ever fires, we should verify that the color is rendered properly.
+            SkASSERT(false);
+            color = fRec.getLuminanceColor();
+        }
+        paint.setColor(color);
+
+        SkPath path;
+        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
+        HRVM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
+             "Could not create geometry to path converter.");
+        {
+            Exclusive l(DWriteFactoryMutex);
+            HRVM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
+                colorGlyph->glyphRun.fontEmSize,
+                colorGlyph->glyphRun.glyphIndices,
+                colorGlyph->glyphRun.glyphAdvances,
+                colorGlyph->glyphRun.glyphOffsets,
+                colorGlyph->glyphRun.glyphCount,
+                colorGlyph->glyphRun.isSideways,
+                colorGlyph->glyphRun.bidiLevel % 2, //rtl
+                geometryToPath.get()),
+                "Could not create glyph outline.");
+        }
+        draw.drawPath(path, paint, nullptr, true /* pathIsMutable */);
+    }
+}
+#endif
+
 void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
     //Create the mask.
     DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
@@ -718,6 +846,14 @@
         renderingMode = DWRITE_RENDERING_MODE_ALIASED;
         textureType = DWRITE_TEXTURE_ALIASED_1x1;
     }
+
+#if SK_HAS_DWRITE_2_H
+    if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
+        generateColorGlyphImage(glyph);
+        return;
+    }
+#endif
+
     const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
     if (!bits) {
         sk_bzero(glyph.fImage, glyph.computeImageSize());
diff --git a/src/ports/SkScalerContext_win_dw.h b/src/ports/SkScalerContext_win_dw.h
index 9b24772..98c4910 100644
--- a/src/ports/SkScalerContext_win_dw.h
+++ b/src/ports/SkScalerContext_win_dw.h
@@ -14,6 +14,9 @@
 #include "SkTypes.h"
 
 #include <dwrite.h>
+#if SK_HAS_DWRITE_2_H
+#include <dwrite_2.h>
+#endif
 
 class SkGlyph;
 class SkDescriptor;
@@ -42,6 +45,14 @@
                            DWRITE_TEXTURE_TYPE textureType,
                            RECT* bbox);
 
+    bool isColorGlyph(const SkGlyph& glyph);
+
+#if SK_HAS_DWRITE_2_H
+    bool getColorGlyphRun(const SkGlyph& glyph, IDWriteColorGlyphRunEnumerator** colorGlyph);
+
+    void generateColorGlyphImage(const SkGlyph& glyph);
+#endif
+
     SkTDArray<uint8_t> fBits;
     /** The total matrix without the text height scale. */
     SkMatrix fSkXform;
@@ -64,6 +75,10 @@
     DWRITE_RENDERING_MODE fRenderingMode;
     DWRITE_TEXTURE_TYPE fTextureType;
     DWRITE_MEASURING_MODE fMeasuringMode;
+#if SK_HAS_DWRITE_2_H
+    SkTScopedComPtr<IDWriteFactory2> fFactory2;
+    bool fIsColorFont;
+#endif
 };
 
 #endif
diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp
index 9278c84..8303f09 100644
--- a/tools/sk_tool_utils.cpp
+++ b/tools/sk_tool_utils.cpp
@@ -12,6 +12,8 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkCommonFlags.h"
+#include "SkFontMgr.h"
+#include "SkFontStyle.h"
 #include "SkPoint3.h"
 #include "SkShader.h"
 #include "SkTestScalerContext.h"
@@ -72,6 +74,9 @@
     if (!strncmp(osName, "Mac", 3)) {
         return "SBIX";
     }
+    if (!strncmp(osName, "Win", 3)) {
+        return "COLR";
+    }
     return "";
 }
 
@@ -82,6 +87,23 @@
     if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX")) {
         return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle());
     }
+    if (!strcmp(sk_tool_utils::platform_os_emoji(), "COLR")) {
+        sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+        const char *colorEmojiFontName = "Segoe UI Emoji";
+        sk_sp<SkTypeface> typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle()));
+        if (typeface) {
+            return typeface;
+        }
+        sk_sp<SkTypeface> fallback(fm->matchFamilyStyleCharacter(
+            colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */,
+            0x1f4b0 /* character: 💰 */));
+        if (fallback) {
+            return fallback;
+        }
+        // If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol.
+        // Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji.
+        return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle());
+    }
     return nullptr;
 }
 
@@ -89,7 +111,9 @@
     if (!strcmp(sk_tool_utils::platform_os_emoji(), "CBDT")) {
         return "Hamburgefons";
     }
-    if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX")) {
+    if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX") ||
+        !strcmp(sk_tool_utils::platform_os_emoji(), "COLR"))
+    {
         return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85"  // 💰🏡🎅
                "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80"  // 🍪🍕🚀
                "\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // 🚻💩📷