tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SkScalerContext_win_dw.cpp (101824B)


      1 /*
      2 * Copyright 2011 Google Inc.
      3 *
      4 * Use of this source code is governed by a BSD-style license that can be
      5 * found in the LICENSE file.
      6 */
      7 #include "src/utils/win/SkDWriteNTDDI_VERSION.h"
      8 
      9 #include "include/core/SkTypes.h"
     10 #if defined(SK_BUILD_FOR_WIN)
     11 
     12 #undef GetGlyphIndices
     13 
     14 #include "include/codec/SkCodec.h"
     15 #include "include/codec/SkPngDecoder.h"
     16 #include "include/core/SkBBHFactory.h"
     17 #include "include/core/SkBitmap.h"
     18 #include "include/core/SkData.h"
     19 #include "include/core/SkDrawable.h"
     20 #include "include/core/SkFontMetrics.h"
     21 #include "include/core/SkGraphics.h"
     22 #include "include/core/SkImage.h"
     23 #include "include/core/SkOpenTypeSVGDecoder.h"
     24 #include "include/core/SkPath.h"
     25 #include "include/core/SkPictureRecorder.h"
     26 #include "include/core/SkSpan.h"
     27 #include "include/effects/SkGradientShader.h"
     28 #include "include/private/base/SkMutex.h"
     29 #include "include/private/base/SkTo.h"
     30 #include "src/base/SkEndian.h"
     31 #include "src/base/SkScopeExit.h"
     32 #include "src/base/SkSharedMutex.h"
     33 #include "src/core/SkDraw.h"
     34 #include "src/core/SkGlyph.h"
     35 #include "src/core/SkMaskGamma.h"
     36 #include "src/core/SkRasterClip.h"
     37 #include "src/core/SkScalerContext.h"
     38 #include "src/ports/SkScalerContext_win_dw.h"
     39 #include "src/ports/SkTypeface_win_dw.h"
     40 #include "src/sfnt/SkOTTable_EBLC.h"
     41 #include "src/sfnt/SkOTTable_EBSC.h"
     42 #include "src/sfnt/SkOTTable_gasp.h"
     43 #include "src/sfnt/SkOTTable_maxp.h"
     44 #include "src/utils/SkMatrix22.h"
     45 #include "src/utils/win/SkDWrite.h"
     46 #include "src/utils/win/SkDWriteGeometrySink.h"
     47 #include "src/utils/win/SkHRESULT.h"
     48 #include "src/utils/win/SkTScopedComPtr.h"
     49 
     50 #include <dwrite.h>
     51 #include <dwrite_1.h>
     52 #include <dwrite_3.h>
     53 
     54 namespace {
     55 static inline const constexpr bool kSkShowTextBlitCoverage = false;
     56 
     57 /* Note:
     58 * In versions 8 and 8.1 of Windows, some calls in DWrite are not thread safe.
     59 * The mutex returned from maybe_dw_mutex protects the calls that are
     60 * problematic.
     61 */
     62 static SkSharedMutex* maybe_dw_mutex(DWriteFontTypeface& typeface) {
     63    static SkSharedMutex mutex;
     64    return typeface.fDWriteFontFace4 ? nullptr : &mutex;
     65 }
     66 
     67 class SK_SCOPED_CAPABILITY Exclusive {
     68 public:
     69    explicit Exclusive(SkSharedMutex* maybe_lock) SK_ACQUIRE(*maybe_lock)
     70        : fLock(maybe_lock) {
     71        if (fLock) {
     72            fLock->acquire();
     73        }
     74    }
     75    ~Exclusive() SK_RELEASE_CAPABILITY() {
     76        if (fLock) {
     77            fLock->release();
     78        }
     79    }
     80 
     81 private:
     82    SkSharedMutex* fLock;
     83 };
     84 class SK_SCOPED_CAPABILITY Shared {
     85 public:
     86    explicit Shared(SkSharedMutex* maybe_lock) SK_ACQUIRE_SHARED(*maybe_lock)
     87        : fLock(maybe_lock)  {
     88        if (fLock) {
     89            fLock->acquireShared();
     90        }
     91    }
     92 
     93    // You would think this should be SK_RELEASE_SHARED_CAPABILITY, but SK_SCOPED_CAPABILITY
     94    // doesn't fully understand the difference between shared and exclusive.
     95    // Please review https://reviews.llvm.org/D52578 for more information.
     96    ~Shared() SK_RELEASE_CAPABILITY() {
     97        if (fLock) {
     98            fLock->releaseShared();
     99        }
    100    }
    101 
    102 private:
    103    SkSharedMutex* fLock;
    104 };
    105 
    106 static bool isLCD(const SkScalerContextRec& rec) {
    107    return SkMask::kLCD16_Format == rec.fMaskFormat;
    108 }
    109 
    110 static bool is_hinted(DWriteFontTypeface* typeface) {
    111    Exclusive l(maybe_dw_mutex(*typeface));
    112    AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
    113    if (!maxp.fExists) {
    114        return false;
    115    }
    116    if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
    117        return false;
    118    }
    119    if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
    120        return false;
    121    }
    122    return (0 != maxp->version.tt.maxSizeOfInstructions);
    123 }
    124 
    125 /** A GaspRange is inclusive, [min, max]. */
    126 struct GaspRange {
    127    using Behavior = SkOTTableGridAndScanProcedure::GaspRange::behavior;
    128    GaspRange(int min, int max, int version, Behavior flags)
    129        : fMin(min), fMax(max), fVersion(version), fFlags(flags) { }
    130    int fMin;
    131    int fMax;
    132    int fVersion;
    133    Behavior fFlags;
    134 };
    135 
    136 bool get_gasp_range(DWriteFontTypeface* typeface, int size, GaspRange* range) {
    137    AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
    138    if (!gasp.fExists) {
    139        return false;
    140    }
    141    if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
    142        return false;
    143    }
    144    if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
    145        gasp->version != SkOTTableGridAndScanProcedure::version1)
    146    {
    147        return false;
    148    }
    149 
    150    uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
    151    if (numRanges > 1024 ||
    152        gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
    153        sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
    154    {
    155        return false;
    156    }
    157 
    158    const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
    159            SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
    160    int minPPEM = -1;
    161    for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
    162        int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
    163        if (minPPEM < size && size <= maxPPEM) {
    164            range->fMin = minPPEM + 1;
    165            range->fMax = maxPPEM;
    166            range->fVersion = SkEndian_SwapBE16(gasp->version);
    167            range->fFlags = rangeTable->flags;
    168            return true;
    169        }
    170        minPPEM = maxPPEM;
    171    }
    172    return false;
    173 }
    174 /** If the rendering mode for the specified 'size' is gridfit, then place
    175 *  the gridfit range into 'range'. Otherwise, leave 'range' alone.
    176 */
    177 static bool is_gridfit_only(GaspRange::Behavior flags) {
    178    return flags.raw.value == GaspRange::Behavior::Raw::GridfitMask;
    179 }
    180 
    181 static bool has_bitmap_strike(DWriteFontTypeface* typeface, GaspRange range) {
    182    Exclusive l(maybe_dw_mutex(*typeface));
    183    {
    184        AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
    185        if (!eblc.fExists) {
    186            return false;
    187        }
    188        if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
    189            return false;
    190        }
    191        if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
    192            return false;
    193        }
    194 
    195        uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
    196        if (numSizes > 1024 ||
    197            eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
    198                         sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
    199        {
    200            return false;
    201        }
    202 
    203        const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
    204                SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
    205        for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
    206            if (sizeTable->ppemX == sizeTable->ppemY &&
    207                range.fMin <= sizeTable->ppemX && sizeTable->ppemX <= range.fMax)
    208            {
    209                // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
    210                // to determine the actual number of glyphs with bitmaps.
    211 
    212                // TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
    213 
    214                // TODO: Ensure that the bitmaps are bi-level?
    215                if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
    216                    return true;
    217                }
    218            }
    219        }
    220    }
    221 
    222    {
    223        AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
    224        if (!ebsc.fExists) {
    225            return false;
    226        }
    227        if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
    228            return false;
    229        }
    230        if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
    231            return false;
    232        }
    233 
    234        uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
    235        if (numSizes > 1024 ||
    236            ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
    237                         sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
    238        {
    239            return false;
    240        }
    241 
    242        const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
    243                SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
    244        for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
    245            if (scaleTable->ppemX == scaleTable->ppemY &&
    246                range.fMin <= scaleTable->ppemX && scaleTable->ppemX <= range.fMax) {
    247                // EBSC tables are normally only found in bitmap only fonts.
    248                return true;
    249            }
    250        }
    251    }
    252 
    253    return false;
    254 }
    255 
    256 static bool both_zero(SkScalar a, SkScalar b) {
    257    return 0 == a && 0 == b;
    258 }
    259 
    260 // returns false if there is any non-90-rotation or skew
    261 static bool is_axis_aligned(const SkScalerContextRec& rec) {
    262    return 0 == rec.fPreSkewX &&
    263           (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
    264            both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
    265 }
    266 
    267 }  //namespace
    268 
    269 SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface& typefaceRef,
    270                                       const SkScalerContextEffects& effects,
    271                                       const SkDescriptor* desc)
    272        : SkScalerContext(typefaceRef, effects, desc)
    273 {
    274    DWriteFontTypeface* typeface = this->getDWriteTypeface();
    275    fGlyphCount = typeface->fDWriteFontFace->GetGlyphCount();
    276 
    277    fClearTypeLevel = int(typeface->GetClearTypeLevel() * 256);
    278 
    279    // In general, all glyphs should use DWriteFontFace::GetRecommendedRenderingMode
    280    // except when bi-level rendering is requested or there are embedded
    281    // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
    282    //
    283    // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
    284    // this. As a result, determine the actual size of the text and then see if
    285    // there are any embedded bi-level bitmaps of that size. If there are, then
    286    // force bitmaps by requesting bi-level rendering.
    287    //
    288    // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
    289    // square pixels and only uses ppemY. Therefore the transform must track any
    290    // non-uniform x-scale.
    291    //
    292    // Also, rotated glyphs should have the same absolute advance widths as
    293    // horizontal glyphs and the subpixel flag should not affect glyph shapes.
    294 
    295    SkVector scale;
    296    fRec.computeMatrices(SkScalerContextRec::PreMatrixScale::kVertical, &scale, &fSkXform);
    297 
    298    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
    299    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
    300    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
    301    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
    302    fXform.dx = 0;
    303    fXform.dy = 0;
    304 
    305    // realTextSize is the actual device size we want (as opposed to the size the user requested).
    306    // gdiTextSize is the size we request when GDI compatible.
    307    // If the scale is negative, this means the matrix will do the flip anyway.
    308    const SkScalar realTextSize = scale.fY;
    309    // Due to floating point math, the lower bits are suspect. Round carefully.
    310    SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
    311    if (gdiTextSize == 0) {
    312        gdiTextSize = SK_Scalar1;
    313    }
    314 
    315    bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
    316    bool treatLikeBitmap = false;
    317    bool axisAlignedBitmap = false;
    318    if (bitmapRequested) {
    319        // When embedded bitmaps are requested, treat the entire range like
    320        // a bitmap strike if the range is gridfit only and contains a bitmap.
    321        int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
    322        GaspRange range(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
    323        if (get_gasp_range(typeface, bitmapPPEM, &range)) {
    324            if (!is_gridfit_only(range.fFlags)) {
    325                range = GaspRange(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
    326            }
    327        }
    328        treatLikeBitmap = has_bitmap_strike(typeface, range);
    329 
    330        axisAlignedBitmap = is_axis_aligned(fRec);
    331    }
    332 
    333    GaspRange range(0, 0xFFFF, 0, GaspRange::Behavior());
    334 
    335    // If the user requested aliased, do so with aliased compatible metrics.
    336    if (SkMask::kBW_Format == fRec.fMaskFormat) {
    337        fTextSizeRender = gdiTextSize;
    338        fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
    339        fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
    340        fTextSizeMeasure = gdiTextSize;
    341        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
    342 
    343    // If we can use a bitmap, use gdi classic rendering and measurement.
    344    // This will not always provide a bitmap, but matches expected behavior.
    345    } else if ((treatLikeBitmap && axisAlignedBitmap) || typeface->ForceGDI()) {
    346        fTextSizeRender = gdiTextSize;
    347        fRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
    348        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    349        fTextSizeMeasure = gdiTextSize;
    350        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
    351 
    352    // If rotated but the horizontal text could have used a bitmap,
    353    // render high quality rotated glyphs but measure using bitmap metrics.
    354    } else if (treatLikeBitmap) {
    355        fTextSizeRender = gdiTextSize;
    356        fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    357        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    358        fTextSizeMeasure = gdiTextSize;
    359        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
    360 
    361    // Force symmetric if the font is above the threshold or there is an explicit mode.
    362    // Here we check if the size exceeds 20 before checking the GASP table to match the
    363    // results of calling GetRecommendedRenderingMode/Direct2D, which skip looking at
    364    // the GASP table if the text is too large.
    365    } else if (realTextSize > SkIntToScalar(20) ||
    366               typeface->GetRenderingMode() == DWRITE_RENDERING_MODE_NATURAL ||
    367               typeface->GetRenderingMode() == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC) {
    368        fTextSizeRender = realTextSize;
    369        fRenderingMode = typeface->GetRenderingMode() == DWRITE_RENDERING_MODE_NATURAL ?
    370            DWRITE_RENDERING_MODE_NATURAL : DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    371        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    372        fTextSizeMeasure = realTextSize;
    373        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    374    // If the font has a gasp table version 1, use it to determine symmetric rendering.
    375    } else if (get_gasp_range(typeface, SkScalarRoundToInt(gdiTextSize), &range) &&
    376               range.fVersion >= 1) {
    377        fTextSizeRender = realTextSize;
    378        fRenderingMode = !range.fFlags.field.SymmetricSmoothing ?
    379            DWRITE_RENDERING_MODE_NATURAL : DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    380        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    381        fTextSizeMeasure = realTextSize;
    382        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    383    // Fonts with hints, no gasp or gasp version 0, and below 20px get non-symmetric rendering.
    384    // Often such fonts have hints which were only tested with GDI ClearType classic.
    385    // Some of these fonts rely on drop out control in the y direction in order to be legible.
    386    // Tenor Sans
    387    //    https://fonts.google.com/specimen/Tenor+Sans
    388    // Gill Sans W04
    389    //    https://cdn.leagueoflegends.com/lolkit/1.1.9/resources/fonts/gill-sans-w04-book.woff
    390    //    https://na.leagueoflegends.com/en/news/game-updates/patch/patch-410-notes
    391    // See https://crbug.com/385897
    392    } else {
    393        if (is_hinted(typeface)) {
    394          fTextSizeRender = gdiTextSize;
    395          fRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
    396        } else {
    397          // Unhinted but with no gasp and below 20px defaults to symmetric for
    398          // GetRecommendedRenderingMode.
    399          fTextSizeRender = realTextSize;
    400          fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    401        }
    402        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    403        fTextSizeMeasure = realTextSize;
    404        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    405    }
    406 
    407    // DirectWrite2 allows for grayscale hinting.
    408    fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE;
    409    if (typeface->fFactory2 && typeface->fDWriteFontFace2 &&
    410        SkMask::kA8_Format == fRec.fMaskFormat &&
    411        !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag))
    412    {
    413        // DWRITE_TEXTURE_ALIASED_1x1 is now misnamed, it must also be used with grayscale.
    414        fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
    415        fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE;
    416    }
    417 
    418    // DirectWrite2 allows hinting to be disabled.
    419    fGridFitMode = DWRITE_GRID_FIT_MODE_ENABLED;
    420    if (fRec.getHinting() == SkFontHinting::kNone) {
    421        fGridFitMode = DWRITE_GRID_FIT_MODE_DISABLED;
    422        if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
    423            fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    424        }
    425    }
    426 
    427    if (this->isLinearMetrics()) {
    428        fTextSizeMeasure = realTextSize;
    429        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    430    }
    431 
    432    // The GDI measuring modes don't seem to work well with CBDT fonts (DWrite.dll 10.0.18362.836).
    433    if (fMeasuringMode != DWRITE_MEASURING_MODE_NATURAL) {
    434        constexpr UINT32 CBDTTag = DWRITE_MAKE_OPENTYPE_TAG('C','B','D','T');
    435        AutoDWriteTable CBDT(typeface->fDWriteFontFace.get(), CBDTTag);
    436        if (CBDT.fExists) {
    437            fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    438        }
    439    }
    440 }
    441 
    442 SkScalerContext_DW::~SkScalerContext_DW() {
    443 }
    444 
    445 #if !SK_DISABLE_DIRECTWRITE_COLRv1 && (DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN))
    446 
    447 namespace {
    448 SkColor4f sk_color_from(DWRITE_COLOR_F const& color) {
    449    // DWRITE_COLOR_F and SkColor4f are laid out the same and this should be a no-op.
    450    return SkColor4f{ color.r, color.g, color.b, color.a };
    451 }
    452 DWRITE_COLOR_F dw_color_from(SkColor4f const& color) {
    453    // DWRITE_COLOR_F and SkColor4f are laid out the same and this should be a no-op.
    454    // Avoid brace initialization as DWRITE_COLOR_F can be defined as four floats (dxgitype.h,
    455    // d3d9types.h) or four unions of two floats (dwrite_2.h, d3dtypes.h). The type changed in
    456    // Direct3D 10, but the change does not appear to be documented.
    457    DWRITE_COLOR_F dwColor;
    458    dwColor.r = color.fR;
    459    dwColor.g = color.fG;
    460    dwColor.b = color.fB;
    461    dwColor.a = color.fA;
    462    return dwColor;
    463 }
    464 
    465 SkRect sk_rect_from(D2D_RECT_F const& rect) {
    466    // D2D_RECT_F and SkRect are both y-down and laid the same so this should be a no-op.
    467    return SkRect{ rect.left, rect.top, rect.right, rect.bottom };
    468 }
    469 constexpr bool D2D_RECT_F_is_empty(const D2D_RECT_F& r) {
    470    return r.right <= r.left || r.bottom <= r.top;
    471 }
    472 
    473 SkMatrix sk_matrix_from(DWRITE_MATRIX const& m) {
    474    // DWRITE_MATRIX and SkMatrix are y-down. However DWRITE_MATRIX is affine only.
    475    return SkMatrix::MakeAll(
    476        m.m11, m.m21, m.dx,
    477        m.m12, m.m22, m.dy,
    478        0, 0, 1);
    479 }
    480 
    481 SkTileMode sk_tile_mode_from(D2D1_EXTEND_MODE extendMode) {
    482    switch (extendMode) {
    483    case D2D1_EXTEND_MODE_CLAMP:
    484        return SkTileMode::kClamp;
    485    case D2D1_EXTEND_MODE_WRAP:
    486        return SkTileMode::kRepeat;
    487    case D2D1_EXTEND_MODE_MIRROR:
    488        return SkTileMode::kMirror;
    489    default:
    490        return SkTileMode::kClamp;
    491    }
    492 }
    493 
    494 SkBlendMode sk_blend_mode_from(DWRITE_COLOR_COMPOSITE_MODE compositeMode) {
    495    switch (compositeMode) {
    496    case DWRITE_COLOR_COMPOSITE_CLEAR:
    497        return SkBlendMode::kClear;
    498    case DWRITE_COLOR_COMPOSITE_SRC:
    499        return SkBlendMode::kSrc;
    500    case DWRITE_COLOR_COMPOSITE_DEST:
    501        return SkBlendMode::kDst;
    502    case DWRITE_COLOR_COMPOSITE_SRC_OVER:
    503        return SkBlendMode::kSrcOver;
    504    case DWRITE_COLOR_COMPOSITE_DEST_OVER:
    505        return SkBlendMode::kDstOver;
    506    case DWRITE_COLOR_COMPOSITE_SRC_IN:
    507        return SkBlendMode::kSrcIn;
    508    case DWRITE_COLOR_COMPOSITE_DEST_IN:
    509        return SkBlendMode::kDstIn;
    510    case DWRITE_COLOR_COMPOSITE_SRC_OUT:
    511        return SkBlendMode::kSrcOut;
    512    case DWRITE_COLOR_COMPOSITE_DEST_OUT:
    513        return SkBlendMode::kDstOut;
    514    case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
    515        return SkBlendMode::kSrcATop;
    516    case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
    517        return SkBlendMode::kDstATop;
    518    case DWRITE_COLOR_COMPOSITE_XOR:
    519        return SkBlendMode::kXor;
    520    case DWRITE_COLOR_COMPOSITE_PLUS:
    521        return SkBlendMode::kPlus;
    522 
    523    case DWRITE_COLOR_COMPOSITE_SCREEN:
    524        return SkBlendMode::kScreen;
    525    case DWRITE_COLOR_COMPOSITE_OVERLAY:
    526        return SkBlendMode::kOverlay;
    527    case DWRITE_COLOR_COMPOSITE_DARKEN:
    528        return SkBlendMode::kDarken;
    529    case DWRITE_COLOR_COMPOSITE_LIGHTEN:
    530        return SkBlendMode::kLighten;
    531    case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
    532        return SkBlendMode::kColorDodge;
    533    case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
    534        return SkBlendMode::kColorBurn;
    535    case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
    536        return SkBlendMode::kHardLight;
    537    case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
    538        return SkBlendMode::kSoftLight;
    539    case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
    540        return SkBlendMode::kDifference;
    541    case DWRITE_COLOR_COMPOSITE_EXCLUSION:
    542        return SkBlendMode::kExclusion;
    543    case DWRITE_COLOR_COMPOSITE_MULTIPLY:
    544        return SkBlendMode::kMultiply;
    545 
    546    case DWRITE_COLOR_COMPOSITE_HSL_HUE:
    547        return SkBlendMode::kHue;
    548    case DWRITE_COLOR_COMPOSITE_HSL_SATURATION:
    549        return SkBlendMode::kSaturation;
    550    case DWRITE_COLOR_COMPOSITE_HSL_COLOR:
    551        return SkBlendMode::kColor;
    552    case DWRITE_COLOR_COMPOSITE_HSL_LUMINOSITY:
    553        return SkBlendMode::kLuminosity;
    554    default:
    555        return SkBlendMode::kDst;
    556    }
    557 }
    558 
    559 inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) {
    560    SkScalar length = b.length();
    561    if (!length) {
    562        return SkPoint();
    563    }
    564    SkPoint bNormalized = b;
    565    bNormalized.normalize();
    566    bNormalized.scale(SkPoint::DotProduct(a, b) / length);
    567    return bNormalized;
    568 }
    569 
    570 // This linear interpolation is used for calculating a truncated color line in special edge cases.
    571 // This interpolation needs to be kept in sync with what the gradient shader would normally do when
    572 // truncating and drawing color lines. When drawing into N32 surfaces, this is expected to be true.
    573 // If that changes, or if we support other color spaces in CPAL tables at some point, this needs to
    574 // be looked at.
    575 D2D1_COLOR_F lerpSkColor(D2D1_COLOR_F c0, D2D1_COLOR_F c1, float t) {
    576    // Due to the floating point calculation in the caller, when interpolating between very narrow
    577    // stops, we may get values outside the interpolation range, guard against these.
    578    if (t < 0) {
    579        return c0;
    580    }
    581    if (t > 1) {
    582        return c1;
    583    }
    584    const auto c0_4f = skvx::float4(c0.r, c0.g, c0.b, c0.a),
    585               c1_4f = skvx::float4(c1.r, c1.g, c1.b, c1.a),
    586                c_4f = c0_4f + (c1_4f - c0_4f) * t;
    587    D2D1_COLOR_F r;
    588    c_4f.store(&r);
    589    return r;
    590 }
    591 
    592 enum TruncateStops {
    593    TruncateStart,
    594    TruncateEnd,
    595 };
    596 // Truncate a vector of color stops at a previously computed stop position and insert at that
    597 // position the color interpolated between the surrounding stops.
    598 void truncateToStopInterpolating(SkScalar zeroRadiusStop,
    599                                 std::vector<D2D1_GRADIENT_STOP>& stops,
    600                                 TruncateStops truncateStops) {
    601    if (stops.size() <= 1u ||
    602        zeroRadiusStop < stops.front().position || stops.back().position < zeroRadiusStop) {
    603        return;
    604    }
    605 
    606    auto lcmp = [](D2D1_GRADIENT_STOP const& stop, SkScalar position) {
    607        return stop.position < position;
    608    };
    609    auto ucmp = [](SkScalar position, D2D1_GRADIENT_STOP const& stop) {
    610        return position < stop.position;
    611    };
    612    size_t afterIndex = (truncateStops == TruncateStart)
    613        ? std::lower_bound(stops.begin(), stops.end(), zeroRadiusStop, lcmp) - stops.begin()
    614        : std::upper_bound(stops.begin(), stops.end(), zeroRadiusStop, ucmp) - stops.begin();
    615 
    616    const float t = (zeroRadiusStop - stops[afterIndex - 1].position) /
    617        (stops[afterIndex].position - stops[afterIndex - 1].position);
    618    D2D1_COLOR_F lerpColor = lerpSkColor(stops[afterIndex - 1].color, stops[afterIndex].color, t);
    619 
    620    if (truncateStops == TruncateStart) {
    621        stops.erase(stops.begin(), stops.begin() + afterIndex);
    622        stops.insert(stops.begin(), { 0, lerpColor });
    623    } else {
    624        stops.erase(stops.begin() + afterIndex, stops.end());
    625        stops.insert(stops.end(), { 1, lerpColor });
    626    }
    627 }
    628 } // namespace
    629 
    630 bool SkScalerContext_DW::drawColorV1Paint(SkCanvas& canvas,
    631                                          IDWritePaintReader& reader,
    632                                          DWRITE_PAINT_ELEMENT const & element)
    633 {
    634    // Helper to draw the specified number of children.
    635    auto drawChildren = [&](uint32_t childCount) -> bool {
    636        if (childCount != 0) {
    637            DWRITE_PAINT_ELEMENT childElement;
    638            HRB(reader.MoveToFirstChild(&childElement));
    639            this->drawColorV1Paint(canvas, reader, childElement);
    640 
    641            for (uint32_t i = 1; i < childCount; i++) {
    642                HRB(reader.MoveToNextSibling(&childElement));
    643                this->drawColorV1Paint(canvas, reader, childElement);
    644            }
    645 
    646            HRB(reader.MoveToParent());
    647        }
    648        return true;
    649    };
    650 
    651    SkAutoCanvasRestore restoreCanvas(&canvas, true);
    652    switch (element.paintType) {
    653    case DWRITE_PAINT_TYPE_NONE:
    654        return true;
    655 
    656    case DWRITE_PAINT_TYPE_LAYERS: {
    657        // A layers paint element has a variable number of children.
    658        return drawChildren(element.paint.layers.childCount);
    659    }
    660 
    661    case DWRITE_PAINT_TYPE_SOLID_GLYPH: {
    662        // A solid glyph paint element has no children.
    663        // glyphIndex, color.value, color.paletteEntryIndex, color.alpha, color.colorAttributes
    664        auto const& solidGlyph = element.paint.solidGlyph;
    665 
    666        SkPathBuilder builder;
    667        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
    668        HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
    669             "Could not create geometry to path converter.");
    670        UINT16 glyphId = SkTo<UINT16>(solidGlyph.glyphIndex);
    671        {
    672            Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
    673            HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
    674                     SkScalarToFloat(fTextSizeRender),
    675                     &glyphId,
    676                     nullptr, //advances
    677                     nullptr, //offsets
    678                     1, //num glyphs
    679                     FALSE, //sideways
    680                     FALSE, //rtl
    681                     geometryToPath.get()),
    682                 "Could not create glyph outline.");
    683        }
    684 
    685        builder.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
    686        SkPaint skPaint;
    687        skPaint.setColor4f(sk_color_from(solidGlyph.color.value));
    688        skPaint.setAntiAlias(fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
    689        canvas.drawPath(builder.detach(), skPaint);
    690        return true;
    691    }
    692 
    693    case DWRITE_PAINT_TYPE_SOLID: {
    694        // A solid paint element has no children.
    695        // value, paletteEntryIndex, alphaMultiplier, colorAttributes
    696        SkPaint skPaint;
    697        skPaint.setColor4f(sk_color_from(element.paint.solid.value));
    698        canvas.drawPaint(skPaint);
    699        return true;
    700    }
    701 
    702    case DWRITE_PAINT_TYPE_LINEAR_GRADIENT: {
    703        auto const& linearGradient = element.paint.linearGradient;
    704        // A linear gradient paint element has no children.
    705        // x0, y0, x1, y1, x2, y2, extendMode, gradientStopCount, [colorStops]
    706 
    707        if (linearGradient.gradientStopCount == 0) {
    708            return true;
    709        }
    710        std::vector<D2D1_GRADIENT_STOP> stops;
    711        stops.resize(linearGradient.gradientStopCount);
    712 
    713        // If success stops will be ordered.
    714        HRBM(reader.GetGradientStops(0, stops.size(), stops.data()),
    715             "Could not get linear gradient stops.");
    716        SkPaint skPaint;
    717        if (stops.size() == 1) {
    718            skPaint.setColor4f(sk_color_from(stops[0].color));
    719            canvas.drawPaint(skPaint);
    720            return true;
    721        }
    722        SkPoint linePositions[2] = { {linearGradient.x0, linearGradient.y0},
    723                                     {linearGradient.x1, linearGradient.y1} };
    724        SkPoint p0 = linePositions[0];
    725        SkPoint p1 = linePositions[1];
    726        SkPoint p2 = SkPoint::Make(linearGradient.x2, linearGradient.y2);
    727 
    728        // If p0p1 or p0p2 are degenerate probably nothing should be drawn.
    729        // If p0p1 and p0p2 are parallel then one side is the first color and the other side is
    730        // the last color, depending on the direction.
    731        // For now, just use the first color.
    732        if (p1 == p0 || p2 == p0 || !SkPoint::CrossProduct(p1 - p0, p2 - p0)) {
    733            skPaint.setColor4f(sk_color_from(stops[0].color));
    734            canvas.drawPaint(skPaint);
    735            return true;
    736        }
    737 
    738        // Follow implementation note in nanoemoji:
    739        // https://github.com/googlefonts/nanoemoji/blob/0ac6e7bb4d8202db692574d8530a9b643f1b3b3c/src/nanoemoji/svg.py#L188
    740        // to compute a new gradient end point P3 as the orthogonal
    741        // projection of the vector from p0 to p1 onto a line perpendicular
    742        // to line p0p2 and passing through p0.
    743        SkVector perpendicularToP2P0 = (p2 - p0);
    744        perpendicularToP2P0 = SkPoint::Make( perpendicularToP2P0.y(),
    745                                            -perpendicularToP2P0.x());
    746        SkVector p3 = p0 + SkVectorProjection((p1 - p0), perpendicularToP2P0);
    747        linePositions[1] = p3;
    748 
    749        // Project/scale points according to stop extrema along p0p3 line,
    750        // p3 being the result of the projection above, then scale stops to
    751        // to [0, 1] range so that repeat modes work.  The Skia linear
    752        // gradient shader performs the repeat modes over the 0 to 1 range,
    753        // that's why we need to scale the stops to within that range.
    754        SkTileMode tileMode = sk_tile_mode_from(SkTo<D2D1_EXTEND_MODE>(linearGradient.extendMode));
    755        SkScalar colorStopRange = stops.back().position - stops.front().position;
    756        // If the color stops are all at the same offset position, repeat and reflect modes
    757        // become meaningless.
    758        if (colorStopRange == 0.f) {
    759            if (tileMode != SkTileMode::kClamp) {
    760                //skPaint.setColor(SK_ColorTRANSPARENT);
    761                return true;
    762            } else {
    763                // Insert duplicated fake color stop in pad case at +1.0f to enable the projection
    764                // of circles for an originally 0-length color stop range. Adding this stop will
    765                // paint the equivalent gradient, because: All font specified color stops are in the
    766                // same spot, mode is pad, so everything before this spot is painted with the first
    767                // color, everything after this spot is painted with the last color. Not adding this
    768                // stop will skip the projection and result in specifying non-normalized color stops
    769                // to the shader.
    770                stops.push_back({ stops.back().position + 1.0f, stops.back().color });
    771                colorStopRange = 1.0f;
    772            }
    773        }
    774        SkASSERT(colorStopRange != 0.f);
    775 
    776        // If the colorStopRange is 0 at this point, the default behavior of the shader is to
    777        // clamp to 1 color stops that are above 1, clamp to 0 for color stops that are below 0,
    778        // and repeat the outer color stops at 0 and 1 if the color stops are inside the
    779        // range. That will result in the correct rendering.
    780        if ((colorStopRange != 1 || stops.front().position != 0.f)) {
    781            SkVector p0p3 = p3 - p0;
    782            SkVector p0Offset = p0p3;
    783            p0Offset.scale(stops.front().position);
    784            SkVector p1Offset = p0p3;
    785            p1Offset.scale(stops.back().position);
    786 
    787            linePositions[0] = p0 + p0Offset;
    788            linePositions[1] = p0 + p1Offset;
    789 
    790            SkScalar scaleFactor = 1 / colorStopRange;
    791            SkScalar startOffset = stops.front().position;
    792            for (D2D1_GRADIENT_STOP& stop : stops) {
    793                stop.position = (stop.position - startOffset) * scaleFactor;
    794            }
    795        }
    796 
    797        std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
    798        std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
    799        for (size_t i = 0; i < stops.size(); ++i) {
    800            skColors[i] = sk_color_from(stops[i].color);
    801            skStops[i] = stops[i].position;
    802        }
    803 
    804        sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
    805            linePositions,
    806            skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
    807            tileMode,
    808            SkGradientShader::Interpolation{
    809                SkGradientShader::Interpolation::InPremul::kNo,
    810                SkGradientShader::Interpolation::ColorSpace::kSRGB,
    811                SkGradientShader::Interpolation::HueMethod::kShorter
    812            },
    813            nullptr));
    814 
    815        SkASSERT(shader);
    816        // An opaque color is needed to ensure the gradient is not modulated by alpha.
    817        skPaint.setColor(SK_ColorBLACK);
    818        skPaint.setShader(shader);
    819        canvas.drawPaint(skPaint);
    820        return true;
    821    }
    822 
    823    case DWRITE_PAINT_TYPE_RADIAL_GRADIENT: {
    824        auto const& radialGradient = element.paint.radialGradient;
    825        // A radial gradient paint element has no children.
    826        // x0, y0, radius0, x1, y1, radius1, extendMode, gradientStopCount, [colorsStops]
    827 
    828        SkPoint start = SkPoint::Make(radialGradient.x0, radialGradient.y0);
    829        SkScalar startRadius = radialGradient.radius0;
    830        SkPoint end = SkPoint::Make(radialGradient.x1, radialGradient.y1);
    831        SkScalar endRadius = radialGradient.radius1;
    832 
    833        if (radialGradient.gradientStopCount == 0) {
    834            return true;
    835        }
    836        std::vector<D2D1_GRADIENT_STOP> stops;
    837        stops.resize(radialGradient.gradientStopCount);
    838 
    839        // If success stops will be ordered.
    840        HRBM(reader.GetGradientStops(0, stops.size(), stops.data()),
    841             "Could not get radial gradient stops.");
    842        SkPaint skPaint;
    843        if (stops.size() == 1) {
    844            skPaint.setColor4f(sk_color_from(stops[0].color));
    845            canvas.drawPaint(skPaint);
    846            return true;
    847        }
    848 
    849        SkScalar colorStopRange = stops.back().position - stops.front().position;
    850        SkTileMode tileMode = sk_tile_mode_from(SkTo<D2D1_EXTEND_MODE>(radialGradient.extendMode));
    851 
    852        if (colorStopRange == 0.f) {
    853            if (tileMode != SkTileMode::kClamp) {
    854                //skPaint.setColor(SK_ColorTRANSPARENT);
    855                return true;
    856            } else {
    857                // Insert duplicated fake color stop in pad case at +1.0f to enable the projection
    858                // of circles for an originally 0-length color stop range. Adding this stop will
    859                // paint the equivalent gradient, because: All font specified color stops are in the
    860                // same spot, mode is pad, so everything before this spot is painted with the first
    861                // color, everything after this spot is painted with the last color. Not adding this
    862                // stop will skip the projection and result in specifying non-normalized color stops
    863                // to the shader.
    864                stops.push_back({ stops.back().position + 1.0f, stops.back().color });
    865                colorStopRange = 1.0f;
    866            }
    867        }
    868        SkASSERT(colorStopRange != 0.f);
    869 
    870        // If the colorStopRange is 0 at this point, the default behavior of the shader is to
    871        // clamp to 1 color stops that are above 1, clamp to 0 for color stops that are below 0,
    872        // and repeat the outer color stops at 0 and 1 if the color stops are inside the
    873        // range. That will result in the correct rendering.
    874        if (colorStopRange != 1 || stops.front().position != 0.f) {
    875            // For the Skia two-point caonical shader to understand the
    876            // COLRv1 color stops we need to scale stops to 0 to 1 range and
    877            // interpolate new centers and radii. Otherwise the shader
    878            // clamps stops outside the range to 0 and 1 (larger interval)
    879            // or repeats the outer stops at 0 and 1 if the (smaller
    880            // interval).
    881            SkVector startToEnd = end - start;
    882            SkScalar radiusDiff = endRadius - startRadius;
    883            SkScalar scaleFactor = 1 / colorStopRange;
    884            SkScalar stopsStartOffset = stops.front().position;
    885 
    886            SkVector startOffset = startToEnd;
    887            startOffset.scale(stops.front().position);
    888            SkVector endOffset = startToEnd;
    889            endOffset.scale(stops.back().position);
    890 
    891            // The order of the following computations is important in order to avoid
    892            // overwriting start or startRadius before the second reassignment.
    893            end = start + endOffset;
    894            start = start + startOffset;
    895            endRadius = startRadius + radiusDiff * stops.back().position;
    896            startRadius = startRadius + radiusDiff * stops.front().position;
    897 
    898            for (auto& stop : stops) {
    899                stop.position = (stop.position - stopsStartOffset) * scaleFactor;
    900            }
    901        }
    902 
    903        // For negative radii, interpolation is needed to prepare parameters suitable
    904        // for invoking the shader. Implementation below as resolution discussed in
    905        // https://github.com/googlefonts/colr-gradients-spec/issues/367.
    906        // Truncate to manually interpolated color for tile mode clamp, otherwise
    907        // calculate positive projected circles.
    908        if (startRadius < 0 || endRadius < 0) {
    909            if (startRadius == endRadius && startRadius < 0) {
    910                //skPaint.setColor(SK_ColorTRANSPARENT);
    911                return true;
    912            }
    913 
    914            if (tileMode == SkTileMode::kClamp) {
    915                SkVector startToEnd = end - start;
    916                SkScalar radiusDiff = endRadius - startRadius;
    917                SkScalar zeroRadiusStop = 0.f;
    918                TruncateStops truncateSide = TruncateStart;
    919                if (startRadius < 0) {
    920                    truncateSide = TruncateStart;
    921 
    922                    // Compute color stop position where radius is = 0.  After the scaling
    923                    // of stop positions to the normal 0,1 range that we have done above,
    924                    // the size of the radius as a function of the color stops is: r(x) = r0
    925                    // + x*(r1-r0) Solving this function for r(x) = 0, we get: x = -r0 /
    926                    // (r1-r0)
    927                    zeroRadiusStop = -startRadius / (endRadius - startRadius);
    928                    startRadius = 0.f;
    929                    SkVector startEndDiff = end - start;
    930                    startEndDiff.scale(zeroRadiusStop);
    931                    start = start + startEndDiff;
    932                }
    933 
    934                if (endRadius < 0) {
    935                    truncateSide = TruncateEnd;
    936                    zeroRadiusStop = -startRadius / (endRadius - startRadius);
    937                    endRadius = 0.f;
    938                    SkVector startEndDiff = end - start;
    939                    startEndDiff.scale(1 - zeroRadiusStop);
    940                    end = end - startEndDiff;
    941                }
    942 
    943                if (!(startRadius == 0 && endRadius == 0)) {
    944                    truncateToStopInterpolating(zeroRadiusStop, stops, truncateSide);
    945                } else {
    946                    // If both radii have become negative and where clamped to 0, we need to
    947                    // produce a single color cone, otherwise the shader colors the whole
    948                    // plane in a single color when two radii are specified as 0.
    949                    if (radiusDiff > 0) {
    950                        end = start + startToEnd;
    951                        endRadius = radiusDiff;
    952                        stops.erase(stops.begin(), stops.end() - 1);
    953                    } else {
    954                        start -= startToEnd;
    955                        startRadius = -radiusDiff;
    956                        stops.erase(stops.begin() + 1, stops.end());
    957                    }
    958                }
    959            } else {
    960                if (startRadius < 0 || endRadius < 0) {
    961                    auto roundIntegerMultiple = [](SkScalar factorZeroCrossing,
    962                        SkTileMode tileMode) {
    963                            int roundedMultiple = factorZeroCrossing > 0
    964                                ? ceilf(factorZeroCrossing)
    965                                : floorf(factorZeroCrossing) - 1;
    966                            if (tileMode == SkTileMode::kMirror && roundedMultiple % 2 != 0) {
    967                                roundedMultiple += roundedMultiple < 0 ? -1 : 1;
    968                            }
    969                            return roundedMultiple;
    970                    };
    971 
    972                    SkVector startToEnd = end - start;
    973                    SkScalar radiusDiff = endRadius - startRadius;
    974                    SkScalar factorZeroCrossing = (startRadius / (startRadius - endRadius));
    975                    bool inRange = 0.f <= factorZeroCrossing && factorZeroCrossing <= 1.0f;
    976                    SkScalar direction = inRange && radiusDiff < 0 ? -1.0f : 1.0f;
    977                    SkScalar circleProjectionFactor =
    978                        roundIntegerMultiple(factorZeroCrossing * direction, tileMode);
    979                    startToEnd.scale(circleProjectionFactor);
    980                    startRadius += circleProjectionFactor * radiusDiff;
    981                    endRadius += circleProjectionFactor * radiusDiff;
    982                    start += startToEnd;
    983                    end += startToEnd;
    984                }
    985            }
    986        }
    987 
    988        std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
    989        std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
    990        for (size_t i = 0; i < stops.size(); ++i) {
    991            skColors[i] = sk_color_from(stops[i].color);
    992            skStops[i] = stops[i].position;
    993        }
    994 
    995        // An opaque color is needed to ensure the gradient is not modulated by alpha.
    996        skPaint.setColor(SK_ColorBLACK);
    997        skPaint.setShader(SkGradientShader::MakeTwoPointConical(
    998            start, startRadius, end, endRadius,
    999            skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
   1000            tileMode,
   1001            SkGradientShader::Interpolation{
   1002                SkGradientShader::Interpolation::InPremul::kNo,
   1003                SkGradientShader::Interpolation::ColorSpace::kSRGB,
   1004                SkGradientShader::Interpolation::HueMethod::kShorter
   1005            },
   1006            nullptr));
   1007        canvas.drawPaint(skPaint);
   1008        return true;
   1009    }
   1010 
   1011    case DWRITE_PAINT_TYPE_SWEEP_GRADIENT: {
   1012        auto const& sweepGradient = element.paint.sweepGradient;
   1013        // A sweep gradient paint element has no children.
   1014        // centerX, centerY, startAngle, endAngle, extendMode, gradientStopCount, [colorStops]
   1015 
   1016        if (sweepGradient.gradientStopCount == 0) {
   1017            return true;
   1018        }
   1019        std::vector<D2D1_GRADIENT_STOP> stops;
   1020        stops.resize(sweepGradient.gradientStopCount);
   1021 
   1022        // If success stops will be ordered.
   1023        HRBM(reader.GetGradientStops(0, stops.size(), stops.data()),
   1024             "Could not get sweep gradient stops");
   1025        SkPaint skPaint;
   1026        if (stops.size() == 1) {
   1027            skPaint.setColor4f(sk_color_from(stops[0].color));
   1028            canvas.drawPaint(skPaint);
   1029            return true;
   1030        }
   1031 
   1032        SkPoint center = SkPoint::Make(sweepGradient.centerX, sweepGradient.centerY);
   1033 
   1034        SkScalar startAngle = sweepGradient.startAngle;
   1035        SkScalar endAngle = sweepGradient.endAngle;
   1036        // OpenType 1.9.1 adds a shift to the angle to ease specification of a 0 to 360
   1037        // degree sweep. This appears to already be applied by DW.
   1038        //startAngle += 180.0f;
   1039        //endAngle += 180.0f;
   1040 
   1041        // An opaque color is needed to ensure the gradient is not modulated by alpha.
   1042        skPaint.setColor(SK_ColorBLACK);
   1043 
   1044        // New (Var)SweepGradient implementation compliant with OpenType 1.9.1 from here.
   1045 
   1046        // The shader expects stops from 0 to 1, so we need to account for
   1047        // minimum and maximum stop positions being different from 0 and
   1048        // 1. We do that by scaling minimum and maximum stop positions to
   1049        // the 0 to 1 interval and scaling the angles inverse proportionally.
   1050 
   1051        // 1) Scale angles to their equivalent positions if stops were from 0 to 1.
   1052 
   1053        SkScalar sectorAngle = endAngle - startAngle;
   1054        SkTileMode tileMode = sk_tile_mode_from(SkTo<D2D1_EXTEND_MODE>(sweepGradient.extendMode));
   1055        if (sectorAngle == 0 && tileMode != SkTileMode::kClamp) {
   1056            // "If the ColorLine's extend mode is reflect or repeat and start and end angle
   1057            // are equal, nothing is drawn.".
   1058            //skPaint.setColor(SK_ColorTRANSPARENT);
   1059            return true;
   1060        }
   1061 
   1062        SkScalar startAngleScaled = startAngle + sectorAngle * stops.front().position;
   1063        SkScalar endAngleScaled = startAngle + sectorAngle * stops.back().position;
   1064 
   1065        // 2) Scale stops accordingly to 0 to 1 range.
   1066 
   1067        float colorStopRange = stops.back().position - stops.front().position;
   1068        if (colorStopRange == 0.f) {
   1069            if (tileMode != SkTileMode::kClamp) {
   1070                //skPaint.setColor(SK_ColorTRANSPARENT);
   1071                return true;
   1072            } else {
   1073                // Insert duplicated fake color stop in pad case at +1.0f to feed the shader correct
   1074                // values and enable painting a pad sweep gradient with two colors. Adding this stop
   1075                // will paint the equivalent gradient, because: All font specified color stops are
   1076                // in the same spot, mode is pad, so everything before this spot is painted with the
   1077                // first color, everything after this spot is painted with the last color. Not
   1078                // adding this stop will skip the projection and result in specifying non-normalized
   1079                // color stops to the shader.
   1080                stops.push_back({ stops.back().position + 1.0f, stops.back().color });
   1081                colorStopRange = 1.0f;
   1082            }
   1083        }
   1084 
   1085        SkScalar scaleFactor = 1 / colorStopRange;
   1086        SkScalar startOffset = stops.front().position;
   1087 
   1088        for (D2D1_GRADIENT_STOP& stop : stops) {
   1089            stop.position = (stop.position - startOffset) * scaleFactor;
   1090        }
   1091 
   1092        /* https://docs.microsoft.com/en-us/typography/opentype/spec/colr#sweep-gradients
   1093        * "The angles are expressed in counter-clockwise degrees from
   1094        * the direction of the positive x-axis on the design
   1095        * grid. [...]  The color line progresses from the start angle
   1096        * to the end angle in the counter-clockwise direction;" -
   1097        * Convert angles and stops from counter-clockwise to clockwise
   1098        * for the shader if the gradient is not already reversed due to
   1099        * start angle being larger than end angle. */
   1100        startAngleScaled = 360.f - startAngleScaled;
   1101        endAngleScaled = 360.f - endAngleScaled;
   1102        if (startAngleScaled >= endAngleScaled) {
   1103            std::swap(startAngleScaled, endAngleScaled);
   1104            std::reverse(stops.begin(), stops.end());
   1105            for (auto& stop : stops) {
   1106                stop.position = 1.0f - stop.position;
   1107            }
   1108        }
   1109 
   1110        std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
   1111        std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
   1112        for (size_t i = 0; i < stops.size(); ++i) {
   1113            skColors[i] = sk_color_from(stops[i].color);
   1114            skStops[i] = stops[i].position;
   1115        }
   1116 
   1117        skPaint.setShader(SkGradientShader::MakeSweep(
   1118            center.x(), center.y(),
   1119            skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
   1120            tileMode,
   1121            startAngleScaled, endAngleScaled,
   1122            SkGradientShader::Interpolation{
   1123                SkGradientShader::Interpolation::InPremul::kNo,
   1124                SkGradientShader::Interpolation::ColorSpace::kSRGB,
   1125                SkGradientShader::Interpolation::HueMethod::kShorter
   1126            },
   1127            nullptr));
   1128        canvas.drawPaint(skPaint);
   1129        return true;
   1130    }
   1131 
   1132    case DWRITE_PAINT_TYPE_GLYPH: {
   1133        // A glyph paint element has one child, which is the fill for the glyph shape glyphIndex.
   1134        SkPathBuilder builder;
   1135        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
   1136        HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
   1137             "Could not create geometry to path converter.");
   1138        UINT16 glyphId = SkTo<UINT16>(element.paint.glyph.glyphIndex);
   1139        {
   1140            Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
   1141            HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
   1142                     SkScalarToFloat(fTextSizeRender),
   1143                     &glyphId,
   1144                     nullptr, //advances
   1145                     nullptr, //offsets
   1146                     1, //num glyphs
   1147                     FALSE, //sideways
   1148                     FALSE, //rtl
   1149                     geometryToPath.get()),
   1150                 "Could not create glyph outline.");
   1151        }
   1152 
   1153        builder.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
   1154        canvas.clipPath(builder.detach(), fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
   1155 
   1156        drawChildren(1);
   1157        return true;
   1158    }
   1159 
   1160    case DWRITE_PAINT_TYPE_COLOR_GLYPH: {
   1161        auto const& colorGlyph = element.paint.colorGlyph;
   1162        // A color glyph paint element has one child, the root of the paint tree for glyphIndex.
   1163        // glyphIndex, clipBox
   1164        if (D2D_RECT_F_is_empty(colorGlyph.clipBox)) {
   1165            // Does not have a clip box
   1166        } else {
   1167            SkRect r = sk_rect_from(colorGlyph.clipBox);
   1168            canvas.clipRect(r, fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
   1169        }
   1170 
   1171        drawChildren(1);
   1172        return true;
   1173    }
   1174 
   1175    case DWRITE_PAINT_TYPE_TRANSFORM: {
   1176        // A transform paint element always has one child, the transformed content.
   1177        canvas.concat(sk_matrix_from(element.paint.transform));
   1178        drawChildren(1);
   1179        return true;
   1180    }
   1181 
   1182    case DWRITE_PAINT_TYPE_COMPOSITE: {
   1183        // A composite paint element has two children, the source and destination of the operation.
   1184 
   1185        SkPaint blendModePaint;
   1186        blendModePaint.setBlendMode(sk_blend_mode_from(element.paint.composite.mode));
   1187 
   1188        SkAutoCanvasRestore acr(&canvas, false);
   1189 
   1190        // Need to visit the second child first and do savelayers, so manually handle children.
   1191        DWRITE_PAINT_ELEMENT sourceElement;
   1192        DWRITE_PAINT_ELEMENT backdropElement;
   1193 
   1194        HRBM(reader.MoveToFirstChild(&sourceElement), "Could not move to child.");
   1195        HRBM(reader.MoveToNextSibling(&backdropElement), "Could not move to sibiling.");
   1196        canvas.saveLayer(nullptr, nullptr);
   1197        this->drawColorV1Paint(canvas, reader, backdropElement);
   1198 
   1199        HRBM(reader.MoveToParent(), "Could not move to parent.");
   1200        HRBM(reader.MoveToFirstChild(&sourceElement), "Could not move to child.");
   1201        canvas.saveLayer(nullptr, &blendModePaint);
   1202        this->drawColorV1Paint(canvas, reader, sourceElement);
   1203 
   1204        HRBM(reader.MoveToParent(), "Could not move to parent.");
   1205 
   1206        return true;
   1207    }
   1208 
   1209    default:
   1210        return false;
   1211    }
   1212 }
   1213 
   1214 bool SkScalerContext_DW::drawColorV1Image(const SkGlyph& glyph, SkCanvas& canvas) {
   1215    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   1216    IDWriteFontFace7* fontFace = typeface->fDWriteFontFace7/*.get()*/;
   1217    if (!fontFace) {
   1218        return false;
   1219    }
   1220    UINT32 glyphIndex = glyph.getGlyphID();
   1221 
   1222    SkTScopedComPtr<IDWritePaintReader> paintReader;
   1223    HRBM(fontFace->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
   1224                                     DWRITE_PAINT_FEATURE_LEVEL_COLR_V1,
   1225                                     &paintReader),
   1226         "Could not create paint reader.");
   1227 
   1228    DWRITE_PAINT_ELEMENT paintElement;
   1229    D2D_RECT_F clipBox;
   1230    DWRITE_PAINT_ATTRIBUTES attributes;
   1231    HRBM(paintReader->SetCurrentGlyph(glyphIndex, &paintElement, &clipBox, &attributes),
   1232         "Could not set current glyph.");
   1233 
   1234    if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
   1235        // Does not have paint layers, try another format.
   1236        return false;
   1237    }
   1238 
   1239    // All coordinates (including top level clip) are reported in "em"s (1 == em).
   1240    // Size up all em units to the current size and transform.
   1241    // Get glyph paths at render size, divide out the render size to get em units.
   1242 
   1243    SkMatrix matrix = fSkXform;
   1244    SkScalar scale = fTextSizeRender;
   1245    matrix.preScale(scale, scale);
   1246    if (this->isSubpixel()) {
   1247        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
   1248                             SkFixedToScalar(glyph.getSubYFixed()));
   1249    }
   1250    canvas.concat(matrix);
   1251 
   1252    if (D2D_RECT_F_is_empty(clipBox)) {
   1253        // Does not have a clip box
   1254    } else {
   1255        canvas.clipRect(sk_rect_from(clipBox));
   1256    }
   1257 
   1258    // The DirectWrite interface returns resolved colors if these are provided.
   1259    // Indexes and alphas are reported but there is no reason to duplicate the color calculation.
   1260    paintReader->SetTextColor(dw_color_from(SkColor4f::FromColor(fRec.fForegroundColor)));
   1261    paintReader->SetCustomColorPalette(typeface->fDWPalette.get(), typeface->fPaletteEntryCount);
   1262 
   1263    return this->drawColorV1Paint(canvas, *paintReader, paintElement);
   1264 }
   1265 
   1266 bool SkScalerContext_DW::generateColorV1Image(const SkGlyph& glyph, void* imageBuffer) {
   1267    SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
   1268 
   1269    SkBitmap dstBitmap;
   1270    // TODO: mark this as sRGB when the blits will be sRGB.
   1271    dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
   1272                      kN32_SkColorType, kPremul_SkAlphaType),
   1273                      glyph.rowBytes());
   1274    dstBitmap.setPixels(imageBuffer);
   1275 
   1276    SkCanvas canvas(dstBitmap);
   1277    if constexpr (kSkShowTextBlitCoverage) {
   1278        canvas.clear(0x33FF0000);
   1279    } else {
   1280        canvas.clear(SK_ColorTRANSPARENT);
   1281    }
   1282    canvas.translate(-SkIntToScalar(glyph.left()), -SkIntToScalar(glyph.top()));
   1283 
   1284    return this->drawColorV1Image(glyph, canvas);
   1285 }
   1286 
   1287 bool SkScalerContext_DW::generateColorV1PaintBounds(
   1288    SkMatrix* ctm, SkRect* bounds,
   1289    IDWritePaintReader& reader, DWRITE_PAINT_ELEMENT const & element)
   1290 {
   1291    // Helper to iterate over the specified number of children.
   1292    auto boundChildren = [&](UINT32 childCount) -> bool {
   1293        if (childCount == 0) {
   1294            return true;
   1295        }
   1296        DWRITE_PAINT_ELEMENT childElement;
   1297        HRB(reader.MoveToFirstChild(&childElement));
   1298        this->generateColorV1PaintBounds(ctm, bounds, reader, childElement);
   1299 
   1300        for (uint32_t i = 1; i < childCount; ++i) {
   1301            HRB(reader.MoveToNextSibling(&childElement));
   1302            this->generateColorV1PaintBounds(ctm, bounds, reader, childElement);
   1303        }
   1304 
   1305        HRB(reader.MoveToParent());
   1306        return true;
   1307    };
   1308 
   1309    SkMatrix restoreMatrix = *ctm;
   1310    SK_AT_SCOPE_EXIT(*ctm = restoreMatrix);
   1311 
   1312    switch (element.paintType) {
   1313    case DWRITE_PAINT_TYPE_NONE:
   1314        return false;
   1315 
   1316    case DWRITE_PAINT_TYPE_LAYERS: {
   1317        // A layers paint element has a variable number of children.
   1318        return boundChildren(element.paint.layers.childCount);
   1319    }
   1320 
   1321    case DWRITE_PAINT_TYPE_SOLID_GLYPH: {
   1322        // A solid glyph paint element has no children.
   1323        // glyphIndex, color.value, color.paletteEntryIndex, color.alpha, color.colorAttributes
   1324 
   1325        SkPathBuilder builder;
   1326        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
   1327        HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
   1328            "Could not create geometry to path converter.");
   1329        UINT16 glyphId = SkTo<UINT16>(element.paint.solidGlyph.glyphIndex);
   1330        {
   1331            Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
   1332            HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
   1333                    SkScalarToFloat(fTextSizeRender),
   1334                    &glyphId,
   1335                    nullptr, //advances
   1336                    nullptr, //offsets
   1337                    1, //num glyphs
   1338                    FALSE, //sideways
   1339                    FALSE, //rtl
   1340                    geometryToPath.get()),
   1341                "Could not create glyph outline.");
   1342        }
   1343 
   1344        SkMatrix t = *ctm;
   1345        t.preConcat(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
   1346        builder.transform(t);
   1347        bounds->join(builder.detach().getBounds());
   1348        return true;
   1349    }
   1350 
   1351    case DWRITE_PAINT_TYPE_SOLID: {
   1352        return true;
   1353    }
   1354 
   1355    case DWRITE_PAINT_TYPE_LINEAR_GRADIENT: {
   1356        return true;
   1357    }
   1358 
   1359    case DWRITE_PAINT_TYPE_RADIAL_GRADIENT: {
   1360        return true;
   1361    }
   1362 
   1363    case DWRITE_PAINT_TYPE_SWEEP_GRADIENT: {
   1364        return true;
   1365    }
   1366 
   1367    case DWRITE_PAINT_TYPE_GLYPH: {
   1368        // A glyph paint element has one child, which is the fill for the glyph shape glyphIndex.
   1369        SkPathBuilder builder;
   1370        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
   1371        HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
   1372             "Could not create geometry to path converter.");
   1373        UINT16 glyphId = SkTo<UINT16>(element.paint.glyph.glyphIndex);
   1374        {
   1375            Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
   1376            HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
   1377                     SkScalarToFloat(fTextSizeRender),
   1378                     &glyphId,
   1379                     nullptr, //advances
   1380                     nullptr, //offsets
   1381                     1, //num glyphs
   1382                     FALSE, //sideways
   1383                     FALSE, //rtl
   1384                     geometryToPath.get()),
   1385                 "Could not create glyph outline.");
   1386        }
   1387 
   1388        SkMatrix t = *ctm;
   1389        t.preConcat(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
   1390        builder.transform(t);
   1391        bounds->join(builder.detach().getBounds());
   1392        return true;
   1393    }
   1394 
   1395    case DWRITE_PAINT_TYPE_COLOR_GLYPH: {
   1396        // A color glyph paint element has one child, which is the root
   1397        // of the paint tree for the glyph specified by glyphIndex.
   1398        auto const& colorGlyph = element.paint.colorGlyph;
   1399        if (D2D_RECT_F_is_empty(colorGlyph.clipBox)) {
   1400            // Does not have a clip box
   1401            return boundChildren(1);
   1402        }
   1403        SkRect r = sk_rect_from(colorGlyph.clipBox);
   1404        ctm->mapRect(r);
   1405        bounds->join(r);
   1406        return true;
   1407    }
   1408 
   1409    case DWRITE_PAINT_TYPE_TRANSFORM: {
   1410        // A transform paint element always has one child, which is the transformed content.
   1411        ctm->preConcat(sk_matrix_from(element.paint.transform));
   1412        return boundChildren(1);
   1413    }
   1414 
   1415    case DWRITE_PAINT_TYPE_COMPOSITE: {
   1416        // A composite paint element has two children, the source and destination of the operation.
   1417        return boundChildren(2);
   1418    }
   1419 
   1420    default:
   1421        return false;
   1422    }
   1423 }
   1424 
   1425 bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph& glyph, SkRect* bounds) {
   1426    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   1427    IDWriteFontFace7* fontFace = typeface->fDWriteFontFace7/*.get()*/;
   1428    if (!fontFace) {
   1429        return false;
   1430    }
   1431    UINT32 glyphIndex = glyph.getGlyphID();
   1432 
   1433    SkTScopedComPtr<IDWritePaintReader> paintReader;
   1434    HRESULT hr;
   1435    // No message on failure here, since this will fail if the font has no color glyphs.
   1436    hr = fontFace->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
   1437                                     DWRITE_PAINT_FEATURE_LEVEL_COLR_V1,
   1438                                     &paintReader);
   1439    if (FAILED(hr)) {
   1440        return false;
   1441    }
   1442 
   1443    DWRITE_PAINT_ELEMENT paintElement;
   1444    D2D_RECT_F clipBox;
   1445    DWRITE_PAINT_ATTRIBUTES attributes;
   1446    // If the glyph is not color this will succeed but return paintType NONE.
   1447    HRBM(paintReader->SetCurrentGlyph(glyphIndex, &paintElement, &clipBox, &attributes),
   1448         "Could not set the current glyph.");
   1449 
   1450    if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
   1451        // Does not have paint layers, try another format.
   1452        return false;
   1453    }
   1454 
   1455    // All coordinates (including top level clip) are reported in "em"s (1 == em).
   1456    // Size up all em units to the current size and transform.
   1457 
   1458    SkMatrix matrix = fSkXform;
   1459    SkScalar scale = fTextSizeRender;
   1460    matrix.preScale(scale, scale);
   1461    if (this->isSubpixel()) {
   1462        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
   1463                             SkFixedToScalar(glyph.getSubYFixed()));
   1464    }
   1465 
   1466    SkRect r;
   1467    if (D2D_RECT_F_is_empty(clipBox)) {
   1468        // Does not have a clip box.
   1469        r = SkRect::MakeEmpty();
   1470        if (!this->generateColorV1PaintBounds(&matrix, &r, *paintReader, paintElement)) {
   1471            return false;
   1472        }
   1473        *bounds = r;
   1474    } else {
   1475        *bounds = sk_rect_from(clipBox);
   1476        matrix.mapRect(bounds);
   1477    }
   1478    return true;
   1479 }
   1480 
   1481 #else  // !SK_DISABLE_DIRECTWRITE_COLRv1 && (DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN))
   1482 
   1483 bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph&, SkRect*) { return false; }
   1484 bool SkScalerContext_DW::generateColorV1Image(const SkGlyph&, void*) { return false; }
   1485 bool SkScalerContext_DW::drawColorV1Image(const SkGlyph&, SkCanvas&) { return false; }
   1486 
   1487 #endif  // !SK_DISABLE_DIRECTWRITE_COLRv1 && (DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN))
   1488 
   1489 bool SkScalerContext_DW::setAdvance(const SkGlyph& glyph, SkVector* advance) {
   1490    *advance = {0, 0};
   1491    UINT16 glyphId = glyph.getGlyphID();
   1492    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   1493 
   1494    // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0.
   1495    // For consistency with all other backends, treat out of range glyph ids as an error.
   1496    if (fGlyphCount <= glyphId) {
   1497        return false;
   1498    }
   1499 
   1500    DWRITE_GLYPH_METRICS gm;
   1501 
   1502    if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
   1503        DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
   1504    {
   1505        Exclusive l(maybe_dw_mutex(*typeface));
   1506        HRBM(typeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
   1507                 fTextSizeMeasure,
   1508                 1.0f, // pixelsPerDip
   1509                 // This parameter does not act like the lpmat2 parameter to GetGlyphOutlineW.
   1510                 // If it did then GsA here and G_inv below to mapVectors.
   1511                 nullptr,
   1512                 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
   1513                 &glyphId, 1,
   1514                 &gm),
   1515             "Could not get gdi compatible glyph metrics.");
   1516    } else {
   1517        Exclusive l(maybe_dw_mutex(*typeface));
   1518        HRBM(typeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
   1519             "Could not get design metrics.");
   1520    }
   1521 
   1522    DWRITE_FONT_METRICS dwfm;
   1523    {
   1524        Shared l(maybe_dw_mutex(*typeface));
   1525        typeface->fDWriteFontFace->GetMetrics(&dwfm);
   1526    }
   1527    SkScalar advanceX = fTextSizeMeasure * gm.advanceWidth / dwfm.designUnitsPerEm;
   1528 
   1529    *advance = { advanceX, 0 };
   1530    if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
   1531        DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
   1532    {
   1533        // DirectWrite produced 'compatible' metrics, but while close,
   1534        // the end result is not always an integer as it would be with GDI.
   1535        advance->fX = SkScalarRoundToScalar(advance->fX);
   1536    }
   1537    *advance = fSkXform.mapVector(*advance);
   1538    return true;
   1539 }
   1540 
   1541 bool SkScalerContext_DW::generateDWMetrics(const SkGlyph& glyph,
   1542                                           DWRITE_RENDERING_MODE renderingMode,
   1543                                           DWRITE_TEXTURE_TYPE textureType,
   1544                                           SkRect* bounds)
   1545 {
   1546    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   1547 
   1548    //Measure raster size.
   1549    fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
   1550    fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
   1551 
   1552    FLOAT advance = 0;
   1553 
   1554    UINT16 glyphId = glyph.getGlyphID();
   1555 
   1556    DWRITE_GLYPH_OFFSET offset;
   1557    offset.advanceOffset = 0.0f;
   1558    offset.ascenderOffset = 0.0f;
   1559 
   1560    DWRITE_GLYPH_RUN run;
   1561    run.glyphCount = 1;
   1562    run.glyphAdvances = &advance;
   1563    run.fontFace = typeface->fDWriteFontFace.get();
   1564    run.fontEmSize = SkScalarToFloat(fTextSizeRender);
   1565    run.bidiLevel = 0;
   1566    run.glyphIndices = &glyphId;
   1567    run.isSideways = FALSE;
   1568    run.glyphOffsets = &offset;
   1569 
   1570    SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
   1571    {
   1572        Exclusive l(maybe_dw_mutex(*typeface));
   1573        // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
   1574        if (typeface->fFactory2 &&
   1575                (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
   1576                 fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
   1577        {
   1578            HRBM(typeface->fFactory2->CreateGlyphRunAnalysis(
   1579                    &run,
   1580                    &fXform,
   1581                    renderingMode,
   1582                    fMeasuringMode,
   1583                    fGridFitMode,
   1584                    fAntiAliasMode,
   1585                    0.0f, // baselineOriginX,
   1586                    0.0f, // baselineOriginY,
   1587                    &glyphRunAnalysis),
   1588                 "Could not create DW2 glyph run analysis.");
   1589        } else {
   1590            HRBM(typeface->fFactory->CreateGlyphRunAnalysis(&run,
   1591                    1.0f, // pixelsPerDip,
   1592                    &fXform,
   1593                    renderingMode,
   1594                    fMeasuringMode,
   1595                    0.0f, // baselineOriginX,
   1596                    0.0f, // baselineOriginY,
   1597                    &glyphRunAnalysis),
   1598                 "Could not create glyph run analysis.");
   1599        }
   1600    }
   1601    RECT bbox;
   1602    {
   1603        Shared l(maybe_dw_mutex(*typeface));
   1604        HRBM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, &bbox),
   1605             "Could not get texture bounds.");
   1606    }
   1607 
   1608    // GetAlphaTextureBounds succeeds but sometimes returns empty bounds like
   1609    // { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
   1610    // for small but not quite zero and large (but not really large) glyphs,
   1611    // Only set as non-empty if the returned bounds are non-empty.
   1612    if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) {
   1613        return false;
   1614    }
   1615 
   1616    *bounds = SkRect::MakeLTRB(bbox.left, bbox.top, bbox.right, bbox.bottom);
   1617    return true;
   1618 }
   1619 
   1620 bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph,
   1621                                          IDWriteColorGlyphRunEnumerator** colorGlyph)
   1622 {
   1623    FLOAT advance = 0;
   1624    UINT16 glyphId = glyph.getGlyphID();
   1625 
   1626    DWRITE_GLYPH_OFFSET offset;
   1627    offset.advanceOffset = 0.0f;
   1628    offset.ascenderOffset = 0.0f;
   1629 
   1630    DWRITE_GLYPH_RUN run;
   1631    run.glyphCount = 1;
   1632    run.glyphAdvances = &advance;
   1633    run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
   1634    run.fontEmSize = SkScalarToFloat(fTextSizeRender);
   1635    run.bidiLevel = 0;
   1636    run.glyphIndices = &glyphId;
   1637    run.isSideways = FALSE;
   1638    run.glyphOffsets = &offset;
   1639 
   1640    HRESULT hr = this->getDWriteTypeface()->fFactory2->TranslateColorGlyphRun(
   1641        0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph);
   1642    if (hr == DWRITE_E_NOCOLOR) {
   1643        return false;
   1644    }
   1645    HRBM(hr, "Failed to translate color glyph run");
   1646    return true;
   1647 }
   1648 
   1649 bool SkScalerContext_DW::generateColorMetrics(const SkGlyph& glyph, SkRect* bounds) {
   1650    SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
   1651    if (!getColorGlyphRun(glyph, &colorLayers)) {
   1652        return false;
   1653    }
   1654    SkASSERT(colorLayers.get());
   1655 
   1656    *bounds = SkRect::MakeEmpty();
   1657    BOOL hasNextRun = FALSE;
   1658    while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
   1659        const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
   1660        HRBM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
   1661 
   1662        SkPathBuilder builder;
   1663        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
   1664        HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
   1665             "Could not create geometry to path converter.");
   1666        {
   1667            Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
   1668            HRBM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
   1669                    colorGlyph->glyphRun.fontEmSize,
   1670                    colorGlyph->glyphRun.glyphIndices,
   1671                    colorGlyph->glyphRun.glyphAdvances,
   1672                    colorGlyph->glyphRun.glyphOffsets,
   1673                    colorGlyph->glyphRun.glyphCount,
   1674                    colorGlyph->glyphRun.isSideways,
   1675                    colorGlyph->glyphRun.bidiLevel % 2, //rtl
   1676                    geometryToPath.get()),
   1677                 "Could not create glyph outline.");
   1678        }
   1679        bounds->join(builder.detach().getBounds());
   1680    }
   1681    SkMatrix matrix = fSkXform;
   1682    if (this->isSubpixel()) {
   1683        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
   1684                             SkFixedToScalar(glyph.getSubYFixed()));
   1685    }
   1686    matrix.mapRect(bounds);
   1687    return true;
   1688 }
   1689 
   1690 #ifdef USE_SVG
   1691 bool SkScalerContext_DW::generateSVGMetrics(const SkGlyph& glyph, SkRect* bounds) {
   1692    SkPictureRecorder recorder;
   1693    SkRect infiniteRect = SkRect::MakeLTRB(-SK_ScalarInfinity, -SK_ScalarInfinity,
   1694                                            SK_ScalarInfinity,  SK_ScalarInfinity);
   1695    sk_sp<SkBBoxHierarchy> bboxh = SkRTreeFactory()();
   1696    SkCanvas* recordingCanvas = recorder.beginRecording(infiniteRect, bboxh);
   1697    if (!this->drawSVGImage(glyph, *recordingCanvas)) {
   1698        return false;
   1699    }
   1700    sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
   1701    *bounds = pic->cullRect();
   1702    SkASSERT(bounds->isFinite());
   1703    bounds->roundOut(bounds);
   1704    return true;
   1705 }
   1706 #endif
   1707 
   1708 #ifdef USE_PNG
   1709 namespace {
   1710 struct Context {
   1711    SkTScopedComPtr<IDWriteFontFace4> fontFace4;
   1712    void* glyphDataContext;
   1713    Context(IDWriteFontFace4* face4, void* context)
   1714        : fontFace4(SkRefComPtr(face4))
   1715        , glyphDataContext(context)
   1716    {}
   1717 };
   1718 
   1719 static void ReleaseProc(const void* ptr, void* context) {
   1720    Context* ctx = (Context*)context;
   1721    ctx->fontFace4->ReleaseGlyphImageData(ctx->glyphDataContext);
   1722    delete ctx;
   1723 }
   1724 }
   1725 
   1726 bool SkScalerContext_DW::generatePngMetrics(const SkGlyph& glyph, SkRect* bounds) {
   1727    IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
   1728    if (!fontFace4) {
   1729        return false;
   1730    }
   1731 
   1732    DWRITE_GLYPH_IMAGE_FORMATS imageFormats;
   1733    HRBM(fontFace4->GetGlyphImageFormats(glyph.getGlyphID(), 0, UINT32_MAX, &imageFormats),
   1734         "Cannot get glyph image formats.");
   1735    if (!(imageFormats & DWRITE_GLYPH_IMAGE_FORMATS_PNG)) {
   1736        return false;
   1737    }
   1738 
   1739    DWRITE_GLYPH_IMAGE_DATA glyphData;
   1740    void* glyphDataContext;
   1741    HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
   1742                                      fTextSizeRender,
   1743                                      DWRITE_GLYPH_IMAGE_FORMATS_PNG,
   1744                                      &glyphData,
   1745                                      &glyphDataContext),
   1746         "Glyph image data could not be acquired.");
   1747 
   1748    Context* context = new Context(fontFace4, glyphDataContext);
   1749    sk_sp<SkData> data = SkData::MakeWithProc(glyphData.imageData,
   1750                                              glyphData.imageDataSize,
   1751                                              &ReleaseProc,
   1752                                              context);
   1753 
   1754    std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(std::move(data), nullptr);
   1755    if (!codec) {
   1756        return false;
   1757    }
   1758 
   1759    SkImageInfo info = codec->getInfo();
   1760    *bounds = SkRect::Make(info.bounds());
   1761 
   1762    SkMatrix matrix = fSkXform;
   1763    SkScalar scale = fTextSizeRender / glyphData.pixelsPerEm;
   1764    matrix.preScale(scale, scale);
   1765    matrix.preTranslate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
   1766    if (this->isSubpixel()) {
   1767        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
   1768                             SkFixedToScalar(glyph.getSubYFixed()));
   1769    }
   1770    matrix.mapRect(bounds);
   1771    bounds->roundOut(bounds);
   1772    return true;
   1773 }
   1774 #endif
   1775 
   1776 SkScalerContext::GlyphMetrics SkScalerContext_DW::generateMetrics(const SkGlyph& glyph,
   1777                                                                  SkArenaAlloc* alloc) {
   1778    GlyphMetrics mx(glyph.maskFormat());
   1779 
   1780    mx.extraBits = ScalerContextBits::NONE;
   1781 
   1782    if (!this->setAdvance(glyph, &mx.advance)) {
   1783        return mx;
   1784    }
   1785 
   1786    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   1787    if (typeface->fIsColorFont) {
   1788        if (generateColorV1Metrics(glyph, &mx.bounds)) {
   1789            mx.maskFormat = SkMask::kARGB32_Format;
   1790            mx.extraBits |= ScalerContextBits::COLRv1;
   1791            mx.neverRequestPath = true;
   1792            return mx;
   1793        }
   1794 
   1795        if (generateColorMetrics(glyph, &mx.bounds)) {
   1796            mx.maskFormat = SkMask::kARGB32_Format;
   1797            mx.extraBits |= ScalerContextBits::COLR;
   1798            mx.neverRequestPath = true;
   1799            return mx;
   1800        }
   1801 
   1802 #ifdef USE_SVG
   1803        if (generateSVGMetrics(glyph, &mx.bounds)) {
   1804            mx.maskFormat = SkMask::kARGB32_Format;
   1805            mx.extraBits |= ScalerContextBits::SVG;
   1806            mx.neverRequestPath = true;
   1807            return mx;
   1808        }
   1809 #endif
   1810 
   1811 #ifdef USE_PNG
   1812        if (generatePngMetrics(glyph, &mx.bounds)) {
   1813            mx.maskFormat = SkMask::kARGB32_Format;
   1814            mx.extraBits |= ScalerContextBits::PNG;
   1815            mx.neverRequestPath = true;
   1816            return mx;
   1817        }
   1818 #endif
   1819    }
   1820 
   1821    if (this->generateDWMetrics(glyph, fRenderingMode, fTextureType, &mx.bounds)) {
   1822        mx.extraBits = ScalerContextBits::DW;
   1823        return mx;
   1824    }
   1825 
   1826    // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
   1827    // glyphs of the specified texture type or it is too big for smoothing.
   1828    // When this happens, try with the alternate texture type.
   1829    if (DWRITE_TEXTURE_ALIASED_1x1 != fTextureType ||
   1830        DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE == fAntiAliasMode)
   1831    {
   1832        if (this->generateDWMetrics(glyph,
   1833                                    DWRITE_RENDERING_MODE_ALIASED,
   1834                                    DWRITE_TEXTURE_ALIASED_1x1,
   1835                                    &mx.bounds))
   1836        {
   1837            mx.maskFormat = SkMask::kBW_Format;
   1838            mx.extraBits = ScalerContextBits::DW_1;
   1839            return mx;
   1840        }
   1841    }
   1842    // TODO: Try DWRITE_TEXTURE_CLEARTYPE_3x1 if DWRITE_TEXTURE_ALIASED_1x1 fails
   1843 
   1844    // GetAlphaTextureBounds can fail for various reasons.
   1845    // As a fallback, attempt to generate the metrics and image from the path.
   1846    mx.computeFromPath = true;
   1847    mx.extraBits = ScalerContextBits::PATH;
   1848    return mx;
   1849 }
   1850 
   1851 void SkScalerContext_DW::generateFontMetrics(SkFontMetrics* metrics) {
   1852    if (nullptr == metrics) {
   1853        return;
   1854    }
   1855 
   1856    sk_bzero(metrics, sizeof(*metrics));
   1857 
   1858    DWRITE_FONT_METRICS dwfm;
   1859    if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
   1860        DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
   1861    {
   1862        this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleMetrics(
   1863             fTextSizeRender,
   1864             1.0f, // pixelsPerDip
   1865             &fXform,
   1866             &dwfm);
   1867    } else {
   1868        this->getDWriteTypeface()->fDWriteFontFace->GetMetrics(&dwfm);
   1869    }
   1870 
   1871    SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
   1872 
   1873    metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
   1874    metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
   1875    metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
   1876    metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
   1877    metrics->fCapHeight = fTextSizeRender * SkIntToScalar(dwfm.capHeight) / upem;
   1878    metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
   1879    metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem);
   1880    metrics->fStrikeoutThickness = fTextSizeRender * SkIntToScalar(dwfm.strikethroughThickness) / upem;
   1881    metrics->fStrikeoutPosition = -(fTextSizeRender * SkIntToScalar(dwfm.strikethroughPosition) / upem);
   1882 
   1883    metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
   1884    metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
   1885    metrics->fFlags |= SkFontMetrics::kStrikeoutThicknessIsValid_Flag;
   1886    metrics->fFlags |= SkFontMetrics::kStrikeoutPositionIsValid_Flag;
   1887 
   1888    SkTScopedComPtr<IDWriteFontFace5> fontFace5;
   1889    if (SUCCEEDED(this->getDWriteTypeface()->fDWriteFontFace->QueryInterface(&fontFace5))) {
   1890        if (fontFace5->HasVariations()) {
   1891            // The bounds are only valid for the default variation.
   1892            metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
   1893        }
   1894    }
   1895 
   1896    if (this->getDWriteTypeface()->fDWriteFontFace1.get()) {
   1897        DWRITE_FONT_METRICS1 dwfm1;
   1898        this->getDWriteTypeface()->fDWriteFontFace1->GetMetrics(&dwfm1);
   1899        metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
   1900        metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
   1901        metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
   1902        metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
   1903 
   1904        metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
   1905        return;
   1906    }
   1907 
   1908    AutoTDWriteTable<SkOTTableHead> head(this->getDWriteTypeface()->fDWriteFontFace.get());
   1909    if (head.fExists &&
   1910        head.fSize >= sizeof(SkOTTableHead) &&
   1911        head->version == SkOTTableHead::version1)
   1912    {
   1913        metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
   1914        metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
   1915        metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
   1916        metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
   1917 
   1918        metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
   1919        return;
   1920    }
   1921 
   1922    // The real bounds weren't actually available.
   1923    metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
   1924    metrics->fTop = metrics->fAscent;
   1925    metrics->fBottom = metrics->fDescent;
   1926 }
   1927 
   1928 ///////////////////////////////////////////////////////////////////////////////
   1929 
   1930 #include "src/core/SkColorData.h"
   1931 
   1932 void SkScalerContext_DW::BilevelToBW(const uint8_t* SK_RESTRICT src,
   1933                                     const SkGlyph& glyph, void* imageBuffer) {
   1934    const int width = glyph.width();
   1935    const size_t dstRB = (width + 7) >> 3;
   1936    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(imageBuffer);
   1937 
   1938    int byteCount = width >> 3;
   1939    int bitCount = width & 7;
   1940 
   1941    for (int y = 0; y < glyph.height(); ++y) {
   1942        if (byteCount > 0) {
   1943            for (int i = 0; i < byteCount; ++i) {
   1944                unsigned byte = 0;
   1945                byte |= src[0] & (1 << 7);
   1946                byte |= src[1] & (1 << 6);
   1947                byte |= src[2] & (1 << 5);
   1948                byte |= src[3] & (1 << 4);
   1949                byte |= src[4] & (1 << 3);
   1950                byte |= src[5] & (1 << 2);
   1951                byte |= src[6] & (1 << 1);
   1952                byte |= src[7] & (1 << 0);
   1953                dst[i] = byte;
   1954                src += 8;
   1955            }
   1956        }
   1957        if (bitCount > 0) {
   1958            unsigned byte = 0;
   1959            unsigned mask = 0x80;
   1960            for (int i = 0; i < bitCount; i++) {
   1961                byte |= (src[i]) & mask;
   1962                mask >>= 1;
   1963            }
   1964            dst[byteCount] = byte;
   1965        }
   1966        src += bitCount;
   1967        dst += dstRB;
   1968    }
   1969 
   1970    if constexpr (kSkShowTextBlitCoverage) {
   1971        dst = static_cast<uint8_t*>(imageBuffer);
   1972        for (unsigned y = 0; y < (unsigned)glyph.height(); y += 2) {
   1973            for (unsigned x = (y & 0x2); x < (unsigned)glyph.width(); x+=4) {
   1974                uint8_t& b = dst[(dstRB * y) + (x >> 3)];
   1975                b = b ^ (1 << (0x7 - (x & 0x7)));
   1976            }
   1977        }
   1978    }
   1979 }
   1980 
   1981 template<bool APPLY_PREBLEND>
   1982 void SkScalerContext_DW::GrayscaleToA8(const uint8_t* SK_RESTRICT src,
   1983                                       const SkGlyph& glyph, void* imageBuffer,
   1984                                       const uint8_t* table8) {
   1985    const size_t dstRB = glyph.rowBytes();
   1986    const int width = glyph.width();
   1987    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(imageBuffer);
   1988 
   1989    for (int y = 0; y < glyph.height(); y++) {
   1990        for (int i = 0; i < width; i++) {
   1991            U8CPU a = *(src++);
   1992            dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(a, table8);
   1993            if constexpr (kSkShowTextBlitCoverage) {
   1994                dst[i] = std::max<U8CPU>(0x30, dst[i]);
   1995            }
   1996        }
   1997        dst = SkTAddOffset<uint8_t>(dst, dstRB);
   1998    }
   1999 }
   2000 
   2001 template<bool APPLY_PREBLEND>
   2002 void SkScalerContext_DW::RGBToA8(const uint8_t* SK_RESTRICT src,
   2003                                 const SkGlyph& glyph, void* imageBuffer,
   2004                                 const uint8_t* table8) {
   2005    const size_t dstRB = glyph.rowBytes();
   2006    const int width = glyph.width();
   2007    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(imageBuffer);
   2008 
   2009    for (int y = 0; y < glyph.height(); y++) {
   2010        for (int i = 0; i < width; i++) {
   2011            // Ignore the R, B channels. It looks the closest to what
   2012            // D2D with grayscale AA has. But there's no way
   2013            // to just get a grayscale AA alpha texture from a glyph run.
   2014            U8CPU g = src[1];
   2015            src += 3;
   2016 
   2017            dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(g, table8);
   2018            if constexpr (kSkShowTextBlitCoverage) {
   2019                dst[i] = std::max<U8CPU>(0x30, dst[i]);
   2020            }
   2021        }
   2022        dst = SkTAddOffset<uint8_t>(dst, dstRB);
   2023    }
   2024 }
   2025 
   2026 template<bool APPLY_PREBLEND, bool RGB>
   2027 void SkScalerContext_DW::RGBToLcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
   2028                                    void* imageBuffer,
   2029                                    const uint8_t* tableR, const uint8_t* tableG,
   2030                                    const uint8_t* tableB, int clearTypeLevel) {
   2031    const size_t dstRB = glyph.rowBytes();
   2032    const int width = glyph.width();
   2033    uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(imageBuffer);
   2034 
   2035    for (int y = 0; y < glyph.height(); y++) {
   2036        for (int i = 0; i < width; i++) {
   2037            int r, g, b;
   2038            if (RGB) {
   2039                r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
   2040                g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
   2041                b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
   2042            } else {
   2043                b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
   2044                g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
   2045                r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
   2046            }
   2047            if constexpr (kSkShowTextBlitCoverage) {
   2048                r = std::max<U8CPU>(0x30, r);
   2049                g = std::max<U8CPU>(0x30, g);
   2050                b = std::max<U8CPU>(0x30, b);
   2051            }
   2052            r = g + (((r - g) * clearTypeLevel) >> 8);
   2053            b = g + (((b - g) * clearTypeLevel) >> 8);
   2054            dst[i] = SkPack888ToRGB16(r, g, b);
   2055        }
   2056        dst = SkTAddOffset<uint16_t>(dst, dstRB);
   2057    }
   2058 }
   2059 
   2060 const void* SkScalerContext_DW::getDWMaskBits(const SkGlyph& glyph,
   2061                                              DWRITE_RENDERING_MODE renderingMode,
   2062                                              DWRITE_TEXTURE_TYPE textureType)
   2063 {
   2064    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   2065 
   2066    int sizeNeeded = glyph.width() * glyph.height();
   2067    if (DWRITE_TEXTURE_CLEARTYPE_3x1 == textureType) {
   2068        sizeNeeded *= 3;
   2069    }
   2070    if (sizeNeeded > fBits.size()) {
   2071        fBits.resize(sizeNeeded);
   2072    }
   2073 
   2074    // erase
   2075    memset(fBits.begin(), 0, sizeNeeded);
   2076 
   2077    fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
   2078    fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
   2079 
   2080    FLOAT advance = 0.0f;
   2081 
   2082    UINT16 index = glyph.getGlyphID();
   2083 
   2084    DWRITE_GLYPH_OFFSET offset;
   2085    offset.advanceOffset = 0.0f;
   2086    offset.ascenderOffset = 0.0f;
   2087 
   2088    DWRITE_GLYPH_RUN run;
   2089    run.glyphCount = 1;
   2090    run.glyphAdvances = &advance;
   2091    run.fontFace = typeface->fDWriteFontFace.get();
   2092    run.fontEmSize = SkScalarToFloat(fTextSizeRender);
   2093    run.bidiLevel = 0;
   2094    run.glyphIndices = &index;
   2095    run.isSideways = FALSE;
   2096    run.glyphOffsets = &offset;
   2097    {
   2098        SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
   2099        {
   2100            Exclusive l(maybe_dw_mutex(*typeface));
   2101            // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
   2102            if (typeface->fFactory2 &&
   2103                    (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
   2104                     fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
   2105            {
   2106                HRNM(typeface->fFactory2->CreateGlyphRunAnalysis(&run,
   2107                         &fXform,
   2108                         renderingMode,
   2109                         fMeasuringMode,
   2110                         fGridFitMode,
   2111                         fAntiAliasMode,
   2112                         0.0f, // baselineOriginX,
   2113                         0.0f, // baselineOriginY,
   2114                         &glyphRunAnalysis),
   2115                     "Could not create DW2 glyph run analysis.");
   2116            } else {
   2117                HRNM(typeface->fFactory->CreateGlyphRunAnalysis(&run,
   2118                         1.0f, // pixelsPerDip,
   2119                         &fXform,
   2120                         renderingMode,
   2121                         fMeasuringMode,
   2122                         0.0f, // baselineOriginX,
   2123                         0.0f, // baselineOriginY,
   2124                         &glyphRunAnalysis),
   2125                     "Could not create glyph run analysis.");
   2126            }
   2127        }
   2128        //NOTE: this assumes that the glyph has already been measured
   2129        //with an exact same glyph run analysis.
   2130        RECT bbox;
   2131        bbox.left = glyph.left();
   2132        bbox.top = glyph.top();
   2133        bbox.right = glyph.left() + glyph.width();
   2134        bbox.bottom = glyph.top() + glyph.height();
   2135        {
   2136            Shared l(maybe_dw_mutex(*typeface));
   2137            HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
   2138                    &bbox,
   2139                    fBits.begin(),
   2140                    sizeNeeded),
   2141                 "Could not draw mask.");
   2142        }
   2143    }
   2144    return fBits.begin();
   2145 }
   2146 
   2147 bool SkScalerContext_DW::generateDWImage(const SkGlyph& glyph, void* imageBuffer) {
   2148    //Create the mask.
   2149    ScalerContextBits::value_type format = glyph.extraBits();
   2150    DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
   2151    DWRITE_TEXTURE_TYPE textureType = fTextureType;
   2152    if (format == ScalerContextBits::DW_1) {
   2153        renderingMode = DWRITE_RENDERING_MODE_ALIASED;
   2154        textureType = DWRITE_TEXTURE_ALIASED_1x1;
   2155    }
   2156    const void* bits = this->getDWMaskBits(glyph, renderingMode, textureType);
   2157    if (!bits) {
   2158        sk_bzero(imageBuffer, glyph.imageSize());
   2159        return false;
   2160    }
   2161 
   2162    //Copy the mask into the glyph.
   2163    const uint8_t* src = (const uint8_t*)bits;
   2164    if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
   2165        SkASSERT(SkMask::kBW_Format == glyph.maskFormat());
   2166        SkASSERT(DWRITE_TEXTURE_ALIASED_1x1 == textureType);
   2167        BilevelToBW(src, glyph, imageBuffer);
   2168    } else if (!isLCD(fRec)) {
   2169        if (textureType == DWRITE_TEXTURE_ALIASED_1x1) {
   2170            if (fPreBlend.isApplicable()) {
   2171                GrayscaleToA8<true>(src, glyph, imageBuffer, fPreBlend.fG);
   2172            } else {
   2173                GrayscaleToA8<false>(src, glyph, imageBuffer, fPreBlend.fG);
   2174            }
   2175        } else {
   2176            if (fPreBlend.isApplicable()) {
   2177                RGBToA8<true>(src, glyph, imageBuffer, fPreBlend.fG);
   2178            } else {
   2179                RGBToA8<false>(src, glyph, imageBuffer, fPreBlend.fG);
   2180            }
   2181        }
   2182    } else {
   2183        SkASSERT(SkMask::kLCD16_Format == glyph.maskFormat());
   2184        if (fPreBlend.isApplicable()) {
   2185            if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
   2186                RGBToLcd16<true, false>(src, glyph, imageBuffer,
   2187                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
   2188            } else {
   2189                RGBToLcd16<true, true>(src, glyph, imageBuffer,
   2190                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
   2191            }
   2192        } else {
   2193            if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
   2194                RGBToLcd16<false, false>(src, glyph, imageBuffer,
   2195                                         fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
   2196            } else {
   2197                RGBToLcd16<false, true>(src, glyph, imageBuffer,
   2198                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
   2199            }
   2200        }
   2201    }
   2202    return true;
   2203 }
   2204 
   2205 bool SkScalerContext_DW::drawColorImage(const SkGlyph& glyph, SkCanvas& canvas) {
   2206    SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
   2207    if (!getColorGlyphRun(glyph, &colorLayers)) {
   2208        SkASSERTF(false, "Could not get color layers");
   2209        return false;
   2210    }
   2211 
   2212    SkPaint paint;
   2213    paint.setAntiAlias(fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
   2214 
   2215    if (this->isSubpixel()) {
   2216        canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
   2217                         SkFixedToScalar(glyph.getSubYFixed()));
   2218    }
   2219    canvas.concat(fSkXform);
   2220 
   2221    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   2222    size_t paletteEntryCount = typeface->fPaletteEntryCount;
   2223    SkColor* palette = typeface->fPalette.get();
   2224    BOOL hasNextRun = FALSE;
   2225    while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
   2226        const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
   2227        HRBM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
   2228 
   2229        SkColor color;
   2230        if (colorGlyph->paletteIndex == 0xffff) {
   2231            color = fRec.fForegroundColor;
   2232        } else if (colorGlyph->paletteIndex < paletteEntryCount) {
   2233            color = palette[colorGlyph->paletteIndex];
   2234        } else {
   2235            SK_TRACEHR(DWRITE_E_NOCOLOR, "Invalid palette index.");
   2236            color = SK_ColorBLACK;
   2237        }
   2238        paint.setColor(color);
   2239 
   2240        SkPathBuilder builder;
   2241        SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
   2242        HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
   2243             "Could not create geometry to path converter.");
   2244        {
   2245            Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
   2246            HRBM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
   2247                     colorGlyph->glyphRun.fontEmSize,
   2248                     colorGlyph->glyphRun.glyphIndices,
   2249                     colorGlyph->glyphRun.glyphAdvances,
   2250                     colorGlyph->glyphRun.glyphOffsets,
   2251                     colorGlyph->glyphRun.glyphCount,
   2252                     colorGlyph->glyphRun.isSideways,
   2253                     colorGlyph->glyphRun.bidiLevel % 2, //rtl
   2254                     geometryToPath.get()),
   2255                 "Could not create glyph outline.");
   2256        }
   2257        canvas.drawPath(builder.detach(), paint);
   2258    }
   2259    return true;
   2260 }
   2261 
   2262 bool SkScalerContext_DW::generateColorImage(const SkGlyph& glyph, void* imageBuffer) {
   2263    SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
   2264 
   2265    SkBitmap dstBitmap;
   2266    // TODO: mark this as sRGB when the blits will be sRGB.
   2267    dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
   2268                                        kN32_SkColorType, kPremul_SkAlphaType),
   2269                                        glyph.rowBytes());
   2270    dstBitmap.setPixels(imageBuffer);
   2271 
   2272    SkCanvas canvas(dstBitmap);
   2273    if constexpr (kSkShowTextBlitCoverage) {
   2274        canvas.clear(0x33FF0000);
   2275    } else {
   2276        canvas.clear(SK_ColorTRANSPARENT);
   2277    }
   2278    canvas.translate(-SkIntToScalar(glyph.left()), -SkIntToScalar(glyph.top()));
   2279 
   2280    return this->drawColorImage(glyph, canvas);
   2281 }
   2282 
   2283 bool SkScalerContext_DW::drawSVGImage(const SkGlyph& glyph, SkCanvas& canvas) {
   2284    DWriteFontTypeface* typeface = this->getDWriteTypeface();
   2285    IDWriteFontFace4* fontFace4 = typeface->fDWriteFontFace4.get();
   2286    if (!fontFace4) {
   2287        return false;
   2288    }
   2289 
   2290    DWRITE_GLYPH_IMAGE_FORMATS imageFormats;
   2291    HRBM(fontFace4->GetGlyphImageFormats(glyph.getGlyphID(), 0, UINT32_MAX, &imageFormats),
   2292         "Cannot get glyph image formats.");
   2293    if (!(imageFormats & DWRITE_GLYPH_IMAGE_FORMATS_SVG)) {
   2294        return false;
   2295    }
   2296 
   2297    SkGraphics::OpenTypeSVGDecoderFactory svgFactory = SkGraphics::GetOpenTypeSVGDecoderFactory();
   2298    if (!svgFactory) {
   2299        return false;
   2300    }
   2301 
   2302    DWRITE_GLYPH_IMAGE_DATA glyphData;
   2303    void* glyphDataContext;
   2304    HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
   2305                                      fTextSizeRender,
   2306                                      DWRITE_GLYPH_IMAGE_FORMATS_SVG,
   2307                                      &glyphData,
   2308                                      &glyphDataContext),
   2309         "Glyph SVG data could not be acquired.");
   2310    auto svgDecoder = svgFactory((const uint8_t*)glyphData.imageData, glyphData.imageDataSize);
   2311    fontFace4->ReleaseGlyphImageData(glyphDataContext);
   2312    if (!svgDecoder) {
   2313        return false;
   2314    }
   2315 
   2316    size_t paletteEntryCount = typeface->fPaletteEntryCount;
   2317    SkColor* palette = typeface->fPalette.get();
   2318    int upem = typeface->getUnitsPerEm();
   2319 
   2320    SkMatrix matrix = fSkXform;
   2321    SkScalar scale = fTextSizeRender / upem;
   2322    matrix.preScale(scale, scale);
   2323    matrix.preTranslate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
   2324    if (this->isSubpixel()) {
   2325        matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
   2326                             SkFixedToScalar(glyph.getSubYFixed()));
   2327    }
   2328    canvas.concat(matrix);
   2329 
   2330    return svgDecoder->render(canvas, upem, glyph.getGlyphID(),
   2331                              fRec.fForegroundColor, SkSpan(palette, paletteEntryCount));
   2332 }
   2333 
   2334 bool SkScalerContext_DW::generateSVGImage(const SkGlyph& glyph, void* imageBuffer) {
   2335    SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
   2336 
   2337    SkBitmap dstBitmap;
   2338    // TODO: mark this as sRGB when the blits will be sRGB.
   2339    dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
   2340                                        kN32_SkColorType, kPremul_SkAlphaType),
   2341                      glyph.rowBytes());
   2342    dstBitmap.setPixels(imageBuffer);
   2343 
   2344    SkCanvas canvas(dstBitmap);
   2345    if constexpr (kSkShowTextBlitCoverage) {
   2346        canvas.clear(0x33FF0000);
   2347    } else {
   2348        canvas.clear(SK_ColorTRANSPARENT);
   2349    }
   2350    canvas.translate(-SkIntToScalar(glyph.left()), -SkIntToScalar(glyph.top()));
   2351 
   2352    return this->drawSVGImage(glyph, canvas);
   2353 }
   2354 
   2355 #ifdef USE_PNG
   2356 bool SkScalerContext_DW::drawPngImage(const SkGlyph& glyph, SkCanvas& canvas) {
   2357    IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
   2358    if (!fontFace4) {
   2359        return false;
   2360    }
   2361 
   2362    DWRITE_GLYPH_IMAGE_DATA glyphData;
   2363    void* glyphDataContext;
   2364    HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
   2365                                      fTextSizeRender,
   2366                                      DWRITE_GLYPH_IMAGE_FORMATS_PNG,
   2367                                      &glyphData,
   2368                                      &glyphDataContext),
   2369         "Glyph image data could not be acquired.");
   2370    Context* context = new Context(fontFace4, glyphDataContext);
   2371    sk_sp<SkData> data = SkData::MakeWithProc(glyphData.imageData,
   2372                                              glyphData.imageDataSize,
   2373                                              &ReleaseProc,
   2374                                              context);
   2375    sk_sp<SkImage> image = SkImages::DeferredFromEncodedData(std::move(data));
   2376    if (!image) {
   2377        return false;
   2378    }
   2379 
   2380    if (this->isSubpixel()) {
   2381        canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
   2382                         SkFixedToScalar(glyph.getSubYFixed()));
   2383    }
   2384    canvas.concat(fSkXform);
   2385    SkScalar ratio = fTextSizeRender / glyphData.pixelsPerEm;
   2386    canvas.scale(ratio, ratio);
   2387    canvas.translate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
   2388    canvas.drawImage(image, 0, 0);
   2389    return true;
   2390 }
   2391 
   2392 bool SkScalerContext_DW::generatePngImage(const SkGlyph& glyph, void* imageBuffer) {
   2393    SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
   2394 
   2395    SkBitmap dstBitmap;
   2396    dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
   2397                                        kN32_SkColorType, kPremul_SkAlphaType),
   2398                      glyph.rowBytes());
   2399    dstBitmap.setPixels(imageBuffer);
   2400 
   2401    SkCanvas canvas(dstBitmap);
   2402    canvas.clear(SK_ColorTRANSPARENT);
   2403    canvas.translate(-glyph.left(), -glyph.top());
   2404 
   2405    return this->drawPngImage(glyph, canvas);
   2406 }
   2407 #endif
   2408 
   2409 void SkScalerContext_DW::generateImage(const SkGlyph& glyph, void* imageBuffer) {
   2410    ScalerContextBits::value_type format = glyph.extraBits();
   2411    if (format == ScalerContextBits::DW ||
   2412        format == ScalerContextBits::DW_1)
   2413    {
   2414        this->generateDWImage(glyph, imageBuffer);
   2415    } else if (format == ScalerContextBits::COLRv1) {
   2416        this->generateColorV1Image(glyph, imageBuffer);
   2417    } else if (format == ScalerContextBits::COLR) {
   2418        this->generateColorImage(glyph, imageBuffer);
   2419 #ifdef USE_SVG
   2420    } else if (format == ScalerContextBits::SVG) {
   2421        this->generateSVGImage(glyph, imageBuffer);
   2422 #endif
   2423 #ifdef USE_PNG
   2424    } else if (format == ScalerContextBits::PNG) {
   2425        this->generatePngImage(glyph, imageBuffer);
   2426 #endif
   2427    } else if (format == ScalerContextBits::PATH) {
   2428        this->generateImageFromPath(glyph, imageBuffer);
   2429    } else {
   2430        SK_ABORT("Bad format");
   2431    }
   2432 }
   2433 
   2434 std::optional<SkScalerContext::GeneratedPath>
   2435 SkScalerContext_DW::generatePath(const SkGlyph& glyph) {
   2436    SkGlyphID glyphID = glyph.getGlyphID();
   2437 
   2438    // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0.
   2439    // For consistency with all other backends, treat out of range glyph ids as an error.
   2440    if (fGlyphCount <= glyphID) {
   2441        return {};
   2442    }
   2443 
   2444    SkPathBuilder builder;
   2445    SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
   2446    HR_GENERAL(SkDWriteGeometrySink::Create(&builder, &geometryToPath),
   2447                "Could not create geometry to path converter.", {});
   2448    UINT16 glyphId = SkTo<UINT16>(glyphID);
   2449    {
   2450        Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
   2451        //TODO: convert to<->from DIUs? This would make a difference if hinting.
   2452        //It may not be needed, it appears that DirectWrite only hints at em size.
   2453        HR_GENERAL(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
   2454             SkScalarToFloat(fTextSizeRender),
   2455             &glyphId,
   2456             nullptr, //advances
   2457             nullptr, //offsets
   2458             1, //num glyphs
   2459             FALSE, //sideways
   2460             FALSE, //rtl
   2461             geometryToPath.get()),
   2462             "Could not create glyph outline.", {});
   2463    }
   2464 
   2465    builder.transform(fSkXform);
   2466    return {{builder.detach(), false}};
   2467 }
   2468 
   2469 sk_sp<SkDrawable> SkScalerContext_DW::generateDrawable(const SkGlyph& glyph) {
   2470    struct GlyphDrawable : public SkDrawable {
   2471        SkScalerContext_DW* fSelf;
   2472        SkGlyph fGlyph;
   2473        GlyphDrawable(SkScalerContext_DW* self, const SkGlyph& glyph) : fSelf(self), fGlyph(glyph){}
   2474        SkRect onGetBounds() override { return fGlyph.rect();  }
   2475        size_t onApproximateBytesUsed() override { return sizeof(GlyphDrawable); }
   2476        void maybeShowTextBlitCoverage(SkCanvas* canvas) {
   2477            if constexpr (kSkShowTextBlitCoverage) {
   2478                SkPaint paint;
   2479                paint.setColor(0x3300FF00);
   2480                paint.setStyle(SkPaint::kFill_Style);
   2481                canvas->drawRect(this->onGetBounds(), paint);
   2482            }
   2483        }
   2484    };
   2485    struct COLRv1GlyphDrawable : public GlyphDrawable {
   2486        using GlyphDrawable::GlyphDrawable;
   2487        void onDraw(SkCanvas* canvas) override {
   2488            this->maybeShowTextBlitCoverage(canvas);
   2489            fSelf->drawColorV1Image(fGlyph, *canvas);
   2490        }
   2491    };
   2492    struct COLRGlyphDrawable : public GlyphDrawable {
   2493        using GlyphDrawable::GlyphDrawable;
   2494        void onDraw(SkCanvas* canvas) override {
   2495            this->maybeShowTextBlitCoverage(canvas);
   2496            fSelf->drawColorImage(fGlyph, *canvas);
   2497        }
   2498    };
   2499    struct SVGGlyphDrawable : public GlyphDrawable {
   2500        using GlyphDrawable::GlyphDrawable;
   2501        void onDraw(SkCanvas* canvas) override {
   2502            this->maybeShowTextBlitCoverage(canvas);
   2503            fSelf->drawSVGImage(fGlyph, *canvas);
   2504        }
   2505    };
   2506    ScalerContextBits::value_type format = glyph.extraBits();
   2507    if (format == ScalerContextBits::COLRv1) {
   2508        return sk_sp<SkDrawable>(new COLRv1GlyphDrawable(this, glyph));
   2509    }
   2510    if (format == ScalerContextBits::COLR) {
   2511        return sk_sp<SkDrawable>(new COLRGlyphDrawable(this, glyph));
   2512    }
   2513    if (format == ScalerContextBits::SVG) {
   2514        return sk_sp<SkDrawable>(new SVGGlyphDrawable(this, glyph));
   2515    }
   2516    return nullptr;
   2517 }
   2518 
   2519 #endif//defined(SK_BUILD_FOR_WIN)