1/*
2 * Copyright 2015 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 "GrTextUtils.h"
9
10#include "GrAtlasTextBlob.h"
11#include "GrBatchFontCache.h"
12#include "GrBlurUtils.h"
13#include "GrCaps.h"
14#include "GrContext.h"
15#include "GrDrawContext.h"
16
17#include "SkDistanceFieldGen.h"
18#include "SkDrawProcs.h"
19#include "SkFindAndPlaceGlyph.h"
20#include "SkGlyphCache.h"
21#include "SkPaint.h"
22#include "SkRect.h"
23#include "SkTextMapStateProc.h"
24#include "SkTextToPathIter.h"
25
26namespace {
27static const int kMinDFFontSize = 18;
28static const int kSmallDFFontSize = 32;
29static const int kSmallDFFontLimit = 32;
30static const int kMediumDFFontSize = 72;
31static const int kMediumDFFontLimit = 72;
32static const int kLargeDFFontSize = 162;
33#ifdef SK_BUILD_FOR_ANDROID
34static const int kLargeDFFontLimit = 384;
35#else
36static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
37#endif
38};
39
40void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
41                              GrBatchFontCache* fontCache,
42                              const SkSurfaceProps& props, const SkPaint& skPaint,
43                              GrColor color,
44                              const SkMatrix& viewMatrix,
45                              const char text[], size_t byteLength,
46                              SkScalar x, SkScalar y) {
47    SkASSERT(byteLength == 0 || text != nullptr);
48
49    // nothing to draw
50    if (text == nullptr || byteLength == 0) {
51        return;
52    }
53
54    // Ensure the blob is set for bitmaptext
55    blob->setHasBitmap();
56
57    GrBatchTextStrike* currStrike = nullptr;
58
59    // Get GrFontScaler from cache
60    SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::On,
61                                           skPaint, &viewMatrix);
62    GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache);
63
64    SkFindAndPlaceGlyph::ProcessText(
65        skPaint.getTextEncoding(), text, byteLength,
66        {x, y}, viewMatrix, skPaint.getTextAlign(),
67        cache,
68        [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
69            position += rounding;
70            BmpAppendGlyph(
71                blob, runIndex, fontCache, &currStrike, glyph,
72                SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
73                color, fontScaler);
74        }
75    );
76
77    SkGlyphCache::AttachCache(cache);
78}
79
80void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
81                                 GrBatchFontCache* fontCache,
82                                 const SkSurfaceProps& props, const SkPaint& skPaint,
83                                 GrColor color,
84                                 const SkMatrix& viewMatrix,
85                                 const char text[], size_t byteLength,
86                                 const SkScalar pos[], int scalarsPerPosition,
87                                 const SkPoint& offset) {
88    SkASSERT(byteLength == 0 || text != nullptr);
89    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
90
91    // nothing to draw
92    if (text == nullptr || byteLength == 0) {
93        return;
94    }
95
96    // Ensure the blob is set for bitmaptext
97    blob->setHasBitmap();
98
99    GrBatchTextStrike* currStrike = nullptr;
100
101    // Get GrFontScaler from cache
102    SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::On,
103                                           skPaint, &viewMatrix);
104    GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache);
105
106    SkFindAndPlaceGlyph::ProcessPosText(
107        skPaint.getTextEncoding(), text, byteLength,
108        offset, viewMatrix, pos, scalarsPerPosition,
109        skPaint.getTextAlign(), cache,
110        [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
111            position += rounding;
112            BmpAppendGlyph(
113                blob, runIndex, fontCache, &currStrike, glyph,
114                SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
115                color, fontScaler);
116        }
117    );
118
119    SkGlyphCache::AttachCache(cache);
120}
121
122void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
123                                 GrBatchFontCache* fontCache,
124                                 GrBatchTextStrike** strike, const SkGlyph& skGlyph,
125                                 int vx, int vy, GrColor color, GrFontScaler* scaler) {
126    if (!*strike) {
127        *strike = fontCache->getStrike(scaler);
128    }
129
130    GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
131                                         skGlyph.getSubXFixed(),
132                                         skGlyph.getSubYFixed(),
133                                         GrGlyph::kCoverage_MaskStyle);
134    GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, scaler);
135    if (!glyph) {
136        return;
137    }
138
139    int x = vx + glyph->fBounds.fLeft;
140    int y = vy + glyph->fBounds.fTop;
141
142    // keep them as ints until we've done the clip-test
143    int width = glyph->fBounds.width();
144    int height = glyph->fBounds.height();
145
146    SkRect r;
147    r.fLeft = SkIntToScalar(x);
148    r.fTop = SkIntToScalar(y);
149    r.fRight = r.fLeft + SkIntToScalar(width);
150    r.fBottom = r.fTop + SkIntToScalar(height);
151
152    blob->appendGlyph(runIndex, r, color, *strike, glyph, scaler, skGlyph,
153                      SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, false);
154}
155
156bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
157                                          const SkSurfaceProps& props, const GrShaderCaps& caps) {
158    // TODO: support perspective (need getMaxScale replacement)
159    if (viewMatrix.hasPerspective()) {
160        return false;
161    }
162
163    SkScalar maxScale = viewMatrix.getMaxScale();
164    SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
165    // Hinted text looks far better at small resolutions
166    // Scaling up beyond 2x yields undesireable artifacts
167    if (scaledTextSize < kMinDFFontSize ||
168        scaledTextSize > kLargeDFFontLimit) {
169        return false;
170    }
171
172    bool useDFT = props.isUseDeviceIndependentFonts();
173#if SK_FORCE_DISTANCE_FIELD_TEXT
174    useDFT = true;
175#endif
176
177    if (!useDFT && scaledTextSize < kLargeDFFontSize) {
178        return false;
179    }
180
181    // rasterizers and mask filters modify alpha, which doesn't
182    // translate well to distance
183    if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
184        return false;
185    }
186
187    // TODO: add some stroking support
188    if (skPaint.getStyle() != SkPaint::kFill_Style) {
189        return false;
190    }
191
192    return true;
193}
194
195void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
196                                         SkPaint* skPaint,
197                                         SkScalar* textRatio,
198                                         const SkMatrix& viewMatrix) {
199    // getMaxScale doesn't support perspective, so neither do we at the moment
200    SkASSERT(!viewMatrix.hasPerspective());
201    SkScalar maxScale = viewMatrix.getMaxScale();
202    SkScalar textSize = skPaint->getTextSize();
203    SkScalar scaledTextSize = textSize;
204    // if we have non-unity scale, we need to choose our base text size
205    // based on the SkPaint's text size multiplied by the max scale factor
206    // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
207    if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
208        scaledTextSize *= maxScale;
209    }
210
211    // We have three sizes of distance field text, and within each size 'bucket' there is a floor
212    // and ceiling.  A scale outside of this range would require regenerating the distance fields
213    SkScalar dfMaskScaleFloor;
214    SkScalar dfMaskScaleCeil;
215    if (scaledTextSize <= kSmallDFFontLimit) {
216        dfMaskScaleFloor = kMinDFFontSize;
217        dfMaskScaleCeil = kSmallDFFontLimit;
218        *textRatio = textSize / kSmallDFFontSize;
219        skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
220    } else if (scaledTextSize <= kMediumDFFontLimit) {
221        dfMaskScaleFloor = kSmallDFFontLimit;
222        dfMaskScaleCeil = kMediumDFFontLimit;
223        *textRatio = textSize / kMediumDFFontSize;
224        skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
225    } else {
226        dfMaskScaleFloor = kMediumDFFontLimit;
227        dfMaskScaleCeil = kLargeDFFontLimit;
228        *textRatio = textSize / kLargeDFFontSize;
229        skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
230    }
231
232    // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
233    // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
234    // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
235    // tolerate before we'd have to move to a large mip size.  When we actually test these values
236    // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
237    // against these values to decide if we can reuse or not(ie, will a given scale change our mip
238    // level)
239    SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
240    blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
241
242    skPaint->setLCDRenderText(false);
243    skPaint->setAutohinted(false);
244    skPaint->setHinting(SkPaint::kNormal_Hinting);
245    skPaint->setSubpixelText(true);
246}
247
248void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
249                             GrBatchFontCache* fontCache, const SkSurfaceProps& props,
250                             const SkPaint& skPaint, GrColor color,
251                             const SkMatrix& viewMatrix,
252                             const char text[], size_t byteLength,
253                             SkScalar x, SkScalar y) {
254    SkASSERT(byteLength == 0 || text != nullptr);
255
256    // nothing to draw
257    if (text == nullptr || byteLength == 0) {
258        return;
259    }
260
261    SkPaint::GlyphCacheProc glyphCacheProc = skPaint.getGlyphCacheProc(true);
262    SkAutoDescriptor desc;
263    skPaint.getScalerContextDescriptor(&desc, props, SkPaint::FakeGamma::Off, nullptr);
264    SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(),
265                                                             desc.getDesc());
266
267    SkTArray<SkScalar> positions;
268
269    const char* textPtr = text;
270    SkFixed stopX = 0;
271    SkFixed stopY = 0;
272    SkFixed origin = 0;
273    switch (skPaint.getTextAlign()) {
274        case SkPaint::kRight_Align: origin = SK_Fixed1; break;
275        case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
276        case SkPaint::kLeft_Align: origin = 0; break;
277    }
278
279    SkAutoKern autokern;
280    const char* stop = text + byteLength;
281    while (textPtr < stop) {
282        // don't need x, y here, since all subpixel variants will have the
283        // same advance
284        const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
285
286        SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
287        positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
288
289        SkFixed height = glyph.fAdvanceY;
290        positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
291
292        stopX += width;
293        stopY += height;
294    }
295    SkASSERT(textPtr == stop);
296
297    SkGlyphCache::AttachCache(origPaintCache);
298
299    // now adjust starting point depending on alignment
300    SkScalar alignX = SkFixedToScalar(stopX);
301    SkScalar alignY = SkFixedToScalar(stopY);
302    if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
303        alignX = SkScalarHalf(alignX);
304        alignY = SkScalarHalf(alignY);
305    } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
306        alignX = 0;
307        alignY = 0;
308    }
309    x -= alignX;
310    y -= alignY;
311    SkPoint offset = SkPoint::Make(x, y);
312
313    DrawDFPosText(blob, runIndex, fontCache, props, skPaint, color, viewMatrix, text, byteLength,
314                  positions.begin(), 2, offset);
315}
316
317void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
318                                GrBatchFontCache* fontCache, const SkSurfaceProps& props,
319                                const SkPaint& origPaint,
320                                GrColor color, const SkMatrix& viewMatrix,
321                                const char text[], size_t byteLength,
322                                const SkScalar pos[], int scalarsPerPosition,
323                                const SkPoint& offset) {
324    SkASSERT(byteLength == 0 || text != nullptr);
325    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
326
327    // nothing to draw
328    if (text == nullptr || byteLength == 0) {
329        return;
330    }
331
332    SkTDArray<char> fallbackTxt;
333    SkTDArray<SkScalar> fallbackPos;
334
335    // Setup distance field paint and text ratio
336    SkScalar textRatio;
337    SkPaint dfPaint(origPaint);
338    GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
339    blob->setHasDistanceField();
340    blob->setSubRunHasDistanceFields(runIndex, origPaint.isLCDRenderText());
341
342    GrBatchTextStrike* currStrike = nullptr;
343
344    SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::Off,
345                                           dfPaint, nullptr);
346    SkPaint::GlyphCacheProc glyphCacheProc = dfPaint.getGlyphCacheProc(true);
347    GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache);
348
349    const char* stop = text + byteLength;
350
351    if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
352        while (text < stop) {
353            const char* lastText = text;
354            // the last 2 parameters are ignored
355            const SkGlyph& glyph = glyphCacheProc(cache, &text);
356
357            if (glyph.fWidth) {
358                SkScalar x = offset.x() + pos[0];
359                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
360
361                if (!DfAppendGlyph(blob,
362                                   runIndex,
363                                   fontCache,
364                                   &currStrike,
365                                   glyph,
366                                   x, y, color, fontScaler,
367                                   textRatio, viewMatrix)) {
368                    // couldn't append, send to fallback
369                    fallbackTxt.append(SkToInt(text-lastText), lastText);
370                    *fallbackPos.append() = pos[0];
371                    if (2 == scalarsPerPosition) {
372                        *fallbackPos.append() = pos[1];
373                    }
374                }
375            }
376            pos += scalarsPerPosition;
377        }
378    } else {
379        SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
380                                                                             : SK_Scalar1;
381        while (text < stop) {
382            const char* lastText = text;
383            // the last 2 parameters are ignored
384            const SkGlyph& glyph = glyphCacheProc(cache, &text);
385
386            if (glyph.fWidth) {
387                SkScalar x = offset.x() + pos[0];
388                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
389
390                SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
391                SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
392
393                if (!DfAppendGlyph(blob,
394                                   runIndex,
395                                   fontCache,
396                                   &currStrike,
397                                   glyph,
398                                   x - advanceX, y - advanceY, color,
399                                   fontScaler,
400                                   textRatio,
401                                   viewMatrix)) {
402                    // couldn't append, send to fallback
403                    fallbackTxt.append(SkToInt(text-lastText), lastText);
404                    *fallbackPos.append() = pos[0];
405                    if (2 == scalarsPerPosition) {
406                        *fallbackPos.append() = pos[1];
407                    }
408                }
409            }
410            pos += scalarsPerPosition;
411        }
412    }
413
414    SkGlyphCache::AttachCache(cache);
415    if (fallbackTxt.count()) {
416        blob->initOverride(runIndex);
417        GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props,
418                                    origPaint, origPaint.getColor(), viewMatrix,
419                                    fallbackTxt.begin(), fallbackTxt.count(),
420                                    fallbackPos.begin(), scalarsPerPosition, offset);
421    }
422}
423
424bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* cache,
425                                GrBatchTextStrike** strike, const SkGlyph& skGlyph,
426                                SkScalar sx, SkScalar sy, GrColor color,
427                                GrFontScaler* scaler,
428                                SkScalar textRatio, const SkMatrix& viewMatrix) {
429    if (!*strike) {
430        *strike = cache->getStrike(scaler);
431    }
432
433    GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
434                                         skGlyph.getSubXFixed(),
435                                         skGlyph.getSubYFixed(),
436                                         GrGlyph::kDistance_MaskStyle);
437    GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, scaler);
438    if (!glyph) {
439        return true;
440    }
441
442    // fallback to color glyph support
443    if (kA8_GrMaskFormat != glyph->fMaskFormat) {
444        return false;
445    }
446
447    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
448    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
449    SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
450    SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
451
452    SkScalar scale = textRatio;
453    dx *= scale;
454    dy *= scale;
455    width *= scale;
456    height *= scale;
457    sx += dx;
458    sy += dy;
459    SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
460
461    blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, scaler, skGlyph,
462                      sx - dx, sy - dy, scale, true);
463    return true;
464}
465
466void GrTextUtils::DrawTextAsPath(GrContext* context, GrDrawContext* dc,
467                                 const GrClip& clip,
468                                 const SkPaint& skPaint, const SkMatrix& viewMatrix,
469                                 const char text[], size_t byteLength, SkScalar x, SkScalar y,
470                                 const SkIRect& clipBounds) {
471    SkTextToPathIter iter(text, byteLength, skPaint, true);
472
473    SkMatrix    matrix;
474    matrix.setScale(iter.getPathScale(), iter.getPathScale());
475    matrix.postTranslate(x, y);
476
477    const SkPath* iterPath;
478    SkScalar xpos, prevXPos = 0;
479
480    while (iter.next(&iterPath, &xpos)) {
481        matrix.postTranslate(xpos - prevXPos, 0);
482        if (iterPath) {
483            const SkPaint& pnt = iter.getPaint();
484            GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, *iterPath,
485                                                pnt, viewMatrix, &matrix, clipBounds, false);
486        }
487        prevXPos = xpos;
488    }
489}
490
491void GrTextUtils::DrawPosTextAsPath(GrContext* context,
492                                    GrDrawContext* dc,
493                                    const SkSurfaceProps& props,
494                                    const GrClip& clip,
495                                    const SkPaint& origPaint, const SkMatrix& viewMatrix,
496                                    const char text[], size_t byteLength,
497                                    const SkScalar pos[], int scalarsPerPosition,
498                                    const SkPoint& offset, const SkIRect& clipBounds) {
499    // setup our std paint, in hopes of getting hits in the cache
500    SkPaint paint(origPaint);
501    SkScalar matrixScale = paint.setupForAsPaths();
502
503    SkMatrix matrix;
504    matrix.setScale(matrixScale, matrixScale);
505
506    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
507    paint.setStyle(SkPaint::kFill_Style);
508    paint.setPathEffect(nullptr);
509
510    SkPaint::GlyphCacheProc    glyphCacheProc = paint.getGlyphCacheProc(true);
511    SkAutoGlyphCache           autoCache(paint, &props, nullptr);
512    SkGlyphCache*              cache = autoCache.getCache();
513
514    const char*        stop = text + byteLength;
515    SkTextAlignProc    alignProc(paint.getTextAlign());
516    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
517
518    // Now restore the original settings, so we "draw" with whatever style/stroking.
519    paint.setStyle(origPaint.getStyle());
520    paint.setPathEffect(origPaint.getPathEffect());
521
522    while (text < stop) {
523        const SkGlyph& glyph = glyphCacheProc(cache, &text);
524        if (glyph.fWidth) {
525            const SkPath* path = cache->findPath(glyph);
526            if (path) {
527                SkPoint tmsLoc;
528                tmsProc(pos, &tmsLoc);
529                SkPoint loc;
530                alignProc(tmsLoc, glyph, &loc);
531
532                matrix[SkMatrix::kMTransX] = loc.fX;
533                matrix[SkMatrix::kMTransY] = loc.fY;
534                GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, *path, paint,
535                                                    viewMatrix, &matrix, clipBounds, false);
536            }
537        }
538        pos += scalarsPerPosition;
539    }
540}
541
542bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
543    return !SkXfermode::AsMode(paint.getXfermode(), nullptr) ||
544           paint.getMaskFilter() ||
545           paint.getRasterizer() ||
546           paint.getPathEffect() ||
547           paint.isFakeBoldText() ||
548           paint.getStyle() != SkPaint::kFill_Style;
549}
550
551uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
552    uint32_t flags = paint.getFlags();
553
554    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
555        return flags;
556    }
557
558    if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
559        flags &= ~SkPaint::kLCDRenderText_Flag;
560        flags |= SkPaint::kGenA8FromLCD_Flag;
561    }
562
563    return flags;
564}
565
566static void glyph_cache_aux_proc(void* data) {
567    GrFontScaler* scaler = (GrFontScaler*)data;
568    SkSafeUnref(scaler);
569}
570
571GrFontScaler* GrTextUtils::GetGrFontScaler(SkGlyphCache* cache) {
572    void* auxData;
573    GrFontScaler* scaler = nullptr;
574
575    if (cache->getAuxProcData(glyph_cache_aux_proc, &auxData)) {
576        scaler = (GrFontScaler*)auxData;
577    }
578    if (nullptr == scaler) {
579        scaler = new GrFontScaler(cache);
580        cache->setAuxProc(glyph_cache_aux_proc, scaler);
581    }
582
583    return scaler;
584}
585