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