• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "GrAtlasTextContext.h"
8 #include "GrContext.h"
9 #include "GrContextPriv.h"
10 #include "GrTextBlobCache.h"
11 #include "SkDistanceFieldGen.h"
12 #include "SkDraw.h"
13 #include "SkDrawFilter.h"
14 #include "SkDrawProcs.h"
15 #include "SkFindAndPlaceGlyph.h"
16 #include "SkGr.h"
17 #include "SkGraphics.h"
18 #include "SkMakeUnique.h"
19 #include "SkMaskFilterBase.h"
20 #include "SkTextMapStateProc.h"
21 
22 #include "ops/GrMeshDrawOp.h"
23 
24 // DF sizes and thresholds for usage of the small and medium sizes. For example, above
25 // kSmallDFFontLimit we will use the medium size. The large size is used up until the size at
26 // which we switch over to drawing as paths as controlled by Options.
27 static const int kSmallDFFontSize = 32;
28 static const int kSmallDFFontLimit = 32;
29 static const int kMediumDFFontSize = 72;
30 static const int kMediumDFFontLimit = 72;
31 static const int kLargeDFFontSize = 162;
32 
33 static const int kDefaultMinDistanceFieldFontSize = 18;
34 #ifdef SK_BUILD_FOR_ANDROID
35 static const int kDefaultMaxDistanceFieldFontSize = 384;
36 #else
37 static const int kDefaultMaxDistanceFieldFontSize = 2 * kLargeDFFontSize;
38 #endif
39 
GrAtlasTextContext(const Options & options)40 GrAtlasTextContext::GrAtlasTextContext(const Options& options)
41         : fDistanceAdjustTable(new GrDistanceFieldAdjustTable) {
42     fMaxDistanceFieldFontSize = options.fMaxDistanceFieldFontSize < 0.f
43                                         ? kDefaultMaxDistanceFieldFontSize
44                                         : options.fMaxDistanceFieldFontSize;
45     fMinDistanceFieldFontSize = options.fMinDistanceFieldFontSize < 0.f
46                                         ? kDefaultMinDistanceFieldFontSize
47                                         : options.fMinDistanceFieldFontSize;
48     fDistanceFieldVerticesAlwaysHaveW = options.fDistanceFieldVerticesAlwaysHaveW;
49 }
50 
Make(const Options & options)51 std::unique_ptr<GrAtlasTextContext> GrAtlasTextContext::Make(const Options& options) {
52     return std::unique_ptr<GrAtlasTextContext>(new GrAtlasTextContext(options));
53 }
54 
ComputeCanonicalColor(const SkPaint & paint,bool lcd)55 SkColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
56     SkColor canonicalColor = paint.computeLuminanceColor();
57     if (lcd) {
58         // This is the correct computation, but there are tons of cases where LCD can be overridden.
59         // For now we just regenerate if any run in a textblob has LCD.
60         // TODO figure out where all of these overrides are and see if we can incorporate that logic
61         // at a higher level *OR* use sRGB
62         SkASSERT(false);
63         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
64     } else {
65         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
66         // gamma corrected masks anyways, nor color
67         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
68                                        SkColorGetG(canonicalColor),
69                                        SkColorGetB(canonicalColor));
70         // reduce to our finite number of bits
71         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
72     }
73     return canonicalColor;
74 }
75 
ComputeScalerContextFlags(const GrColorSpaceInfo & colorSpaceInfo)76 SkScalerContextFlags GrAtlasTextContext::ComputeScalerContextFlags(
77         const GrColorSpaceInfo& colorSpaceInfo) {
78     // If we're doing gamma-correct rendering, then we can disable the gamma hacks.
79     // Otherwise, leave them on. In either case, we still want the contrast boost:
80     if (colorSpaceInfo.isGammaCorrect()) {
81         return SkScalerContextFlags::kBoostContrast;
82     } else {
83         return SkScalerContextFlags::kFakeGammaAndBoostContrast;
84     }
85 }
86 
87 // TODO if this function ever shows up in profiling, then we can compute this value when the
88 // textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
89 // run so this is not a big deal to compute here.
HasLCD(const SkTextBlob * blob)90 bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
91     SkTextBlobRunIterator it(blob);
92     for (; !it.done(); it.next()) {
93         if (it.isLCD()) {
94             return true;
95         }
96     }
97     return false;
98 }
99 
drawTextBlob(GrContext * context,GrTextUtils::Target * target,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkTextBlob * blob,SkScalar x,SkScalar y,SkDrawFilter * drawFilter,const SkIRect & clipBounds)100 void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target,
101                                       const GrClip& clip, const SkPaint& skPaint,
102                                       const SkMatrix& viewMatrix, const SkSurfaceProps& props,
103                                       const SkTextBlob* blob, SkScalar x, SkScalar y,
104                                       SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
105     // If we have been abandoned, then don't draw
106     if (context->abandoned()) {
107         return;
108     }
109 
110     sk_sp<GrAtlasTextBlob> cacheBlob;
111     SkMaskFilterBase::BlurRec blurRec;
112     GrAtlasTextBlob::Key key;
113     // It might be worth caching these things, but its not clear at this time
114     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
115     const SkMaskFilter* mf = skPaint.getMaskFilter();
116     bool canCache = !(skPaint.getPathEffect() ||
117                       (mf && !as_MFB(mf)->asABlur(&blurRec)) ||
118                       drawFilter);
119     SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
120 
121     auto glyphCache = context->contextPriv().getGlyphCache();
122     auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager();
123     GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
124 
125     if (canCache) {
126         bool hasLCD = HasLCD(blob);
127 
128         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
129         SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
130                                                  kUnknown_SkPixelGeometry;
131 
132         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
133         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
134         // ensure we always match the same key
135         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
136                                           ComputeCanonicalColor(skPaint, hasLCD);
137 
138         key.fPixelGeometry = pixelGeometry;
139         key.fUniqueID = blob->uniqueID();
140         key.fStyle = skPaint.getStyle();
141         key.fHasBlur = SkToBool(mf);
142         key.fCanonicalColor = canonicalColor;
143         key.fScalerContextFlags = scalerContextFlags;
144         cacheBlob = textBlobCache->find(key);
145     }
146 
147     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
148     if (cacheBlob) {
149         if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
150             // We have to remake the blob because changes may invalidate our masks.
151             // TODO we could probably get away reuse most of the time if the pointer is unique,
152             // but we'd have to clear the subrun information
153             textBlobCache->remove(cacheBlob.get());
154             cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
155             this->regenerateTextBlob(cacheBlob.get(), glyphCache,
156                                      *context->caps()->shaderCaps(), paint, scalerContextFlags,
157                                      viewMatrix, props, blob, x, y, drawFilter);
158         } else {
159             textBlobCache->makeMRU(cacheBlob.get());
160 
161             if (CACHE_SANITY_CHECK) {
162                 int glyphCount = 0;
163                 int runCount = 0;
164                 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
165                 sk_sp<GrAtlasTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
166                 sanityBlob->setupKey(key, blurRec, skPaint);
167                 this->regenerateTextBlob(sanityBlob.get(), glyphCache,
168                                          *context->caps()->shaderCaps(), paint, scalerContextFlags,
169                                          viewMatrix, props, blob, x, y, drawFilter);
170                 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
171             }
172         }
173     } else {
174         if (canCache) {
175             cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
176         } else {
177             cacheBlob = textBlobCache->makeBlob(blob);
178         }
179         this->regenerateTextBlob(cacheBlob.get(), glyphCache,
180                                  *context->caps()->shaderCaps(), paint, scalerContextFlags,
181                                  viewMatrix, props, blob, x, y, drawFilter);
182     }
183 
184     cacheBlob->flush(restrictedAtlasManager, target, props, fDistanceAdjustTable.get(), paint,
185                      clip, viewMatrix, clipBounds, x, y);
186 }
187 
regenerateTextBlob(GrAtlasTextBlob * cacheBlob,GrGlyphCache * glyphCache,const GrShaderCaps & shaderCaps,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkTextBlob * blob,SkScalar x,SkScalar y,SkDrawFilter * drawFilter) const188 void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
189                                             GrGlyphCache* glyphCache,
190                                             const GrShaderCaps& shaderCaps,
191                                             const GrTextUtils::Paint& paint,
192                                             SkScalerContextFlags scalerContextFlags,
193                                             const SkMatrix& viewMatrix,
194                                             const SkSurfaceProps& props, const SkTextBlob* blob,
195                                             SkScalar x, SkScalar y,
196                                             SkDrawFilter* drawFilter) const {
197     cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y);
198 
199     // Regenerate textblob
200     SkTextBlobRunIterator it(blob);
201     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
202     for (int run = 0; !it.done(); it.next(), run++) {
203         int glyphCount = it.glyphCount();
204         size_t textLen = glyphCount * sizeof(uint16_t);
205         const SkPoint& offset = it.offset();
206         cacheBlob->push_back_run(run);
207         if (!runPaint.modifyForRun([it](SkPaint* p) { it.applyFontToPaint(p); })) {
208             continue;
209         }
210         cacheBlob->setRunPaintFlags(run, runPaint.skPaint().getFlags());
211 
212         if (this->canDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
213             switch (it.positioning()) {
214                 case SkTextBlob::kDefault_Positioning: {
215                     this->drawDFText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
216                                      viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
217                                      y + offset.y());
218                     break;
219                 }
220                 case SkTextBlob::kHorizontal_Positioning: {
221                     SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
222                     this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
223                                         scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
224                                         textLen, it.pos(), 1, dfOffset);
225                     break;
226                 }
227                 case SkTextBlob::kFull_Positioning: {
228                     SkPoint dfOffset = SkPoint::Make(x, y);
229                     this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
230                                         scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
231                                         textLen, it.pos(), 2, dfOffset);
232                     break;
233                 }
234             }
235         } else {
236             switch (it.positioning()) {
237                 case SkTextBlob::kDefault_Positioning:
238                     DrawBmpText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
239                                 viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
240                                 y + offset.y());
241                     break;
242                 case SkTextBlob::kHorizontal_Positioning:
243                     DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
244                                    viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
245                                    SkPoint::Make(x, y + offset.y()));
246                     break;
247                 case SkTextBlob::kFull_Positioning:
248                     DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
249                                    viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
250                                    SkPoint::Make(x, y));
251                     break;
252             }
253         }
254     }
255 }
256 
257 inline sk_sp<GrAtlasTextBlob>
makeDrawTextBlob(GrTextBlobCache * blobCache,GrGlyphCache * glyphCache,const GrShaderCaps & shaderCaps,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const char text[],size_t byteLength,SkScalar x,SkScalar y) const258 GrAtlasTextContext::makeDrawTextBlob(GrTextBlobCache* blobCache,
259                                      GrGlyphCache* glyphCache,
260                                      const GrShaderCaps& shaderCaps,
261                                      const GrTextUtils::Paint& paint,
262                                      SkScalerContextFlags scalerContextFlags,
263                                      const SkMatrix& viewMatrix,
264                                      const SkSurfaceProps& props,
265                                      const char text[], size_t byteLength,
266                                      SkScalar x, SkScalar y) const {
267     int glyphCount = paint.skPaint().countText(text, byteLength);
268     if (!glyphCount) {
269         return nullptr;
270     }
271     sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
272     blob->initThrowawayBlob(viewMatrix, x, y);
273     blob->setRunPaintFlags(0, paint.skPaint().getFlags());
274 
275     if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
276         this->drawDFText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
277                          text, byteLength, x, y);
278     } else {
279         DrawBmpText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix, text,
280                     byteLength, x, y);
281     }
282     return blob;
283 }
284 
285 inline sk_sp<GrAtlasTextBlob>
makeDrawPosTextBlob(GrTextBlobCache * blobCache,GrGlyphCache * glyphCache,const GrShaderCaps & shaderCaps,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset) const286 GrAtlasTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache,
287                                         GrGlyphCache* glyphCache,
288                                         const GrShaderCaps& shaderCaps,
289                                         const GrTextUtils::Paint& paint,
290                                         SkScalerContextFlags scalerContextFlags,
291                                         const SkMatrix& viewMatrix,
292                                         const SkSurfaceProps& props,
293                                         const char text[], size_t byteLength,
294                                         const SkScalar pos[], int scalarsPerPosition, const
295                                         SkPoint& offset) const {
296     int glyphCount = paint.skPaint().countText(text, byteLength);
297     if (!glyphCount) {
298         return nullptr;
299     }
300 
301     sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
302     blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
303     blob->setRunPaintFlags(0, paint.skPaint().getFlags());
304 
305     if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
306         this->drawDFPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
307                             text, byteLength, pos, scalarsPerPosition, offset);
308     } else {
309         DrawBmpPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
310                        text, byteLength, pos, scalarsPerPosition, offset);
311     }
312     return blob;
313 }
314 
drawText(GrContext * context,GrTextUtils::Target * target,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & regionClipBounds)315 void GrAtlasTextContext::drawText(GrContext* context, GrTextUtils::Target* target,
316                                   const GrClip& clip, const SkPaint& skPaint,
317                                   const SkMatrix& viewMatrix, const SkSurfaceProps& props,
318                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
319                                   const SkIRect& regionClipBounds) {
320     if (context->abandoned()) {
321         return;
322     }
323 
324     auto glyphCache = context->contextPriv().getGlyphCache();
325     auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager();
326     auto textBlobCache = context->contextPriv().getTextBlobCache();
327 
328     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
329     sk_sp<GrAtlasTextBlob> blob(
330             this->makeDrawTextBlob(textBlobCache, glyphCache,
331                                     *context->caps()->shaderCaps(), paint,
332                                     ComputeScalerContextFlags(target->colorSpaceInfo()),
333                                     viewMatrix, props, text, byteLength, x, y));
334     if (blob) {
335         blob->flush(restrictedAtlasManager, target, props, fDistanceAdjustTable.get(), paint,
336                     clip, viewMatrix, regionClipBounds, x, y);
337     }
338 }
339 
drawPosText(GrContext * context,GrTextUtils::Target * target,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & regionClipBounds)340 void GrAtlasTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target,
341                                      const GrClip& clip, const SkPaint& skPaint,
342                                      const SkMatrix& viewMatrix, const SkSurfaceProps& props,
343                                      const char text[], size_t byteLength, const SkScalar pos[],
344                                      int scalarsPerPosition, const SkPoint& offset,
345                                      const SkIRect& regionClipBounds) {
346     GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
347     if (context->abandoned()) {
348         return;
349     }
350 
351     auto glyphCache = context->contextPriv().getGlyphCache();
352     auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager();
353     auto textBlobCache = context->contextPriv().getTextBlobCache();
354 
355     sk_sp<GrAtlasTextBlob> blob(this->makeDrawPosTextBlob(
356             textBlobCache, glyphCache,
357             *context->caps()->shaderCaps(), paint,
358             ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
359             byteLength, pos, scalarsPerPosition, offset));
360     if (blob) {
361         blob->flush(restrictedAtlasManager, target, props, fDistanceAdjustTable.get(), paint,
362                     clip, viewMatrix, regionClipBounds, offset.fX, offset.fY);
363     }
364 }
365 
DrawBmpText(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y)366 void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
367                                      GrGlyphCache* glyphCache, const SkSurfaceProps& props,
368                                      const GrTextUtils::Paint& paint,
369                                      SkScalerContextFlags scalerContextFlags,
370                                      const SkMatrix& viewMatrix, const char text[],
371                                      size_t byteLength, SkScalar x, SkScalar y) {
372     SkASSERT(byteLength == 0 || text != nullptr);
373 
374     // nothing to draw
375     if (text == nullptr || byteLength == 0) {
376         return;
377     }
378 
379     // Ensure the blob is set for bitmaptext
380     blob->setHasBitmap();
381 
382     if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
383         DrawBmpTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags, viewMatrix,
384                            text, byteLength, x, y);
385         return;
386     }
387 
388     sk_sp<GrTextStrike> currStrike;
389     SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
390     SkFindAndPlaceGlyph::ProcessText(paint.skPaint().getTextEncoding(), text, byteLength, {x, y},
391                                      viewMatrix, paint.skPaint().getTextAlign(), cache,
392                                      [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
393                                          position += rounding;
394                                          BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike,
395                                                         glyph, SkScalarFloorToScalar(position.fX),
396                                                         SkScalarFloorToScalar(position.fY),
397                                                         paint.filteredPremulColor(), cache,
398                                                         SK_Scalar1);
399                                      });
400 
401     SkGlyphCache::AttachCache(cache);
402 }
403 
DrawBmpPosText(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset)404 void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
405                                         GrGlyphCache* glyphCache, const SkSurfaceProps& props,
406                                         const GrTextUtils::Paint& paint,
407                                         SkScalerContextFlags scalerContextFlags,
408                                         const SkMatrix& viewMatrix,
409                                         const char text[], size_t byteLength, const SkScalar pos[],
410                                         int scalarsPerPosition, const SkPoint& offset) {
411     SkASSERT(byteLength == 0 || text != nullptr);
412     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
413 
414     // nothing to draw
415     if (text == nullptr || byteLength == 0) {
416         return;
417     }
418 
419     // Ensure the blob is set for bitmaptext
420     blob->setHasBitmap();
421 
422     if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
423         DrawBmpPosTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags,
424                               viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
425         return;
426     }
427 
428     sk_sp<GrTextStrike> currStrike;
429     SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
430     SkFindAndPlaceGlyph::ProcessPosText(
431             paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
432             scalarsPerPosition, paint.skPaint().getTextAlign(), cache,
433             [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
434                 position += rounding;
435                 BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
436                                SkScalarFloorToScalar(position.fX),
437                                SkScalarFloorToScalar(position.fY),
438                                paint.filteredPremulColor(), cache, SK_Scalar1);
439             });
440 
441     SkGlyphCache::AttachCache(cache);
442 }
443 
DrawBmpTextAsPaths(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & origPaint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y)444 void GrAtlasTextContext::DrawBmpTextAsPaths(GrAtlasTextBlob* blob, int runIndex,
445                                             GrGlyphCache* glyphCache,
446                                             const SkSurfaceProps& props,
447                                             const GrTextUtils::Paint& origPaint,
448                                             SkScalerContextFlags scalerContextFlags,
449                                             const SkMatrix& viewMatrix, const char text[],
450                                             size_t byteLength, SkScalar x, SkScalar y) {
451     // nothing to draw
452     if (text == nullptr || byteLength == 0) {
453         return;
454     }
455 
456     // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
457     SkPaint pathPaint(origPaint);
458     pathPaint.setStyle(SkPaint::kFill_Style);
459     pathPaint.setPathEffect(nullptr);
460 
461     GrTextUtils::PathTextIter iter(text, byteLength, pathPaint, true);
462     FallbackTextHelper fallbackTextHelper(viewMatrix, pathPaint, glyphCache, iter.getPathScale());
463 
464     const SkGlyph* iterGlyph;
465     const SkPath* iterPath;
466     SkScalar xpos = 0;
467     const char* lastText = text;
468     while (iter.next(&iterGlyph, &iterPath, &xpos)) {
469         if (iterGlyph) {
470             SkPoint pos = SkPoint::Make(xpos + x, y);
471             fallbackTextHelper.appendText(*iterGlyph, iter.getText() - lastText, lastText, pos);
472         } else if (iterPath) {
473             blob->appendPathGlyph(runIndex, *iterPath, xpos + x, y, iter.getPathScale(), false);
474         }
475         lastText = iter.getText();
476     }
477 
478     fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags);
479 }
480 
DrawBmpPosTextAsPaths(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & origPaint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset)481 void GrAtlasTextContext::DrawBmpPosTextAsPaths(GrAtlasTextBlob* blob, int runIndex,
482                                                GrGlyphCache* glyphCache,
483                                                const SkSurfaceProps& props,
484                                                const GrTextUtils::Paint& origPaint,
485                                                SkScalerContextFlags scalerContextFlags,
486                                                const SkMatrix& viewMatrix,
487                                                const char text[], size_t byteLength,
488                                                const SkScalar pos[], int scalarsPerPosition,
489                                                const SkPoint& offset) {
490     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
491 
492     // nothing to draw
493     if (text == nullptr || byteLength == 0) {
494         return;
495     }
496 
497     // setup our std paint, in hopes of getting hits in the cache
498     SkPaint pathPaint(origPaint);
499     SkScalar matrixScale = pathPaint.setupForAsPaths();
500     FallbackTextHelper fallbackTextHelper(viewMatrix, origPaint, glyphCache, matrixScale);
501 
502     // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
503     pathPaint.setStyle(SkPaint::kFill_Style);
504     pathPaint.setPathEffect(nullptr);
505 
506     SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(pathPaint.getTextEncoding(),
507                                                                         pathPaint.isDevKernText(),
508                                                                         true);
509     SkAutoGlyphCache           autoCache(pathPaint, &props, nullptr);
510     SkGlyphCache*              cache = autoCache.getCache();
511 
512     const char*        stop = text + byteLength;
513     const char*        lastText = text;
514     SkTextAlignProc    alignProc(pathPaint.getTextAlign());
515     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
516 
517     while (text < stop) {
518         const SkGlyph& glyph = glyphCacheProc(cache, &text);
519         if (glyph.fWidth) {
520             SkPoint tmsLoc;
521             tmsProc(pos, &tmsLoc);
522             SkPoint loc;
523             alignProc(tmsLoc, glyph, &loc);
524             if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
525                 fallbackTextHelper.appendText(glyph, text - lastText, lastText, loc);
526             } else {
527                 const SkPath* path = cache->findPath(glyph);
528                 if (path) {
529                     blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
530                 }
531             }
532         }
533         lastText = text;
534         pos += scalarsPerPosition;
535     }
536 
537     fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags);
538 }
539 
BmpAppendGlyph(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * grGlyphCache,sk_sp<GrTextStrike> * strike,const SkGlyph & skGlyph,SkScalar sx,SkScalar sy,GrColor color,SkGlyphCache * skGlyphCache,SkScalar textRatio)540 void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
541                                         GrGlyphCache* grGlyphCache,
542                                         sk_sp<GrTextStrike>* strike,
543                                         const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
544                                         GrColor color, SkGlyphCache* skGlyphCache,
545                                         SkScalar textRatio) {
546     if (!*strike) {
547         *strike = grGlyphCache->getStrike(skGlyphCache);
548     }
549 
550     GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
551                                          skGlyph.getSubXFixed(),
552                                          skGlyph.getSubYFixed(),
553                                          GrGlyph::kCoverage_MaskStyle);
554     GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
555     if (!glyph) {
556         return;
557     }
558 
559     SkASSERT(skGlyph.fWidth == glyph->width());
560     SkASSERT(skGlyph.fHeight == glyph->height());
561 
562     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
563     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
564     SkScalar width = SkIntToScalar(glyph->fBounds.width());
565     SkScalar height = SkIntToScalar(glyph->fBounds.height());
566 
567     dx *= textRatio;
568     dy *= textRatio;
569     width *= textRatio;
570     height *= textRatio;
571 
572     SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
573 
574     blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
575                       textRatio, true);
576 }
577 
canDrawAsDistanceFields(const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const GrShaderCaps & caps) const578 bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
579                                                  const SkSurfaceProps& props,
580                                                  const GrShaderCaps& caps) const {
581     if (!viewMatrix.hasPerspective()) {
582         SkScalar maxScale = viewMatrix.getMaxScale();
583         SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
584         // Hinted text looks far better at small resolutions
585         // Scaling up beyond 2x yields undesireable artifacts
586         if (scaledTextSize < fMinDistanceFieldFontSize ||
587             scaledTextSize > fMaxDistanceFieldFontSize) {
588             return false;
589         }
590 
591         bool useDFT = props.isUseDeviceIndependentFonts();
592 #if SK_FORCE_DISTANCE_FIELD_TEXT
593         useDFT = true;
594 #endif
595 
596         if (!useDFT && scaledTextSize < kLargeDFFontSize) {
597             return false;
598         }
599     }
600 
601     // mask filters modify alpha, which doesn't translate well to distance
602     if (skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
603         return false;
604     }
605 
606     // TODO: add some stroking support
607     if (skPaint.getStyle() != SkPaint::kFill_Style) {
608         return false;
609     }
610 
611     return true;
612 }
613 
initDistanceFieldPaint(GrAtlasTextBlob * blob,SkPaint * skPaint,SkScalar * textRatio,const SkMatrix & viewMatrix) const614 void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
615                                                 SkPaint* skPaint,
616                                                 SkScalar* textRatio,
617                                                 const SkMatrix& viewMatrix) const {
618     SkScalar textSize = skPaint->getTextSize();
619     SkScalar scaledTextSize = textSize;
620 
621     if (viewMatrix.hasPerspective()) {
622         // for perspective, we simply force to the medium size
623         // TODO: compute a size based on approximate screen area
624         scaledTextSize = kMediumDFFontLimit;
625     } else {
626         SkScalar maxScale = viewMatrix.getMaxScale();
627         // if we have non-unity scale, we need to choose our base text size
628         // based on the SkPaint's text size multiplied by the max scale factor
629         // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
630         if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
631             scaledTextSize *= maxScale;
632         }
633     }
634 
635     // We have three sizes of distance field text, and within each size 'bucket' there is a floor
636     // and ceiling.  A scale outside of this range would require regenerating the distance fields
637     SkScalar dfMaskScaleFloor;
638     SkScalar dfMaskScaleCeil;
639     if (scaledTextSize <= kSmallDFFontLimit) {
640         dfMaskScaleFloor = fMinDistanceFieldFontSize;
641         dfMaskScaleCeil = kSmallDFFontLimit;
642         *textRatio = textSize / kSmallDFFontSize;
643         skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
644     } else if (scaledTextSize <= kMediumDFFontLimit) {
645         dfMaskScaleFloor = kSmallDFFontLimit;
646         dfMaskScaleCeil = kMediumDFFontLimit;
647         *textRatio = textSize / kMediumDFFontSize;
648         skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
649     } else {
650         dfMaskScaleFloor = kMediumDFFontLimit;
651         dfMaskScaleCeil = fMaxDistanceFieldFontSize;
652         *textRatio = textSize / kLargeDFFontSize;
653         skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
654     }
655 
656     // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
657     // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
658     // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
659     // tolerate before we'd have to move to a large mip size.  When we actually test these values
660     // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
661     // against these values to decide if we can reuse or not(ie, will a given scale change our mip
662     // level)
663     SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
664     blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
665 
666     skPaint->setAntiAlias(true);
667     skPaint->setLCDRenderText(false);
668     skPaint->setAutohinted(false);
669     skPaint->setHinting(SkPaint::kNormal_Hinting);
670     skPaint->setSubpixelText(true);
671 }
672 
drawDFText(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y) const673 void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex,
674                                     GrGlyphCache* glyphCache, const SkSurfaceProps& props,
675                                     const GrTextUtils::Paint& paint,
676                                     SkScalerContextFlags scalerContextFlags,
677                                     const SkMatrix& viewMatrix, const char text[],
678                                     size_t byteLength, SkScalar x, SkScalar y) const {
679     SkASSERT(byteLength == 0 || text != nullptr);
680 
681     // nothing to draw
682     if (text == nullptr || byteLength == 0) {
683         return;
684     }
685 
686     const SkPaint& skPaint = paint.skPaint();
687     SkPaint::GlyphCacheProc glyphCacheProc =
688             SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), skPaint.isDevKernText(), true);
689     SkAutoDescriptor desc;
690     SkScalerContextEffects effects;
691     // We apply the fake-gamma by altering the distance in the shader, so we ignore the
692     // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
693     SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
694         skPaint, &props, SkScalerContextFlags::kNone, nullptr, &desc, &effects);
695     SkGlyphCache* origPaintCache =
696             SkGlyphCache::DetachCache(skPaint.getTypeface(), effects, desc.getDesc());
697 
698     SkTArray<SkScalar> positions;
699 
700     const char* textPtr = text;
701     SkScalar stopX = 0;
702     SkScalar stopY = 0;
703     SkScalar origin = 0;
704     switch (skPaint.getTextAlign()) {
705         case SkPaint::kRight_Align: origin = SK_Scalar1; break;
706         case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
707         case SkPaint::kLeft_Align: origin = 0; break;
708     }
709 
710     SkAutoKern autokern;
711     const char* stop = text + byteLength;
712     while (textPtr < stop) {
713         // don't need x, y here, since all subpixel variants will have the
714         // same advance
715         const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
716 
717         SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
718         positions.push_back(stopX + origin * width);
719 
720         SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
721         positions.push_back(stopY + origin * height);
722 
723         stopX += width;
724         stopY += height;
725     }
726     SkASSERT(textPtr == stop);
727 
728     SkGlyphCache::AttachCache(origPaintCache);
729 
730     // now adjust starting point depending on alignment
731     SkScalar alignX = stopX;
732     SkScalar alignY = stopY;
733     if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
734         alignX = SkScalarHalf(alignX);
735         alignY = SkScalarHalf(alignY);
736     } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
737         alignX = 0;
738         alignY = 0;
739     }
740     x -= alignX;
741     y -= alignY;
742     SkPoint offset = SkPoint::Make(x, y);
743 
744     this->drawDFPosText(blob, runIndex, glyphCache, props, paint, scalerContextFlags, viewMatrix,
745                         text, byteLength, positions.begin(), 2, offset);
746 }
747 
drawDFPosText(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset) const748 void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex,
749                                        GrGlyphCache* glyphCache, const SkSurfaceProps& props,
750                                        const GrTextUtils::Paint& paint,
751                                        SkScalerContextFlags scalerContextFlags,
752                                        const SkMatrix& viewMatrix, const char text[],
753                                        size_t byteLength, const SkScalar pos[],
754                                        int scalarsPerPosition, const SkPoint& offset) const {
755     SkASSERT(byteLength == 0 || text != nullptr);
756     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
757 
758     // nothing to draw
759     if (text == nullptr || byteLength == 0) {
760         return;
761     }
762 
763     bool hasWCoord = viewMatrix.hasPerspective() || fDistanceFieldVerticesAlwaysHaveW;
764 
765     // Setup distance field paint and text ratio
766     SkScalar textRatio;
767     SkPaint dfPaint(paint);
768     this->initDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
769     blob->setHasDistanceField();
770     blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
771                                      paint.skPaint().isAntiAlias(), hasWCoord);
772 
773     FallbackTextHelper fallbackTextHelper(viewMatrix, paint, glyphCache, textRatio);
774 
775     sk_sp<GrTextStrike> currStrike;
776 
777     // We apply the fake-gamma by altering the distance in the shader, so we ignore the
778     // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
779     SkGlyphCache* cache =
780             blob->setupCache(runIndex, props, SkScalerContextFlags::kNone, dfPaint, nullptr);
781     SkPaint::GlyphCacheProc glyphCacheProc =
782             SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), dfPaint.isDevKernText(), true);
783 
784     const char* stop = text + byteLength;
785 
786     SkPaint::Align align = dfPaint.getTextAlign();
787     SkScalar alignMul = SkPaint::kCenter_Align == align ? SK_ScalarHalf :
788                         (SkPaint::kRight_Align == align ? SK_Scalar1 : 0);
789     while (text < stop) {
790         const char* lastText = text;
791         // the last 2 parameters are ignored
792         const SkGlyph& glyph = glyphCacheProc(cache, &text);
793 
794         if (glyph.fWidth) {
795             SkPoint glyphPos(offset);
796             glyphPos.fX += pos[0] - SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
797             glyphPos.fY += (2 == scalarsPerPosition ? pos[1] : 0) -
798                            SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
799 
800             if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
801                 DfAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos.fX,
802                               glyphPos.fY, paint.filteredPremulColor(), cache, textRatio);
803             } else {
804                 // can't append color glyph to SDF batch, send to fallback
805                 fallbackTextHelper.appendText(glyph, SkToInt(text - lastText), lastText, glyphPos);
806             }
807         }
808         pos += scalarsPerPosition;
809     }
810 
811     SkGlyphCache::AttachCache(cache);
812 
813     fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, paint, scalerContextFlags);
814 }
815 
816 // TODO: merge with BmpAppendGlyph
DfAppendGlyph(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * grGlyphCache,sk_sp<GrTextStrike> * strike,const SkGlyph & skGlyph,SkScalar sx,SkScalar sy,GrColor color,SkGlyphCache * skGlyphCache,SkScalar textRatio)817 void GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
818                                        GrGlyphCache* grGlyphCache, sk_sp<GrTextStrike>* strike,
819                                        const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
820                                        GrColor color, SkGlyphCache* skGlyphCache,
821                                        SkScalar textRatio) {
822     if (!*strike) {
823         *strike = grGlyphCache->getStrike(skGlyphCache);
824     }
825 
826     GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
827                                          skGlyph.getSubXFixed(),
828                                          skGlyph.getSubYFixed(),
829                                          GrGlyph::kDistance_MaskStyle);
830     GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
831     if (!glyph) {
832         return;
833     }
834 
835     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
836     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
837     SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
838     SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
839 
840     dx *= textRatio;
841     dy *= textRatio;
842     width *= textRatio;
843     height *= textRatio;
844     SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
845 
846     blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
847                       textRatio, false);
848 }
849 
850 ///////////////////////////////////////////////////////////////////////////////////////////////////
851 
appendText(const SkGlyph & glyph,int count,const char * text,SkPoint glyphPos)852 void GrAtlasTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, int count,
853                                                         const char* text, SkPoint glyphPos) {
854     SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
855     if (!fUseScaledFallback) {
856         SkScalar scaledGlyphSize = maxDim * fMaxScale;
857         if (!fViewMatrix.hasPerspective() && scaledGlyphSize > fMaxTextSize) {
858             fUseScaledFallback = true;
859         }
860     }
861 
862     fFallbackTxt.append(count, text);
863     if (fUseScaledFallback) {
864         // If there's a glyph in the font that's particularly large, it's possible
865         // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
866         // that glyph than make the others pixelated, so we set a minimum size of half the
867         // maximum text size to avoid this case.
868         SkScalar glyphTextSize = SkTMax(SkScalarFloorToScalar(fMaxTextSize*fTextSize / maxDim),
869                                         0.5f*fMaxTextSize);
870         fScaledFallbackTextSize = SkTMin(glyphTextSize, fScaledFallbackTextSize);
871     }
872     *fFallbackPos.append() = glyphPos;
873 }
874 
drawText(GrAtlasTextBlob * blob,int runIndex,GrGlyphCache * glyphCache,const SkSurfaceProps & props,const GrTextUtils::Paint & paint,SkScalerContextFlags scalerContextFlags)875 void GrAtlasTextContext::FallbackTextHelper::drawText(GrAtlasTextBlob* blob, int runIndex,
876                                                       GrGlyphCache* glyphCache,
877                                                       const SkSurfaceProps& props,
878                                                       const GrTextUtils::Paint& paint,
879                                                       SkScalerContextFlags scalerContextFlags) {
880     if (fFallbackTxt.count()) {
881         blob->initOverride(runIndex);
882         blob->setHasBitmap();
883         SkGlyphCache* cache = nullptr;
884         const SkPaint& skPaint = paint.skPaint();
885         SkPaint::GlyphCacheProc glyphCacheProc =
886             SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
887                                        skPaint.isDevKernText(), true);
888         SkColor textColor = paint.filteredPremulColor();
889         SkScalar textRatio = SK_Scalar1;
890         fViewMatrix.mapPoints(fFallbackPos.begin(), fFallbackPos.count());
891         if (fUseScaledFallback) {
892             // Set up paint and matrix to scale glyphs
893             SkPaint scaledPaint(skPaint);
894             scaledPaint.setTextSize(fScaledFallbackTextSize);
895             // remove maxScale from viewMatrix and move it into textRatio
896             // this keeps the base glyph size consistent regardless of matrix scale
897             SkMatrix modMatrix(fViewMatrix);
898             SkScalar invScale = SkScalarInvert(fMaxScale);
899             modMatrix.preScale(invScale, invScale);
900             textRatio = fTextSize * fMaxScale / fScaledFallbackTextSize;
901             cache = blob->setupCache(runIndex, props, scalerContextFlags, scaledPaint,
902                                      &modMatrix);
903         } else {
904             cache = blob->setupCache(runIndex, props, scalerContextFlags, paint,
905                                      &fViewMatrix);
906         }
907 
908         sk_sp<GrTextStrike> currStrike;
909         const char* text = fFallbackTxt.begin();
910         const char* stop = text + fFallbackTxt.count();
911         SkPoint* glyphPos = fFallbackPos.begin();
912         while (text < stop) {
913             const SkGlyph& glyph = glyphCacheProc(cache, &text);
914             GrAtlasTextContext::BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
915                                                glyphPos->fX, glyphPos->fY, textColor,
916                                                cache, textRatio);
917             glyphPos++;
918         }
919 
920         SkGlyphCache::AttachCache(cache);
921     }
922 }
923 
924 ///////////////////////////////////////////////////////////////////////////////////////////////////
925 
926 #if GR_TEST_UTILS
927 
928 #include "GrRenderTargetContext.h"
929 
GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp)930 GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
931     static uint32_t gContextID = SK_InvalidGenID;
932     static std::unique_ptr<GrAtlasTextContext> gTextContext;
933     static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
934 
935     if (context->uniqueID() != gContextID) {
936         gContextID = context->uniqueID();
937         gTextContext = GrAtlasTextContext::Make(GrAtlasTextContext::Options());
938     }
939 
940     // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
941     sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
942         SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
943 
944     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
945 
946     // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
947     // param.
948     SkPaint skPaint;
949     skPaint.setColor(random->nextU());
950     skPaint.setLCDRenderText(random->nextBool());
951     skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
952     skPaint.setSubpixelText(random->nextBool());
953     GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
954 
955     const char* text = "The quick brown fox jumps over the lazy dog.";
956     int textLen = (int)strlen(text);
957 
958     // create some random x/y offsets, including negative offsets
959     static const int kMaxTrans = 1024;
960     int xPos = (random->nextU() % 2) * 2 - 1;
961     int yPos = (random->nextU() % 2) * 2 - 1;
962     int xInt = (random->nextU() % kMaxTrans) * xPos;
963     int yInt = (random->nextU() % kMaxTrans) * yPos;
964     SkScalar x = SkIntToScalar(xInt);
965     SkScalar y = SkIntToScalar(yInt);
966 
967     auto glyphCache = context->contextPriv().getGlyphCache();
968     auto restrictedAtlasManager = context->contextPriv().getRestrictedAtlasManager();
969 
970     // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
971     // test the text op with this unit test, that is okay.
972     sk_sp<GrAtlasTextBlob> blob(gTextContext->makeDrawTextBlob(
973             context->contextPriv().getTextBlobCache(), glyphCache,
974             *context->caps()->shaderCaps(), utilsPaint,
975             GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, gSurfaceProps, text,
976             static_cast<size_t>(textLen), x, y));
977 
978     return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, gSurfaceProps,
979                              gTextContext->dfAdjustTable(), restrictedAtlasManager,
980                              rtc->textTarget());
981 }
982 
983 #endif
984