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