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