Home | History | Annotate | Download | only in ports
      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 
      8 #include "SkTypes.h"
      9 #if defined(SK_BUILD_FOR_WIN)
     10 
     11 #undef GetGlyphIndices
     12 
     13 #include "SkDraw.h"
     14 #include "SkDWrite.h"
     15 #include "SkDWriteGeometrySink.h"
     16 #include "SkEndian.h"
     17 #include "SkGlyph.h"
     18 #include "SkHRESULT.h"
     19 #include "SkMaskGamma.h"
     20 #include "SkMatrix22.h"
     21 #include "SkMutex.h"
     22 #include "SkOTTable_EBLC.h"
     23 #include "SkOTTable_EBSC.h"
     24 #include "SkOTTable_gasp.h"
     25 #include "SkOTTable_maxp.h"
     26 #include "SkPath.h"
     27 #include "SkRasterClip.h"
     28 #include "SkScalerContext.h"
     29 #include "SkScalerContext_win_dw.h"
     30 #include "SkSharedMutex.h"
     31 #include "SkTScopedComPtr.h"
     32 #include "SkTypeface_win_dw.h"
     33 
     34 #include <dwrite.h>
     35 #include <dwrite_1.h>
     36 
     37 /* Note:
     38  * In versions 8 and 8.1 of Windows, some calls in DWrite are not thread safe.
     39  * The DWriteFactoryMutex protects the calls that are problematic.
     40  */
     41 static SkSharedMutex DWriteFactoryMutex;
     42 
     43 typedef SkAutoSharedMutexShared Shared;
     44 
     45 static bool isLCD(const SkScalerContextRec& rec) {
     46     return SkMask::kLCD16_Format == rec.fMaskFormat;
     47 }
     48 
     49 static bool is_hinted(DWriteFontTypeface* typeface) {
     50     SkAutoExclusive l(DWriteFactoryMutex);
     51     AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
     52     if (!maxp.fExists) {
     53         return false;
     54     }
     55     if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
     56         return false;
     57     }
     58     if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
     59         return false;
     60     }
     61     return (0 != maxp->version.tt.maxSizeOfInstructions);
     62 }
     63 
     64 /** A GaspRange is inclusive, [min, max]. */
     65 struct GaspRange {
     66     using Behavior = SkOTTableGridAndScanProcedure::GaspRange::behavior;
     67     GaspRange(int min, int max, int version, Behavior flags)
     68         : fMin(min), fMax(max), fVersion(version), fFlags(flags) { }
     69     int fMin;
     70     int fMax;
     71     int fVersion;
     72     Behavior fFlags;
     73 };
     74 
     75 bool get_gasp_range(DWriteFontTypeface* typeface, int size, GaspRange* range) {
     76     AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
     77     if (!gasp.fExists) {
     78         return false;
     79     }
     80     if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
     81         return false;
     82     }
     83     if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
     84         gasp->version != SkOTTableGridAndScanProcedure::version1)
     85     {
     86         return false;
     87     }
     88 
     89     uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
     90     if (numRanges > 1024 ||
     91         gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
     92         sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
     93     {
     94         return false;
     95     }
     96 
     97     const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
     98             SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
     99     int minPPEM = -1;
    100     for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
    101         int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
    102         if (minPPEM < size && size <= maxPPEM) {
    103             range->fMin = minPPEM + 1;
    104             range->fMax = maxPPEM;
    105             range->fVersion = SkEndian_SwapBE16(gasp->version);
    106             range->fFlags = rangeTable->flags;
    107             return true;
    108         }
    109         minPPEM = maxPPEM;
    110     }
    111     return false;
    112 }
    113 /** If the rendering mode for the specified 'size' is gridfit, then place
    114  *  the gridfit range into 'range'. Otherwise, leave 'range' alone.
    115  */
    116 static bool is_gridfit_only(GaspRange::Behavior flags) {
    117     return flags.raw.value == GaspRange::Behavior::Raw::GridfitMask;
    118 }
    119 
    120 static bool has_bitmap_strike(DWriteFontTypeface* typeface, GaspRange range) {
    121     SkAutoExclusive l(DWriteFactoryMutex);
    122     {
    123         AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
    124         if (!eblc.fExists) {
    125             return false;
    126         }
    127         if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
    128             return false;
    129         }
    130         if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
    131             return false;
    132         }
    133 
    134         uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
    135         if (numSizes > 1024 ||
    136             eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
    137                          sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
    138         {
    139             return false;
    140         }
    141 
    142         const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
    143                 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
    144         for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
    145             if (sizeTable->ppemX == sizeTable->ppemY &&
    146                 range.fMin <= sizeTable->ppemX && sizeTable->ppemX <= range.fMax)
    147             {
    148                 // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
    149                 // to determine the actual number of glyphs with bitmaps.
    150 
    151                 // TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
    152 
    153                 // TODO: Ensure that the bitmaps are bi-level?
    154                 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
    155                     return true;
    156                 }
    157             }
    158         }
    159     }
    160 
    161     {
    162         AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
    163         if (!ebsc.fExists) {
    164             return false;
    165         }
    166         if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
    167             return false;
    168         }
    169         if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
    170             return false;
    171         }
    172 
    173         uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
    174         if (numSizes > 1024 ||
    175             ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
    176                          sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
    177         {
    178             return false;
    179         }
    180 
    181         const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
    182                 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
    183         for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
    184             if (scaleTable->ppemX == scaleTable->ppemY &&
    185                 range.fMin <= scaleTable->ppemX && scaleTable->ppemX <= range.fMax) {
    186                 // EBSC tables are normally only found in bitmap only fonts.
    187                 return true;
    188             }
    189         }
    190     }
    191 
    192     return false;
    193 }
    194 
    195 static bool both_zero(SkScalar a, SkScalar b) {
    196     return 0 == a && 0 == b;
    197 }
    198 
    199 // returns false if there is any non-90-rotation or skew
    200 static bool is_axis_aligned(const SkScalerContextRec& rec) {
    201     return 0 == rec.fPreSkewX &&
    202            (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
    203             both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
    204 }
    205 
    206 SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef,
    207                                        const SkScalerContextEffects& effects,
    208                                        const SkDescriptor* desc)
    209         : SkScalerContext(std::move(typefaceRef), effects, desc)
    210         , fGlyphCount(-1) {
    211 
    212     DWriteFontTypeface* typeface = this->getDWriteTypeface();
    213     fIsColorFont = typeface->fFactory2 &&
    214                    typeface->fDWriteFontFace2 &&
    215                    typeface->fDWriteFontFace2->IsColorFont();
    216 
    217     // In general, all glyphs should use NATURAL_SYMMETRIC
    218     // except when bi-level rendering is requested or there are embedded
    219     // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
    220     //
    221     // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
    222     // this. As a result, determine the actual size of the text and then see if
    223     // there are any embedded bi-level bitmaps of that size. If there are, then
    224     // force bitmaps by requesting bi-level rendering.
    225     //
    226     // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
    227     // square pixels and only uses ppemY. Therefore the transform must track any
    228     // non-uniform x-scale.
    229     //
    230     // Also, rotated glyphs should have the same absolute advance widths as
    231     // horizontal glyphs and the subpixel flag should not affect glyph shapes.
    232 
    233     SkVector scale;
    234     fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &fSkXform);
    235 
    236     fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
    237     fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
    238     fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
    239     fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
    240     fXform.dx = 0;
    241     fXform.dy = 0;
    242 
    243     // realTextSize is the actual device size we want (as opposed to the size the user requested).
    244     // gdiTextSize is the size we request when GDI compatible.
    245     // If the scale is negative, this means the matrix will do the flip anyway.
    246     const SkScalar realTextSize = scale.fY;
    247     // Due to floating point math, the lower bits are suspect. Round carefully.
    248     SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
    249     if (gdiTextSize == 0) {
    250         gdiTextSize = SK_Scalar1;
    251     }
    252 
    253     bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
    254     bool treatLikeBitmap = false;
    255     bool axisAlignedBitmap = false;
    256     if (bitmapRequested) {
    257         // When embedded bitmaps are requested, treat the entire range like
    258         // a bitmap strike if the range is gridfit only and contains a bitmap.
    259         int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
    260         GaspRange range(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
    261         if (get_gasp_range(typeface, bitmapPPEM, &range)) {
    262             if (!is_gridfit_only(range.fFlags)) {
    263                 range = GaspRange(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
    264             }
    265         }
    266         treatLikeBitmap = has_bitmap_strike(typeface, range);
    267 
    268         axisAlignedBitmap = is_axis_aligned(fRec);
    269     }
    270 
    271     GaspRange range(0, 0xFFFF, 0, GaspRange::Behavior());
    272 
    273     // If the user requested aliased, do so with aliased compatible metrics.
    274     if (SkMask::kBW_Format == fRec.fMaskFormat) {
    275         fTextSizeRender = gdiTextSize;
    276         fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
    277         fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
    278         fTextSizeMeasure = gdiTextSize;
    279         fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
    280 
    281     // If we can use a bitmap, use gdi classic rendering and measurement.
    282     // This will not always provide a bitmap, but matches expected behavior.
    283     } else if (treatLikeBitmap && axisAlignedBitmap) {
    284         fTextSizeRender = gdiTextSize;
    285         fRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
    286         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    287         fTextSizeMeasure = gdiTextSize;
    288         fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
    289 
    290     // If rotated but the horizontal text could have used a bitmap,
    291     // render high quality rotated glyphs but measure using bitmap metrics.
    292     } else if (treatLikeBitmap) {
    293         fTextSizeRender = gdiTextSize;
    294         fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    295         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    296         fTextSizeMeasure = gdiTextSize;
    297         fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
    298 
    299     // If the font has a gasp table version 1, use it to determine symmetric rendering.
    300     } else if (get_gasp_range(typeface, SkScalarRoundToInt(gdiTextSize), &range) &&
    301                range.fVersion >= 1)
    302     {
    303         fTextSizeRender = realTextSize;
    304         fRenderingMode = range.fFlags.field.SymmetricSmoothing
    305                        ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
    306                        : DWRITE_RENDERING_MODE_NATURAL;
    307         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    308         fTextSizeMeasure = realTextSize;
    309         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    310 
    311     // If the requested size is above 20px or there are no bytecode hints, use symmetric rendering.
    312     } else if (realTextSize > SkIntToScalar(20) || !is_hinted(typeface)) {
    313         fTextSizeRender = realTextSize;
    314         fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    315         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    316         fTextSizeMeasure = realTextSize;
    317         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    318 
    319     // Fonts with hints, no gasp or gasp version 0, and below 20px get non-symmetric rendering.
    320     // Often such fonts have hints which were only tested with GDI ClearType classic.
    321     // Some of these fonts rely on drop out control in the y direction in order to be legible.
    322     // Tenor Sans
    323     //    https://fonts.google.com/specimen/Tenor+Sans
    324     // Gill Sans W04
    325     //    https://cdn.leagueoflegends.com/lolkit/1.1.9/resources/fonts/gill-sans-w04-book.woff
    326     //    https://na.leagueoflegends.com/en/news/game-updates/patch/patch-410-notes
    327     // See https://crbug.com/385897
    328     } else {
    329         fTextSizeRender = gdiTextSize;
    330         fRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
    331         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
    332         fTextSizeMeasure = realTextSize;
    333         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    334     }
    335 
    336     // DirectWrite2 allows for grayscale hinting.
    337     fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE;
    338     if (typeface->fFactory2 && typeface->fDWriteFontFace2 &&
    339         SkMask::kA8_Format == fRec.fMaskFormat &&
    340         !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag))
    341     {
    342         // DWRITE_TEXTURE_ALIASED_1x1 is now misnamed, it must also be used with grayscale.
    343         fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
    344         fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE;
    345     }
    346 
    347     // DirectWrite2 allows hinting to be disabled.
    348     fGridFitMode = DWRITE_GRID_FIT_MODE_ENABLED;
    349     if (fRec.getHinting() == SkPaint::kNo_Hinting) {
    350         fGridFitMode = DWRITE_GRID_FIT_MODE_DISABLED;
    351         if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
    352             fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
    353         }
    354     }
    355 
    356     if (this->isSubpixel()) {
    357         fTextSizeMeasure = realTextSize;
    358         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    359     }
    360 }
    361 
    362 SkScalerContext_DW::~SkScalerContext_DW() {
    363 }
    364 
    365 unsigned SkScalerContext_DW::generateGlyphCount() {
    366     if (fGlyphCount < 0) {
    367         fGlyphCount = this->getDWriteTypeface()->fDWriteFontFace->GetGlyphCount();
    368     }
    369     return fGlyphCount;
    370 }
    371 
    372 uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) {
    373     uint16_t index = 0;
    374     UINT32* uniPtr = reinterpret_cast<UINT32*>(&uni);
    375     this->getDWriteTypeface()->fDWriteFontFace->GetGlyphIndices(uniPtr, 1, &index);
    376     return index;
    377 }
    378 
    379 void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) {
    380     //Delta is the difference between the right/left side bearing metric
    381     //and where the right/left side bearing ends up after hinting.
    382     //DirectWrite does not provide this information.
    383     glyph->fRsbDelta = 0;
    384     glyph->fLsbDelta = 0;
    385 
    386     glyph->fAdvanceX = 0;
    387     glyph->fAdvanceY = 0;
    388 
    389     uint16_t glyphId = glyph->getGlyphID();
    390     DWRITE_GLYPH_METRICS gm;
    391 
    392     if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
    393         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
    394     {
    395         SkAutoExclusive l(DWriteFactoryMutex);
    396         HRVM(this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
    397                  fTextSizeMeasure,
    398                  1.0f, // pixelsPerDip
    399                  // This parameter does not act like the lpmat2 parameter to GetGlyphOutlineW.
    400                  // If it did then GsA here and G_inv below to mapVectors.
    401                  nullptr,
    402                  DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
    403                  &glyphId, 1,
    404                  &gm),
    405              "Could not get gdi compatible glyph metrics.");
    406     } else {
    407         SkAutoExclusive l(DWriteFactoryMutex);
    408         HRVM(this->getDWriteTypeface()->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
    409              "Could not get design metrics.");
    410     }
    411 
    412     DWRITE_FONT_METRICS dwfm;
    413     {
    414         Shared l(DWriteFactoryMutex);
    415         this->getDWriteTypeface()->fDWriteFontFace->GetMetrics(&dwfm);
    416     }
    417     SkScalar advanceX = fTextSizeMeasure * gm.advanceWidth / dwfm.designUnitsPerEm;
    418 
    419     SkVector advance = { advanceX, 0 };
    420     if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
    421         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
    422     {
    423         // DirectWrite produced 'compatible' metrics, but while close,
    424         // the end result is not always an integer as it would be with GDI.
    425         advance.fX = SkScalarRoundToScalar(advance.fX);
    426     }
    427     fSkXform.mapVectors(&advance, 1);
    428 
    429     glyph->fAdvanceX = SkScalarToFloat(advance.fX);
    430     glyph->fAdvanceY = SkScalarToFloat(advance.fY);
    431 }
    432 
    433 HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph,
    434                                            DWRITE_RENDERING_MODE renderingMode,
    435                                            DWRITE_TEXTURE_TYPE textureType,
    436                                            RECT* bbox)
    437 {
    438     //Measure raster size.
    439     fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
    440     fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
    441 
    442     FLOAT advance = 0;
    443 
    444     UINT16 glyphId = glyph->getGlyphID();
    445 
    446     DWRITE_GLYPH_OFFSET offset;
    447     offset.advanceOffset = 0.0f;
    448     offset.ascenderOffset = 0.0f;
    449 
    450     DWRITE_GLYPH_RUN run;
    451     run.glyphCount = 1;
    452     run.glyphAdvances = &advance;
    453     run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
    454     run.fontEmSize = SkScalarToFloat(fTextSizeRender);
    455     run.bidiLevel = 0;
    456     run.glyphIndices = &glyphId;
    457     run.isSideways = FALSE;
    458     run.glyphOffsets = &offset;
    459 
    460     SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
    461     {
    462         SkAutoExclusive l(DWriteFactoryMutex);
    463         // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
    464         if (this->getDWriteTypeface()->fFactory2 &&
    465                 (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
    466                  fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
    467         {
    468             HRM(this->getDWriteTypeface()->fFactory2->CreateGlyphRunAnalysis(
    469                     &run,
    470                     &fXform,
    471                     renderingMode,
    472                     fMeasuringMode,
    473                     fGridFitMode,
    474                     fAntiAliasMode,
    475                     0.0f, // baselineOriginX,
    476                     0.0f, // baselineOriginY,
    477                     &glyphRunAnalysis),
    478                 "Could not create DW2 glyph run analysis.");
    479         } else {
    480             HRM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run,
    481                     1.0f, // pixelsPerDip,
    482                     &fXform,
    483                     renderingMode,
    484                     fMeasuringMode,
    485                     0.0f, // baselineOriginX,
    486                     0.0f, // baselineOriginY,
    487                     &glyphRunAnalysis),
    488                 "Could not create glyph run analysis.");
    489         }
    490     }
    491     {
    492         Shared l(DWriteFactoryMutex);
    493         HRM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, bbox),
    494             "Could not get texture bounds.");
    495     }
    496     return S_OK;
    497 }
    498 
    499 /** GetAlphaTextureBounds succeeds but sometimes returns empty bounds like
    500  *  { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
    501  *  for small, but not quite zero, sized glyphs.
    502  *  Only set as non-empty if the returned bounds are non-empty.
    503  */
    504 static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) {
    505     if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) {
    506         return false;
    507     }
    508 
    509     // We're trying to pack left and top into int16_t,
    510     // and width and height into uint16_t, after outsetting by 1.
    511     if (!SkIRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(
    512                 SkIRect::MakeLTRB(bbox.left, bbox.top, bbox.right, bbox.bottom))) {
    513         return false;
    514     }
    515 
    516     glyph->fWidth = SkToU16(bbox.right - bbox.left);
    517     glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
    518     glyph->fLeft = SkToS16(bbox.left);
    519     glyph->fTop = SkToS16(bbox.top);
    520     return true;
    521 }
    522 
    523 bool SkScalerContext_DW::isColorGlyph(const SkGlyph& glyph) {
    524     SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayer;
    525     return getColorGlyphRun(glyph, &colorLayer);
    526 }
    527 
    528 bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph,
    529                                           IDWriteColorGlyphRunEnumerator** colorGlyph)
    530 {
    531     FLOAT advance = 0;
    532     UINT16 glyphId = glyph.getGlyphID();
    533 
    534     DWRITE_GLYPH_OFFSET offset;
    535     offset.advanceOffset = 0.0f;
    536     offset.ascenderOffset = 0.0f;
    537 
    538     DWRITE_GLYPH_RUN run;
    539     run.glyphCount = 1;
    540     run.glyphAdvances = &advance;
    541     run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
    542     run.fontEmSize = SkScalarToFloat(fTextSizeRender);
    543     run.bidiLevel = 0;
    544     run.glyphIndices = &glyphId;
    545     run.isSideways = FALSE;
    546     run.glyphOffsets = &offset;
    547 
    548     HRESULT hr = this->getDWriteTypeface()->fFactory2->TranslateColorGlyphRun(
    549         0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph);
    550     if (hr == DWRITE_E_NOCOLOR) {
    551         return false;
    552     }
    553     HRBM(hr, "Failed to translate color glyph run");
    554     return true;
    555 }
    556 
    557 void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
    558     glyph->fWidth = 0;
    559     glyph->fHeight = 0;
    560     glyph->fLeft = 0;
    561     glyph->fTop = 0;
    562 
    563     this->generateAdvance(glyph);
    564 
    565     if (fIsColorFont && isColorGlyph(*glyph)) {
    566         glyph->fMaskFormat = SkMask::kARGB32_Format;
    567     }
    568 
    569     RECT bbox;
    570     HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox),
    571          "Requested bounding box could not be determined.");
    572 
    573     if (glyph_check_and_set_bounds(glyph, bbox)) {
    574         return;
    575     }
    576 
    577     // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
    578     // glyphs of the specified texture type. When this happens, try with the
    579     // alternate texture type.
    580     if (DWRITE_TEXTURE_CLEARTYPE_3x1 == fTextureType) {
    581         HRVM(this->getBoundingBox(glyph,
    582                                   DWRITE_RENDERING_MODE_ALIASED,
    583                                   DWRITE_TEXTURE_ALIASED_1x1,
    584                                   &bbox),
    585              "Fallback bounding box could not be determined.");
    586         if (glyph_check_and_set_bounds(glyph, bbox)) {
    587             glyph->fForceBW = 1;
    588         }
    589     }
    590     // TODO: handle the case where a request for DWRITE_TEXTURE_ALIASED_1x1
    591     // fails, and try DWRITE_TEXTURE_CLEARTYPE_3x1.
    592 }
    593 
    594 void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* metrics) {
    595     if (nullptr == metrics) {
    596         return;
    597     }
    598 
    599     sk_bzero(metrics, sizeof(*metrics));
    600 
    601     DWRITE_FONT_METRICS dwfm;
    602     if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
    603         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
    604     {
    605         this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleMetrics(
    606              fTextSizeRender,
    607              1.0f, // pixelsPerDip
    608              &fXform,
    609              &dwfm);
    610     } else {
    611         this->getDWriteTypeface()->fDWriteFontFace->GetMetrics(&dwfm);
    612     }
    613 
    614     SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
    615 
    616     metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
    617     metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
    618     metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
    619     metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
    620     metrics->fCapHeight = fTextSizeRender * SkIntToScalar(dwfm.capHeight) / upem;
    621     metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
    622     metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem);
    623     metrics->fStrikeoutThickness = fTextSizeRender * SkIntToScalar(dwfm.strikethroughThickness) / upem;
    624     metrics->fStrikeoutPosition = -(fTextSizeRender * SkIntToScalar(dwfm.strikethroughPosition) / upem);
    625 
    626     metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
    627     metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
    628     metrics->fFlags |= SkPaint::FontMetrics::kStrikeoutThicknessIsValid_Flag;
    629     metrics->fFlags |= SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag;
    630 
    631     if (this->getDWriteTypeface()->fDWriteFontFace1.get()) {
    632         DWRITE_FONT_METRICS1 dwfm1;
    633         this->getDWriteTypeface()->fDWriteFontFace1->GetMetrics(&dwfm1);
    634         metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
    635         metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
    636         metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
    637         metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
    638 
    639         metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
    640         return;
    641     }
    642 
    643     AutoTDWriteTable<SkOTTableHead> head(this->getDWriteTypeface()->fDWriteFontFace.get());
    644     if (head.fExists &&
    645         head.fSize >= sizeof(SkOTTableHead) &&
    646         head->version == SkOTTableHead::version1)
    647     {
    648         metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
    649         metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
    650         metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
    651         metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
    652 
    653         metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
    654         return;
    655     }
    656 
    657     metrics->fTop = metrics->fAscent;
    658     metrics->fBottom = metrics->fDescent;
    659 }
    660 
    661 ///////////////////////////////////////////////////////////////////////////////
    662 
    663 #include "SkColorData.h"
    664 
    665 static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) {
    666     const int width = glyph.fWidth;
    667     const size_t dstRB = (width + 7) >> 3;
    668     uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
    669 
    670     int byteCount = width >> 3;
    671     int bitCount = width & 7;
    672 
    673     for (int y = 0; y < glyph.fHeight; ++y) {
    674         if (byteCount > 0) {
    675             for (int i = 0; i < byteCount; ++i) {
    676                 unsigned byte = 0;
    677                 byte |= src[0] & (1 << 7);
    678                 byte |= src[1] & (1 << 6);
    679                 byte |= src[2] & (1 << 5);
    680                 byte |= src[3] & (1 << 4);
    681                 byte |= src[4] & (1 << 3);
    682                 byte |= src[5] & (1 << 2);
    683                 byte |= src[6] & (1 << 1);
    684                 byte |= src[7] & (1 << 0);
    685                 dst[i] = byte;
    686                 src += 8;
    687             }
    688         }
    689         if (bitCount > 0) {
    690             unsigned byte = 0;
    691             unsigned mask = 0x80;
    692             for (int i = 0; i < bitCount; i++) {
    693                 byte |= (src[i]) & mask;
    694                 mask >>= 1;
    695             }
    696             dst[byteCount] = byte;
    697         }
    698         src += bitCount;
    699         dst += dstRB;
    700     }
    701 }
    702 
    703 template<bool APPLY_PREBLEND>
    704 static void grayscale_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
    705                             const uint8_t* table8) {
    706     const size_t dstRB = glyph.rowBytes();
    707     const U16CPU width = glyph.fWidth;
    708     uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
    709 
    710     for (U16CPU y = 0; y < glyph.fHeight; y++) {
    711         for (U16CPU i = 0; i < width; i++) {
    712             U8CPU a = *(src++);
    713             dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(a, table8);
    714         }
    715         dst = SkTAddOffset<uint8_t>(dst, dstRB);
    716     }
    717 }
    718 
    719 template<bool APPLY_PREBLEND>
    720 static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
    721     const size_t dstRB = glyph.rowBytes();
    722     const U16CPU width = glyph.fWidth;
    723     uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
    724 
    725     for (U16CPU y = 0; y < glyph.fHeight; y++) {
    726         for (U16CPU i = 0; i < width; i++) {
    727             U8CPU r = *(src++);
    728             U8CPU g = *(src++);
    729             U8CPU b = *(src++);
    730             dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
    731         }
    732         dst = SkTAddOffset<uint8_t>(dst, dstRB);
    733     }
    734 }
    735 
    736 template<bool APPLY_PREBLEND, bool RGB>
    737 static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
    738                          const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
    739     const size_t dstRB = glyph.rowBytes();
    740     const U16CPU width = glyph.fWidth;
    741     uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage);
    742 
    743     for (U16CPU y = 0; y < glyph.fHeight; y++) {
    744         for (U16CPU i = 0; i < width; i++) {
    745             U8CPU r, g, b;
    746             if (RGB) {
    747                 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
    748                 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
    749                 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
    750             } else {
    751                 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
    752                 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
    753                 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
    754             }
    755             dst[i] = SkPack888ToRGB16(r, g, b);
    756         }
    757         dst = SkTAddOffset<uint16_t>(dst, dstRB);
    758     }
    759 }
    760 
    761 const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
    762                                            DWRITE_RENDERING_MODE renderingMode,
    763                                            DWRITE_TEXTURE_TYPE textureType)
    764 {
    765     int sizeNeeded = glyph.fWidth * glyph.fHeight;
    766     if (DWRITE_TEXTURE_CLEARTYPE_3x1 == textureType) {
    767         sizeNeeded *= 3;
    768     }
    769     if (sizeNeeded > fBits.count()) {
    770         fBits.setCount(sizeNeeded);
    771     }
    772 
    773     // erase
    774     memset(fBits.begin(), 0, sizeNeeded);
    775 
    776     fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
    777     fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
    778 
    779     FLOAT advance = 0.0f;
    780 
    781     UINT16 index = glyph.getGlyphID();
    782 
    783     DWRITE_GLYPH_OFFSET offset;
    784     offset.advanceOffset = 0.0f;
    785     offset.ascenderOffset = 0.0f;
    786 
    787     DWRITE_GLYPH_RUN run;
    788     run.glyphCount = 1;
    789     run.glyphAdvances = &advance;
    790     run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
    791     run.fontEmSize = SkScalarToFloat(fTextSizeRender);
    792     run.bidiLevel = 0;
    793     run.glyphIndices = &index;
    794     run.isSideways = FALSE;
    795     run.glyphOffsets = &offset;
    796     {
    797         SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
    798         {
    799             SkAutoExclusive l(DWriteFactoryMutex);
    800             // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
    801             if (this->getDWriteTypeface()->fFactory2 &&
    802                     (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
    803                      fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
    804             {
    805                 HRNM(this->getDWriteTypeface()->fFactory2->CreateGlyphRunAnalysis(&run,
    806                          &fXform,
    807                          renderingMode,
    808                          fMeasuringMode,
    809                          fGridFitMode,
    810                          fAntiAliasMode,
    811                          0.0f, // baselineOriginX,
    812                          0.0f, // baselineOriginY,
    813                          &glyphRunAnalysis),
    814                      "Could not create DW2 glyph run analysis.");
    815             } else {
    816                 HRNM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run,
    817                          1.0f, // pixelsPerDip,
    818                          &fXform,
    819                          renderingMode,
    820                          fMeasuringMode,
    821                          0.0f, // baselineOriginX,
    822                          0.0f, // baselineOriginY,
    823                          &glyphRunAnalysis),
    824                      "Could not create glyph run analysis.");
    825             }
    826         }
    827         //NOTE: this assumes that the glyph has already been measured
    828         //with an exact same glyph run analysis.
    829         RECT bbox;
    830         bbox.left = glyph.fLeft;
    831         bbox.top = glyph.fTop;
    832         bbox.right = glyph.fLeft + glyph.fWidth;
    833         bbox.bottom = glyph.fTop + glyph.fHeight;
    834         {
    835             Shared l(DWriteFactoryMutex);
    836             HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
    837                     &bbox,
    838                     fBits.begin(),
    839                     sizeNeeded),
    840                 "Could not draw mask.");
    841         }
    842     }
    843     return fBits.begin();
    844 }
    845 
    846 void SkScalerContext_DW::generateColorGlyphImage(const SkGlyph& glyph) {
    847     SkASSERT(isColorGlyph(glyph));
    848     SkASSERT(glyph.fMaskFormat == SkMask::Format::kARGB32_Format);
    849 
    850     memset(glyph.fImage, 0, glyph.computeImageSize());
    851 
    852     SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
    853     getColorGlyphRun(glyph, &colorLayers);
    854     SkASSERT(colorLayers.get());
    855 
    856     SkMatrix matrix = fSkXform;
    857     matrix.postTranslate(-SkIntToScalar(glyph.fLeft), -SkIntToScalar(glyph.fTop));
    858     SkRasterClip rc(SkIRect::MakeWH(glyph.fWidth, glyph.fHeight));
    859     SkDraw draw;
    860     draw.fDst = SkPixmap(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
    861                          glyph.fImage,
    862                          glyph.ComputeRowBytes(glyph.fWidth, SkMask::Format::kARGB32_Format));
    863     draw.fMatrix = &matrix;
    864     draw.fRC = &rc;
    865 
    866     SkPaint paint;
    867     if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
    868         paint.setFlags(SkPaint::Flags::kAntiAlias_Flag);
    869     }
    870 
    871     BOOL hasNextRun = FALSE;
    872     while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
    873         const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
    874         HRVM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
    875 
    876         SkColor color;
    877         if (colorGlyph->paletteIndex != 0xffff) {
    878             color = SkColorSetARGB(sk_float_round2int(colorGlyph->runColor.a * 255),
    879                                    sk_float_round2int(colorGlyph->runColor.r * 255),
    880                                    sk_float_round2int(colorGlyph->runColor.g * 255),
    881                                    sk_float_round2int(colorGlyph->runColor.b * 255));
    882         } else {
    883             // If all components of runColor are 0 or (equivalently) paletteIndex is 0xFFFF then
    884             // the 'current brush' is used. fRec.getLuminanceColor() is kinda sorta what is wanted
    885             // here, but not really, it will often be the wrong value because it wan't designed for
    886             // this.
    887             // TODO: implement this fully, bug.skia.org/5788
    888             color = fRec.getLuminanceColor();
    889         }
    890         paint.setColor(color);
    891 
    892         SkPath path;
    893         SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
    894         HRVM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
    895              "Could not create geometry to path converter.");
    896         {
    897             SkAutoExclusive l(DWriteFactoryMutex);
    898             HRVM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
    899                 colorGlyph->glyphRun.fontEmSize,
    900                 colorGlyph->glyphRun.glyphIndices,
    901                 colorGlyph->glyphRun.glyphAdvances,
    902                 colorGlyph->glyphRun.glyphOffsets,
    903                 colorGlyph->glyphRun.glyphCount,
    904                 colorGlyph->glyphRun.isSideways,
    905                 colorGlyph->glyphRun.bidiLevel % 2, //rtl
    906                 geometryToPath.get()),
    907                 "Could not create glyph outline.");
    908         }
    909         draw.drawPath(path, paint, nullptr, true /* pathIsMutable */);
    910     }
    911 }
    912 
    913 void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
    914     //Create the mask.
    915     DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
    916     DWRITE_TEXTURE_TYPE textureType = fTextureType;
    917     if (glyph.fForceBW) {
    918         renderingMode = DWRITE_RENDERING_MODE_ALIASED;
    919         textureType = DWRITE_TEXTURE_ALIASED_1x1;
    920     }
    921 
    922     if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
    923         generateColorGlyphImage(glyph);
    924         return;
    925     }
    926 
    927     const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
    928     if (!bits) {
    929         sk_bzero(glyph.fImage, glyph.computeImageSize());
    930         return;
    931     }
    932 
    933     //Copy the mask into the glyph.
    934     const uint8_t* src = (const uint8_t*)bits;
    935     if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
    936         bilevel_to_bw(src, glyph);
    937         const_cast<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format;
    938     } else if (!isLCD(fRec)) {
    939         if (textureType == DWRITE_TEXTURE_ALIASED_1x1) {
    940             if (fPreBlend.isApplicable()) {
    941                 grayscale_to_a8<true>(src, glyph, fPreBlend.fG);
    942             } else {
    943                 grayscale_to_a8<false>(src, glyph, fPreBlend.fG);
    944             }
    945         } else {
    946             if (fPreBlend.isApplicable()) {
    947                 rgb_to_a8<true>(src, glyph, fPreBlend.fG);
    948             } else {
    949                 rgb_to_a8<false>(src, glyph, fPreBlend.fG);
    950             }
    951         }
    952     } else {
    953         SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
    954         if (fPreBlend.isApplicable()) {
    955             if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
    956                 rgb_to_lcd16<true, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
    957             } else {
    958                 rgb_to_lcd16<true, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
    959             }
    960         } else {
    961             if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
    962                 rgb_to_lcd16<false, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
    963             } else {
    964                 rgb_to_lcd16<false, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
    965             }
    966         }
    967     }
    968 }
    969 
    970 void SkScalerContext_DW::generatePath(SkGlyphID glyph, SkPath* path) {
    971     SkASSERT(path);
    972 
    973     path->reset();
    974 
    975     SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
    976     HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath),
    977          "Could not create geometry to path converter.");
    978     UINT16 glyphId = SkTo<UINT16>(glyph);
    979     {
    980         SkAutoExclusive l(DWriteFactoryMutex);
    981         //TODO: convert to<->from DIUs? This would make a difference if hinting.
    982         //It may not be needed, it appears that DirectWrite only hints at em size.
    983         HRVM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
    984              SkScalarToFloat(fTextSizeRender),
    985              &glyphId,
    986              nullptr, //advances
    987              nullptr, //offsets
    988              1, //num glyphs
    989              FALSE, //sideways
    990              FALSE, //rtl
    991              geometryToPath.get()),
    992              "Could not create glyph outline.");
    993     }
    994 
    995     path->transform(fSkXform);
    996 }
    997 
    998 #endif//defined(SK_BUILD_FOR_WIN)
    999