blob: 0b26c84168624931c42ee4f8b907df4fb519e972 [file] [log] [blame]
joshualitt0a42e682015-12-10 13:20:58 -08001/*
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
joshualitt29677982015-12-11 06:08:59 -080010#include "GrAtlasTextBlob.h"
11#include "GrBatchFontCache.h"
joshualitt0a42e682015-12-10 13:20:58 -080012#include "GrBlurUtils.h"
joshualitt0d2199b2016-01-20 06:36:09 -080013#include "GrCaps.h"
joshualitt0a42e682015-12-10 13:20:58 -080014#include "GrContext.h"
15#include "GrDrawContext.h"
joshualitt0d2199b2016-01-20 06:36:09 -080016
17#include "SkDistanceFieldGen.h"
joshualitt0a42e682015-12-10 13:20:58 -080018#include "SkDrawProcs.h"
joshualitt29677982015-12-11 06:08:59 -080019#include "SkFindAndPlaceGlyph.h"
joshualitt0a42e682015-12-10 13:20:58 -080020#include "SkGlyphCache.h"
21#include "SkPaint.h"
22#include "SkRect.h"
23#include "SkTextMapStateProc.h"
24#include "SkTextToPathIter.h"
25
joshualitt0d2199b2016-01-20 06:36:09 -080026namespace {
27static const int kMinDFFontSize = 18;
28static const int kSmallDFFontSize = 32;
29static const int kSmallDFFontLimit = 32;
30static const int kMediumDFFontSize = 72;
31static const int kMediumDFFontLimit = 72;
32static const int kLargeDFFontSize = 162;
33#ifdef SK_BUILD_FOR_ANDROID
34static const int kLargeDFFontLimit = 384;
35#else
36static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
37#endif
38};
39
joshualitt29677982015-12-11 06:08:59 -080040void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
41 GrBatchFontCache* fontCache,
joshualitte76b4bb2015-12-28 07:23:58 -080042 const SkSurfaceProps& props, const SkPaint& skPaint,
joshualitt29677982015-12-11 06:08:59 -080043 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
joshualitta6bf4c52016-01-19 06:59:29 -080054 // Ensure the blob is set for bitmaptext
55 blob->setHasBitmap();
56
joshualitt29677982015-12-11 06:08:59 -080057 GrBatchTextStrike* currStrike = nullptr;
58
59 // Get GrFontScaler from cache
bungemanf6d1e602016-02-22 13:20:28 -080060 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::On,
61 skPaint, &viewMatrix);
joshualitt8e84a1e2016-02-16 11:09:25 -080062 GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache);
joshualitt29677982015-12-11 06:08:59 -080063
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 );
joshualitte76b4bb2015-12-28 07:23:58 -080076
77 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -080078}
79
80void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
81 GrBatchFontCache* fontCache,
joshualitte76b4bb2015-12-28 07:23:58 -080082 const SkSurfaceProps& props, const SkPaint& skPaint,
joshualitt29677982015-12-11 06:08:59 -080083 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
joshualitta6bf4c52016-01-19 06:59:29 -080096 // Ensure the blob is set for bitmaptext
97 blob->setHasBitmap();
98
joshualitt29677982015-12-11 06:08:59 -080099 GrBatchTextStrike* currStrike = nullptr;
100
101 // Get GrFontScaler from cache
bungemanf6d1e602016-02-22 13:20:28 -0800102 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::On,
103 skPaint, &viewMatrix);
joshualitt8e84a1e2016-02-16 11:09:25 -0800104 GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache);
joshualitt29677982015-12-11 06:08:59 -0800105
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 );
joshualitte76b4bb2015-12-28 07:23:58 -0800118
119 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800120}
121
122void 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
joshualitt0d2199b2016-01-20 06:36:09 -0800156bool 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
195void 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);
joshualitt323c2eb2016-01-20 06:48:47 -0800240 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
joshualitt0d2199b2016-01-20 06:36:09 -0800241
242 skPaint->setLCDRenderText(false);
243 skPaint->setAutohinted(false);
244 skPaint->setHinting(SkPaint::kNormal_Hinting);
245 skPaint->setSubpixelText(true);
246}
247
248void 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
benjaminwagnerd936f632016-02-23 10:44:31 -0800261 SkPaint::GlyphCacheProc glyphCacheProc = skPaint.getGlyphCacheProc(true);
joshualitt0d2199b2016-01-20 06:36:09 -0800262 SkAutoDescriptor desc;
bungemanf6d1e602016-02-22 13:20:28 -0800263 skPaint.getScalerContextDescriptor(&desc, props, SkPaint::FakeGamma::Off, nullptr);
joshualitt0d2199b2016-01-20 06:36:09 -0800264 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(),
265 desc.getDesc());
266
267 SkTArray<SkScalar> positions;
268
269 const char* textPtr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700270 SkScalar stopX = 0;
271 SkScalar stopY = 0;
272 SkScalar origin = 0;
joshualitt0d2199b2016-01-20 06:36:09 -0800273 switch (skPaint.getTextAlign()) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700274 case SkPaint::kRight_Align: origin = SK_Scalar1; break;
275 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
joshualitt0d2199b2016-01-20 06:36:09 -0800276 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
benjaminwagnerd936f632016-02-23 10:44:31 -0800284 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
joshualitt0d2199b2016-01-20 06:36:09 -0800285
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700286 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
287 positions.push_back(stopX + origin * width);
joshualitt0d2199b2016-01-20 06:36:09 -0800288
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700289 SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
290 positions.push_back(stopY + origin * height);
joshualitt0d2199b2016-01-20 06:36:09 -0800291
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
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700300 SkScalar alignX = stopX;
301 SkScalar alignY = stopY;
joshualitt0d2199b2016-01-20 06:36:09 -0800302 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
317void 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
bungemanf6d1e602016-02-22 13:20:28 -0800344 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::Off,
345 dfPaint, nullptr);
benjaminwagnerd936f632016-02-23 10:44:31 -0800346 SkPaint::GlyphCacheProc glyphCacheProc = dfPaint.getGlyphCacheProc(true);
joshualitt8e84a1e2016-02-16 11:09:25 -0800347 GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache);
joshualitt0d2199b2016-01-20 06:36:09 -0800348
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
benjaminwagnerd936f632016-02-23 10:44:31 -0800355 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800356
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
benjaminwagnerd936f632016-02-23 10:44:31 -0800384 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800385
386 if (glyph.fWidth) {
387 SkScalar x = offset.x() + pos[0];
388 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
389
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700390 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
391 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
joshualitt0d2199b2016-01-20 06:36:09 -0800392
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
424bool 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
joshualitt0a42e682015-12-10 13:20:58 -0800466void 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
491void 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
benjaminwagnerd936f632016-02-23 10:44:31 -0800510 SkPaint::GlyphCacheProc glyphCacheProc = paint.getGlyphCacheProc(true);
511 SkAutoGlyphCache autoCache(paint, &props, nullptr);
512 SkGlyphCache* cache = autoCache.getCache();
joshualitt0a42e682015-12-10 13:20:58 -0800513
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());
reeda4393342016-03-18 11:22:57 -0700520 paint.setPathEffect(sk_ref_sp(origPaint.getPathEffect()));
joshualitt0a42e682015-12-10 13:20:58 -0800521
522 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800523 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0a42e682015-12-10 13:20:58 -0800524 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}
joshualitt8e84a1e2016-02-16 11:09:25 -0800541
542bool 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
551uint32_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
566static void glyph_cache_aux_proc(void* data) {
567 GrFontScaler* scaler = (GrFontScaler*)data;
568 SkSafeUnref(scaler);
569}
570
571GrFontScaler* 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}