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