blob: a5685f03815dd576ac414b46bfe5d3ab4dce56da [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,
joshualitte76b4bb32015-12-28 07:23:58 -080042 const SkSurfaceProps& props, const SkPaint& skPaint,
brianosmana1e8f8d2016-04-08 06:47:54 -070043 GrColor color, uint32_t scalerContextFlags,
joshualitt29677982015-12-11 06:08:59 -080044 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
brianosmana1e8f8d2016-04-08 06:47:54 -070059 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, skPaint,
60 &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -080061 SkFindAndPlaceGlyph::ProcessText(
62 skPaint.getTextEncoding(), text, byteLength,
63 {x, y}, viewMatrix, skPaint.getTextAlign(),
64 cache,
65 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
66 position += rounding;
67 BmpAppendGlyph(
68 blob, runIndex, fontCache, &currStrike, glyph,
69 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
bsalomonc2878e22016-05-17 13:18:03 -070070 color, cache);
joshualitt29677982015-12-11 06:08:59 -080071 }
72 );
joshualitte76b4bb32015-12-28 07:23:58 -080073
74 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -080075}
76
77void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
78 GrBatchFontCache* fontCache,
joshualitte76b4bb32015-12-28 07:23:58 -080079 const SkSurfaceProps& props, const SkPaint& skPaint,
brianosmana1e8f8d2016-04-08 06:47:54 -070080 GrColor color, uint32_t scalerContextFlags,
joshualitt29677982015-12-11 06:08:59 -080081 const SkMatrix& viewMatrix,
82 const char text[], size_t byteLength,
83 const SkScalar pos[], int scalarsPerPosition,
84 const SkPoint& offset) {
85 SkASSERT(byteLength == 0 || text != nullptr);
86 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
87
88 // nothing to draw
89 if (text == nullptr || byteLength == 0) {
90 return;
91 }
92
joshualitta6bf4c52016-01-19 06:59:29 -080093 // Ensure the blob is set for bitmaptext
94 blob->setHasBitmap();
95
joshualitt29677982015-12-11 06:08:59 -080096 GrBatchTextStrike* currStrike = nullptr;
97
brianosmana1e8f8d2016-04-08 06:47:54 -070098 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, skPaint,
99 &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -0800100
101 SkFindAndPlaceGlyph::ProcessPosText(
102 skPaint.getTextEncoding(), text, byteLength,
103 offset, viewMatrix, pos, scalarsPerPosition,
104 skPaint.getTextAlign(), cache,
105 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
106 position += rounding;
107 BmpAppendGlyph(
108 blob, runIndex, fontCache, &currStrike, glyph,
109 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
bsalomonc2878e22016-05-17 13:18:03 -0700110 color, cache);
joshualitt29677982015-12-11 06:08:59 -0800111 }
112 );
joshualitte76b4bb32015-12-28 07:23:58 -0800113
114 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800115}
116
117void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
118 GrBatchFontCache* fontCache,
119 GrBatchTextStrike** strike, const SkGlyph& skGlyph,
bsalomonc2878e22016-05-17 13:18:03 -0700120 int vx, int vy, GrColor color, SkGlyphCache* cache) {
joshualitt29677982015-12-11 06:08:59 -0800121 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700122 *strike = fontCache->getStrike(cache);
joshualitt29677982015-12-11 06:08:59 -0800123 }
124
125 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
126 skGlyph.getSubXFixed(),
127 skGlyph.getSubYFixed(),
128 GrGlyph::kCoverage_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700129 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
joshualitt29677982015-12-11 06:08:59 -0800130 if (!glyph) {
131 return;
132 }
133
134 int x = vx + glyph->fBounds.fLeft;
135 int y = vy + glyph->fBounds.fTop;
136
137 // keep them as ints until we've done the clip-test
138 int width = glyph->fBounds.width();
139 int height = glyph->fBounds.height();
140
141 SkRect r;
142 r.fLeft = SkIntToScalar(x);
143 r.fTop = SkIntToScalar(y);
144 r.fRight = r.fLeft + SkIntToScalar(width);
145 r.fBottom = r.fTop + SkIntToScalar(height);
146
bsalomonc2878e22016-05-17 13:18:03 -0700147 blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph,
joshualitt29677982015-12-11 06:08:59 -0800148 SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, false);
149}
150
joshualitt0d2199b2016-01-20 06:36:09 -0800151bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
152 const SkSurfaceProps& props, const GrShaderCaps& caps) {
153 // TODO: support perspective (need getMaxScale replacement)
154 if (viewMatrix.hasPerspective()) {
155 return false;
156 }
157
158 SkScalar maxScale = viewMatrix.getMaxScale();
159 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
160 // Hinted text looks far better at small resolutions
161 // Scaling up beyond 2x yields undesireable artifacts
162 if (scaledTextSize < kMinDFFontSize ||
163 scaledTextSize > kLargeDFFontLimit) {
164 return false;
165 }
166
167 bool useDFT = props.isUseDeviceIndependentFonts();
168#if SK_FORCE_DISTANCE_FIELD_TEXT
169 useDFT = true;
170#endif
171
172 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
173 return false;
174 }
175
176 // rasterizers and mask filters modify alpha, which doesn't
177 // translate well to distance
178 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
179 return false;
180 }
181
182 // TODO: add some stroking support
183 if (skPaint.getStyle() != SkPaint::kFill_Style) {
184 return false;
185 }
186
187 return true;
188}
189
190void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
191 SkPaint* skPaint,
192 SkScalar* textRatio,
193 const SkMatrix& viewMatrix) {
194 // getMaxScale doesn't support perspective, so neither do we at the moment
195 SkASSERT(!viewMatrix.hasPerspective());
196 SkScalar maxScale = viewMatrix.getMaxScale();
197 SkScalar textSize = skPaint->getTextSize();
198 SkScalar scaledTextSize = textSize;
199 // if we have non-unity scale, we need to choose our base text size
200 // based on the SkPaint's text size multiplied by the max scale factor
201 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
202 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
203 scaledTextSize *= maxScale;
204 }
205
206 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
207 // and ceiling. A scale outside of this range would require regenerating the distance fields
208 SkScalar dfMaskScaleFloor;
209 SkScalar dfMaskScaleCeil;
210 if (scaledTextSize <= kSmallDFFontLimit) {
211 dfMaskScaleFloor = kMinDFFontSize;
212 dfMaskScaleCeil = kSmallDFFontLimit;
213 *textRatio = textSize / kSmallDFFontSize;
214 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
215 } else if (scaledTextSize <= kMediumDFFontLimit) {
216 dfMaskScaleFloor = kSmallDFFontLimit;
217 dfMaskScaleCeil = kMediumDFFontLimit;
218 *textRatio = textSize / kMediumDFFontSize;
219 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
220 } else {
221 dfMaskScaleFloor = kMediumDFFontLimit;
222 dfMaskScaleCeil = kLargeDFFontLimit;
223 *textRatio = textSize / kLargeDFFontSize;
224 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
225 }
226
227 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
228 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
229 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
230 // tolerate before we'd have to move to a large mip size. When we actually test these values
231 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
232 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
233 // level)
234 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt323c2eb2016-01-20 06:48:47 -0800235 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
joshualitt0d2199b2016-01-20 06:36:09 -0800236
237 skPaint->setLCDRenderText(false);
238 skPaint->setAutohinted(false);
239 skPaint->setHinting(SkPaint::kNormal_Hinting);
240 skPaint->setSubpixelText(true);
241}
242
243void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
244 GrBatchFontCache* fontCache, const SkSurfaceProps& props,
brianosmana1e8f8d2016-04-08 06:47:54 -0700245 const SkPaint& skPaint, GrColor color, uint32_t scalerContextFlags,
joshualitt0d2199b2016-01-20 06:36:09 -0800246 const SkMatrix& viewMatrix,
247 const char text[], size_t byteLength,
248 SkScalar x, SkScalar y) {
249 SkASSERT(byteLength == 0 || text != nullptr);
250
251 // nothing to draw
252 if (text == nullptr || byteLength == 0) {
253 return;
254 }
255
robertphillipse34f17d2016-07-19 07:59:22 -0700256 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
257 skPaint.isDevKernText(),
258 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800259 SkAutoDescriptor desc;
reeda9322c22016-04-12 06:47:05 -0700260 SkScalerContextEffects effects;
brianosman32f77822016-04-07 06:25:45 -0700261 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700262 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
reeda9322c22016-04-12 06:47:05 -0700263 skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
264 nullptr);
265 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects,
joshualitt0d2199b2016-01-20 06:36:09 -0800266 desc.getDesc());
267
268 SkTArray<SkScalar> positions;
269
270 const char* textPtr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700271 SkScalar stopX = 0;
272 SkScalar stopY = 0;
273 SkScalar origin = 0;
joshualitt0d2199b2016-01-20 06:36:09 -0800274 switch (skPaint.getTextAlign()) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700275 case SkPaint::kRight_Align: origin = SK_Scalar1; break;
276 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
joshualitt0d2199b2016-01-20 06:36:09 -0800277 case SkPaint::kLeft_Align: origin = 0; break;
278 }
279
280 SkAutoKern autokern;
281 const char* stop = text + byteLength;
282 while (textPtr < stop) {
283 // don't need x, y here, since all subpixel variants will have the
284 // same advance
benjaminwagnerd936f632016-02-23 10:44:31 -0800285 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
joshualitt0d2199b2016-01-20 06:36:09 -0800286
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700287 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
288 positions.push_back(stopX + origin * width);
joshualitt0d2199b2016-01-20 06:36:09 -0800289
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700290 SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
291 positions.push_back(stopY + origin * height);
joshualitt0d2199b2016-01-20 06:36:09 -0800292
293 stopX += width;
294 stopY += height;
295 }
296 SkASSERT(textPtr == stop);
297
298 SkGlyphCache::AttachCache(origPaintCache);
299
300 // now adjust starting point depending on alignment
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700301 SkScalar alignX = stopX;
302 SkScalar alignY = stopY;
joshualitt0d2199b2016-01-20 06:36:09 -0800303 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
304 alignX = SkScalarHalf(alignX);
305 alignY = SkScalarHalf(alignY);
306 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
307 alignX = 0;
308 alignY = 0;
309 }
310 x -= alignX;
311 y -= alignY;
312 SkPoint offset = SkPoint::Make(x, y);
313
brianosmana1e8f8d2016-04-08 06:47:54 -0700314 DrawDFPosText(blob, runIndex, fontCache, props, skPaint, color, scalerContextFlags, viewMatrix,
brianosman32f77822016-04-07 06:25:45 -0700315 text, byteLength, positions.begin(), 2, offset);
joshualitt0d2199b2016-01-20 06:36:09 -0800316}
317
318void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
319 GrBatchFontCache* fontCache, const SkSurfaceProps& props,
320 const SkPaint& origPaint,
brianosmana1e8f8d2016-04-08 06:47:54 -0700321 GrColor color, uint32_t scalerContextFlags,
brianosman32f77822016-04-07 06:25:45 -0700322 const SkMatrix& viewMatrix,
joshualitt0d2199b2016-01-20 06:36:09 -0800323 const char text[], size_t byteLength,
324 const SkScalar pos[], int scalarsPerPosition,
325 const SkPoint& offset) {
326 SkASSERT(byteLength == 0 || text != nullptr);
327 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
328
329 // nothing to draw
330 if (text == nullptr || byteLength == 0) {
331 return;
332 }
333
334 SkTDArray<char> fallbackTxt;
335 SkTDArray<SkScalar> fallbackPos;
336
337 // Setup distance field paint and text ratio
338 SkScalar textRatio;
339 SkPaint dfPaint(origPaint);
340 GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
341 blob->setHasDistanceField();
342 blob->setSubRunHasDistanceFields(runIndex, origPaint.isLCDRenderText());
343
344 GrBatchTextStrike* currStrike = nullptr;
345
brianosman32f77822016-04-07 06:25:45 -0700346 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700347 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
348 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -0800349 dfPaint, nullptr);
robertphillipse34f17d2016-07-19 07:59:22 -0700350 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(),
351 dfPaint.isDevKernText(),
352 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800353
354 const char* stop = text + byteLength;
355
356 if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
357 while (text < stop) {
358 const char* lastText = text;
359 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800360 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800361
362 if (glyph.fWidth) {
363 SkScalar x = offset.x() + pos[0];
364 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
365
366 if (!DfAppendGlyph(blob,
367 runIndex,
368 fontCache,
369 &currStrike,
370 glyph,
bsalomonc2878e22016-05-17 13:18:03 -0700371 x, y, color, cache,
joshualitt0d2199b2016-01-20 06:36:09 -0800372 textRatio, viewMatrix)) {
373 // couldn't append, send to fallback
374 fallbackTxt.append(SkToInt(text-lastText), lastText);
375 *fallbackPos.append() = pos[0];
376 if (2 == scalarsPerPosition) {
377 *fallbackPos.append() = pos[1];
378 }
379 }
380 }
381 pos += scalarsPerPosition;
382 }
383 } else {
384 SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
385 : SK_Scalar1;
386 while (text < stop) {
387 const char* lastText = text;
388 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800389 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800390
391 if (glyph.fWidth) {
392 SkScalar x = offset.x() + pos[0];
393 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
394
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700395 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
396 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
joshualitt0d2199b2016-01-20 06:36:09 -0800397
398 if (!DfAppendGlyph(blob,
399 runIndex,
400 fontCache,
401 &currStrike,
402 glyph,
403 x - advanceX, y - advanceY, color,
bsalomonc2878e22016-05-17 13:18:03 -0700404 cache,
joshualitt0d2199b2016-01-20 06:36:09 -0800405 textRatio,
406 viewMatrix)) {
407 // couldn't append, send to fallback
408 fallbackTxt.append(SkToInt(text-lastText), lastText);
409 *fallbackPos.append() = pos[0];
410 if (2 == scalarsPerPosition) {
411 *fallbackPos.append() = pos[1];
412 }
413 }
414 }
415 pos += scalarsPerPosition;
416 }
417 }
418
419 SkGlyphCache::AttachCache(cache);
420 if (fallbackTxt.count()) {
421 blob->initOverride(runIndex);
422 GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props,
brianosmana1e8f8d2016-04-08 06:47:54 -0700423 origPaint, origPaint.getColor(), scalerContextFlags, viewMatrix,
joshualitt0d2199b2016-01-20 06:36:09 -0800424 fallbackTxt.begin(), fallbackTxt.count(),
425 fallbackPos.begin(), scalarsPerPosition, offset);
426 }
427}
428
429bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* cache,
430 GrBatchTextStrike** strike, const SkGlyph& skGlyph,
431 SkScalar sx, SkScalar sy, GrColor color,
bsalomonc2878e22016-05-17 13:18:03 -0700432 SkGlyphCache* glyphCache,
joshualitt0d2199b2016-01-20 06:36:09 -0800433 SkScalar textRatio, const SkMatrix& viewMatrix) {
434 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700435 *strike = cache->getStrike(glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800436 }
437
438 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
439 skGlyph.getSubXFixed(),
440 skGlyph.getSubYFixed(),
441 GrGlyph::kDistance_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700442 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800443 if (!glyph) {
444 return true;
445 }
446
447 // fallback to color glyph support
448 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
449 return false;
450 }
451
452 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
453 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
454 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
455 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
456
457 SkScalar scale = textRatio;
458 dx *= scale;
459 dy *= scale;
460 width *= scale;
461 height *= scale;
462 sx += dx;
463 sy += dy;
464 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
465
bsalomonc2878e22016-05-17 13:18:03 -0700466 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph,
joshualitt0d2199b2016-01-20 06:36:09 -0800467 sx - dx, sy - dy, scale, true);
468 return true;
469}
470
joshualitt0a42e682015-12-10 13:20:58 -0800471void GrTextUtils::DrawTextAsPath(GrContext* context, GrDrawContext* dc,
472 const GrClip& clip,
473 const SkPaint& skPaint, const SkMatrix& viewMatrix,
474 const char text[], size_t byteLength, SkScalar x, SkScalar y,
475 const SkIRect& clipBounds) {
476 SkTextToPathIter iter(text, byteLength, skPaint, true);
477
478 SkMatrix matrix;
479 matrix.setScale(iter.getPathScale(), iter.getPathScale());
480 matrix.postTranslate(x, y);
481
482 const SkPath* iterPath;
483 SkScalar xpos, prevXPos = 0;
484
485 while (iter.next(&iterPath, &xpos)) {
486 matrix.postTranslate(xpos - prevXPos, 0);
487 if (iterPath) {
488 const SkPaint& pnt = iter.getPaint();
489 GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, *iterPath,
490 pnt, viewMatrix, &matrix, clipBounds, false);
491 }
492 prevXPos = xpos;
493 }
494}
495
496void GrTextUtils::DrawPosTextAsPath(GrContext* context,
497 GrDrawContext* dc,
498 const SkSurfaceProps& props,
499 const GrClip& clip,
500 const SkPaint& origPaint, const SkMatrix& viewMatrix,
501 const char text[], size_t byteLength,
502 const SkScalar pos[], int scalarsPerPosition,
503 const SkPoint& offset, const SkIRect& clipBounds) {
504 // setup our std paint, in hopes of getting hits in the cache
505 SkPaint paint(origPaint);
506 SkScalar matrixScale = paint.setupForAsPaths();
507
508 SkMatrix matrix;
509 matrix.setScale(matrixScale, matrixScale);
510
511 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
512 paint.setStyle(SkPaint::kFill_Style);
513 paint.setPathEffect(nullptr);
514
robertphillipse34f17d2016-07-19 07:59:22 -0700515 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
516 paint.isDevKernText(),
517 true);
benjaminwagnerd936f632016-02-23 10:44:31 -0800518 SkAutoGlyphCache autoCache(paint, &props, nullptr);
519 SkGlyphCache* cache = autoCache.getCache();
joshualitt0a42e682015-12-10 13:20:58 -0800520
521 const char* stop = text + byteLength;
522 SkTextAlignProc alignProc(paint.getTextAlign());
523 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
524
525 // Now restore the original settings, so we "draw" with whatever style/stroking.
526 paint.setStyle(origPaint.getStyle());
reeda4393342016-03-18 11:22:57 -0700527 paint.setPathEffect(sk_ref_sp(origPaint.getPathEffect()));
joshualitt0a42e682015-12-10 13:20:58 -0800528
529 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800530 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0a42e682015-12-10 13:20:58 -0800531 if (glyph.fWidth) {
532 const SkPath* path = cache->findPath(glyph);
533 if (path) {
534 SkPoint tmsLoc;
535 tmsProc(pos, &tmsLoc);
536 SkPoint loc;
537 alignProc(tmsLoc, glyph, &loc);
538
539 matrix[SkMatrix::kMTransX] = loc.fX;
540 matrix[SkMatrix::kMTransY] = loc.fY;
541 GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, *path, paint,
542 viewMatrix, &matrix, clipBounds, false);
543 }
544 }
545 pos += scalarsPerPosition;
546 }
547}
joshualitt8e84a1e2016-02-16 11:09:25 -0800548
549bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
reed374772b2016-10-05 17:33:02 -0700550 return paint.getMaskFilter() ||
joshualitt8e84a1e2016-02-16 11:09:25 -0800551 paint.getRasterizer() ||
552 paint.getPathEffect() ||
553 paint.isFakeBoldText() ||
554 paint.getStyle() != SkPaint::kFill_Style;
555}
556
557uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
558 uint32_t flags = paint.getFlags();
559
560 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
561 return flags;
562 }
563
564 if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
565 flags &= ~SkPaint::kLCDRenderText_Flag;
566 flags |= SkPaint::kGenA8FromLCD_Flag;
567 }
568
569 return flags;
570}