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