blob: a199d8c92d0f75af0c6d5ded39a9b7c31e5939c1 [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"
Brian Salomonf856fd12016-12-16 14:24:34 -05009#include "GrAtlasGlyphCache.h"
joshualitt29677982015-12-11 06:08:59 -080010#include "GrAtlasTextBlob.h"
joshualitt0a42e682015-12-10 13:20:58 -080011#include "GrBlurUtils.h"
joshualitt0d2199b2016-01-20 06:36:09 -080012#include "GrCaps.h"
joshualitt0a42e682015-12-10 13:20:58 -080013#include "GrContext.h"
Brian Osman11052242016-10-27 14:47:55 -040014#include "GrRenderTargetContext.h"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050015#include "GrSurfaceContextPriv.h"
joshualitt0d2199b2016-01-20 06:36:09 -080016#include "SkDistanceFieldGen.h"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050017#include "SkDrawFilter.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"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050021#include "SkGrPriv.h"
joshualitt0a42e682015-12-10 13:20:58 -080022#include "SkPaint.h"
23#include "SkRect.h"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050024#include "SkTextBlobRunIterator.h"
joshualitt0a42e682015-12-10 13:20:58 -080025#include "SkTextMapStateProc.h"
26#include "SkTextToPathIter.h"
27
joshualitt0d2199b2016-01-20 06:36:09 -080028namespace {
29static const int kMinDFFontSize = 18;
30static const int kSmallDFFontSize = 32;
31static const int kSmallDFFontLimit = 32;
32static const int kMediumDFFontSize = 72;
33static const int kMediumDFFontLimit = 72;
34static const int kLargeDFFontSize = 162;
35#ifdef SK_BUILD_FOR_ANDROID
36static const int kLargeDFFontLimit = 384;
37#else
38static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
39#endif
40};
41
Brian Salomon6f1d36c2017-01-13 12:02:17 -050042bool GrTextUtils::Paint::toGrPaint(GrMaskFormat maskFormat, GrRenderTargetContext* rtc,
43 const SkMatrix& viewMatrix, GrPaint* grPaint) const {
44 GrContext* context = rtc->surfPriv().getContext();
45 if (kARGB_GrMaskFormat == maskFormat) {
46 return SkPaintToGrPaintWithPrimitiveColor(context, rtc, this->skPaint(), grPaint);
47 } else {
48 return SkPaintToGrPaint(context, rtc, this->skPaint(), viewMatrix, grPaint);
49 }
50}
51
52bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
53 if (!fModifiedPaint.isValid()) {
54 fModifiedPaint.init(fOriginalPaint->skPaint());
55 fPaint = fModifiedPaint.get();
56 } else if (fFilter) {
57 // We have to reset before applying the run because the filter could have arbitrary
58 // changed the paint.
59 *fModifiedPaint.get() = fOriginalPaint->skPaint();
60 }
61 run.applyFontToPaint(fModifiedPaint.get());
62
63 if (fFilter) {
64 if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) {
65 // A false return from filter() means we should abort the current draw.
66 return false;
67 }
68 // The draw filter could have changed either the paint color or color filter.
69 this->initFilteredColor();
70 }
71 fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get()));
72 return true;
73}
74
75void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
76 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
77 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
78 const char text[], size_t byteLength, SkScalar x, SkScalar y) {
joshualitt29677982015-12-11 06:08:59 -080079 SkASSERT(byteLength == 0 || text != nullptr);
80
81 // nothing to draw
82 if (text == nullptr || byteLength == 0) {
83 return;
84 }
85
joshualitta6bf4c52016-01-19 06:59:29 -080086 // Ensure the blob is set for bitmaptext
87 blob->setHasBitmap();
88
Brian Salomonf856fd12016-12-16 14:24:34 -050089 GrAtlasTextStrike* currStrike = nullptr;
joshualitt29677982015-12-11 06:08:59 -080090
Brian Salomon6f1d36c2017-01-13 12:02:17 -050091 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -080092 SkFindAndPlaceGlyph::ProcessText(
Brian Salomon6f1d36c2017-01-13 12:02:17 -050093 paint.skPaint().getTextEncoding(), text, byteLength,
94 {x, y}, viewMatrix, paint.skPaint().getTextAlign(),
joshualitt29677982015-12-11 06:08:59 -080095 cache,
96 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -050097 position += rounding;
98 BmpAppendGlyph(
99 blob, runIndex, fontCache, &currStrike, glyph,
100 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
101 paint.filteredPremulGrColor(), cache);
joshualitt29677982015-12-11 06:08:59 -0800102 }
103 );
joshualitte76b4bb32015-12-28 07:23:58 -0800104
105 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800106}
107
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500108void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
109 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
110 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
111 const char text[], size_t byteLength, const SkScalar pos[],
112 int scalarsPerPosition, const SkPoint& offset) {
joshualitt29677982015-12-11 06:08:59 -0800113 SkASSERT(byteLength == 0 || text != nullptr);
114 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
115
116 // nothing to draw
117 if (text == nullptr || byteLength == 0) {
118 return;
119 }
120
joshualitta6bf4c52016-01-19 06:59:29 -0800121 // Ensure the blob is set for bitmaptext
122 blob->setHasBitmap();
123
Brian Salomonf856fd12016-12-16 14:24:34 -0500124 GrAtlasTextStrike* currStrike = nullptr;
joshualitt29677982015-12-11 06:08:59 -0800125
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500126 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -0800127
128 SkFindAndPlaceGlyph::ProcessPosText(
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500129 paint.skPaint().getTextEncoding(), text, byteLength,
joshualitt29677982015-12-11 06:08:59 -0800130 offset, viewMatrix, pos, scalarsPerPosition,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500131 paint.skPaint().getTextAlign(), cache,
joshualitt29677982015-12-11 06:08:59 -0800132 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
133 position += rounding;
134 BmpAppendGlyph(
135 blob, runIndex, fontCache, &currStrike, glyph,
136 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500137 paint.filteredPremulGrColor(), cache);
joshualitt29677982015-12-11 06:08:59 -0800138 }
139 );
joshualitte76b4bb32015-12-28 07:23:58 -0800140
141 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800142}
143
144void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
Brian Salomonf856fd12016-12-16 14:24:34 -0500145 GrAtlasGlyphCache* fontCache,
146 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
bsalomonc2878e22016-05-17 13:18:03 -0700147 int vx, int vy, GrColor color, SkGlyphCache* cache) {
joshualitt29677982015-12-11 06:08:59 -0800148 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700149 *strike = fontCache->getStrike(cache);
joshualitt29677982015-12-11 06:08:59 -0800150 }
151
152 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
153 skGlyph.getSubXFixed(),
154 skGlyph.getSubYFixed(),
155 GrGlyph::kCoverage_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700156 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
joshualitt29677982015-12-11 06:08:59 -0800157 if (!glyph) {
158 return;
159 }
160
161 int x = vx + glyph->fBounds.fLeft;
162 int y = vy + glyph->fBounds.fTop;
163
164 // keep them as ints until we've done the clip-test
165 int width = glyph->fBounds.width();
166 int height = glyph->fBounds.height();
167
168 SkRect r;
169 r.fLeft = SkIntToScalar(x);
170 r.fTop = SkIntToScalar(y);
171 r.fRight = r.fLeft + SkIntToScalar(width);
172 r.fBottom = r.fTop + SkIntToScalar(height);
173
bsalomonc2878e22016-05-17 13:18:03 -0700174 blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph,
Jim Van Verth08576e62016-11-16 10:15:23 -0500175 SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, true);
joshualitt29677982015-12-11 06:08:59 -0800176}
177
joshualitt0d2199b2016-01-20 06:36:09 -0800178bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
179 const SkSurfaceProps& props, const GrShaderCaps& caps) {
180 // TODO: support perspective (need getMaxScale replacement)
181 if (viewMatrix.hasPerspective()) {
182 return false;
183 }
184
185 SkScalar maxScale = viewMatrix.getMaxScale();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500186 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
joshualitt0d2199b2016-01-20 06:36:09 -0800187 // Hinted text looks far better at small resolutions
188 // Scaling up beyond 2x yields undesireable artifacts
189 if (scaledTextSize < kMinDFFontSize ||
190 scaledTextSize > kLargeDFFontLimit) {
191 return false;
192 }
193
194 bool useDFT = props.isUseDeviceIndependentFonts();
195#if SK_FORCE_DISTANCE_FIELD_TEXT
196 useDFT = true;
197#endif
198
199 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
200 return false;
201 }
202
203 // rasterizers and mask filters modify alpha, which doesn't
204 // translate well to distance
205 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
206 return false;
207 }
208
209 // TODO: add some stroking support
210 if (skPaint.getStyle() != SkPaint::kFill_Style) {
211 return false;
212 }
213
214 return true;
215}
216
217void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
218 SkPaint* skPaint,
219 SkScalar* textRatio,
220 const SkMatrix& viewMatrix) {
221 // getMaxScale doesn't support perspective, so neither do we at the moment
222 SkASSERT(!viewMatrix.hasPerspective());
223 SkScalar maxScale = viewMatrix.getMaxScale();
224 SkScalar textSize = skPaint->getTextSize();
225 SkScalar scaledTextSize = textSize;
226 // if we have non-unity scale, we need to choose our base text size
227 // based on the SkPaint's text size multiplied by the max scale factor
228 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
229 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
230 scaledTextSize *= maxScale;
231 }
232
233 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
234 // and ceiling. A scale outside of this range would require regenerating the distance fields
235 SkScalar dfMaskScaleFloor;
236 SkScalar dfMaskScaleCeil;
237 if (scaledTextSize <= kSmallDFFontLimit) {
238 dfMaskScaleFloor = kMinDFFontSize;
239 dfMaskScaleCeil = kSmallDFFontLimit;
240 *textRatio = textSize / kSmallDFFontSize;
241 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
242 } else if (scaledTextSize <= kMediumDFFontLimit) {
243 dfMaskScaleFloor = kSmallDFFontLimit;
244 dfMaskScaleCeil = kMediumDFFontLimit;
245 *textRatio = textSize / kMediumDFFontSize;
246 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
247 } else {
248 dfMaskScaleFloor = kMediumDFFontLimit;
249 dfMaskScaleCeil = kLargeDFFontLimit;
250 *textRatio = textSize / kLargeDFFontSize;
251 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
252 }
253
254 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
255 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
256 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
257 // tolerate before we'd have to move to a large mip size. When we actually test these values
258 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
259 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
260 // level)
261 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt323c2eb2016-01-20 06:48:47 -0800262 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
joshualitt0d2199b2016-01-20 06:36:09 -0800263
264 skPaint->setLCDRenderText(false);
265 skPaint->setAutohinted(false);
266 skPaint->setHinting(SkPaint::kNormal_Hinting);
267 skPaint->setSubpixelText(true);
268}
269
270void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
Brian Salomonf856fd12016-12-16 14:24:34 -0500271 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500272 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
joshualitt0d2199b2016-01-20 06:36:09 -0800273 const SkMatrix& viewMatrix,
274 const char text[], size_t byteLength,
275 SkScalar x, SkScalar y) {
276 SkASSERT(byteLength == 0 || text != nullptr);
277
278 // nothing to draw
279 if (text == nullptr || byteLength == 0) {
280 return;
281 }
282
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500283 const SkPaint& skPaint = paint.skPaint();
robertphillipse34f17d2016-07-19 07:59:22 -0700284 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
285 skPaint.isDevKernText(),
286 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800287 SkAutoDescriptor desc;
reeda9322c22016-04-12 06:47:05 -0700288 SkScalerContextEffects effects;
brianosman32f77822016-04-07 06:25:45 -0700289 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700290 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
reeda9322c22016-04-12 06:47:05 -0700291 skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
292 nullptr);
293 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects,
joshualitt0d2199b2016-01-20 06:36:09 -0800294 desc.getDesc());
295
296 SkTArray<SkScalar> positions;
297
298 const char* textPtr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700299 SkScalar stopX = 0;
300 SkScalar stopY = 0;
301 SkScalar origin = 0;
joshualitt0d2199b2016-01-20 06:36:09 -0800302 switch (skPaint.getTextAlign()) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700303 case SkPaint::kRight_Align: origin = SK_Scalar1; break;
304 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
joshualitt0d2199b2016-01-20 06:36:09 -0800305 case SkPaint::kLeft_Align: origin = 0; break;
306 }
307
308 SkAutoKern autokern;
309 const char* stop = text + byteLength;
310 while (textPtr < stop) {
311 // don't need x, y here, since all subpixel variants will have the
312 // same advance
benjaminwagnerd936f632016-02-23 10:44:31 -0800313 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
joshualitt0d2199b2016-01-20 06:36:09 -0800314
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700315 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
316 positions.push_back(stopX + origin * width);
joshualitt0d2199b2016-01-20 06:36:09 -0800317
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700318 SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
319 positions.push_back(stopY + origin * height);
joshualitt0d2199b2016-01-20 06:36:09 -0800320
321 stopX += width;
322 stopY += height;
323 }
324 SkASSERT(textPtr == stop);
325
326 SkGlyphCache::AttachCache(origPaintCache);
327
328 // now adjust starting point depending on alignment
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700329 SkScalar alignX = stopX;
330 SkScalar alignY = stopY;
joshualitt0d2199b2016-01-20 06:36:09 -0800331 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
332 alignX = SkScalarHalf(alignX);
333 alignY = SkScalarHalf(alignY);
334 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
335 alignX = 0;
336 alignY = 0;
337 }
338 x -= alignX;
339 y -= alignY;
340 SkPoint offset = SkPoint::Make(x, y);
341
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500342 DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
343 byteLength, positions.begin(), 2, offset);
joshualitt0d2199b2016-01-20 06:36:09 -0800344}
345
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500346void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
347 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
348 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
349 const char text[], size_t byteLength, const SkScalar pos[],
350 int scalarsPerPosition, const SkPoint& offset) {
joshualitt0d2199b2016-01-20 06:36:09 -0800351 SkASSERT(byteLength == 0 || text != nullptr);
352 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
353
354 // nothing to draw
355 if (text == nullptr || byteLength == 0) {
356 return;
357 }
358
359 SkTDArray<char> fallbackTxt;
360 SkTDArray<SkScalar> fallbackPos;
361
362 // Setup distance field paint and text ratio
363 SkScalar textRatio;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500364 SkPaint dfPaint(paint);
joshualitt0d2199b2016-01-20 06:36:09 -0800365 GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
366 blob->setHasDistanceField();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500367 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText());
joshualitt0d2199b2016-01-20 06:36:09 -0800368
Brian Salomonf856fd12016-12-16 14:24:34 -0500369 GrAtlasTextStrike* currStrike = nullptr;
joshualitt0d2199b2016-01-20 06:36:09 -0800370
brianosman32f77822016-04-07 06:25:45 -0700371 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700372 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
373 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -0800374 dfPaint, nullptr);
robertphillipse34f17d2016-07-19 07:59:22 -0700375 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(),
376 dfPaint.isDevKernText(),
377 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800378
379 const char* stop = text + byteLength;
380
381 if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
382 while (text < stop) {
383 const char* lastText = text;
384 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800385 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800386
387 if (glyph.fWidth) {
388 SkScalar x = offset.x() + pos[0];
389 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
390
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500391 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
392 paint.filteredPremulGrColor(), cache, textRatio, viewMatrix)) {
joshualitt0d2199b2016-01-20 06:36:09 -0800393 // couldn't append, send to fallback
394 fallbackTxt.append(SkToInt(text-lastText), lastText);
395 *fallbackPos.append() = pos[0];
396 if (2 == scalarsPerPosition) {
397 *fallbackPos.append() = pos[1];
398 }
399 }
400 }
401 pos += scalarsPerPosition;
402 }
403 } else {
404 SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
405 : SK_Scalar1;
406 while (text < stop) {
407 const char* lastText = text;
408 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800409 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800410
411 if (glyph.fWidth) {
412 SkScalar x = offset.x() + pos[0];
413 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
414
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700415 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
416 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
joshualitt0d2199b2016-01-20 06:36:09 -0800417
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500418 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
419 y - advanceY, paint.filteredPremulGrColor(), cache, textRatio,
joshualitt0d2199b2016-01-20 06:36:09 -0800420 viewMatrix)) {
421 // couldn't append, send to fallback
422 fallbackTxt.append(SkToInt(text-lastText), lastText);
423 *fallbackPos.append() = pos[0];
424 if (2 == scalarsPerPosition) {
425 *fallbackPos.append() = pos[1];
426 }
427 }
428 }
429 pos += scalarsPerPosition;
430 }
431 }
432
433 SkGlyphCache::AttachCache(cache);
434 if (fallbackTxt.count()) {
435 blob->initOverride(runIndex);
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500436 GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags,
437 viewMatrix, fallbackTxt.begin(), fallbackTxt.count(),
joshualitt0d2199b2016-01-20 06:36:09 -0800438 fallbackPos.begin(), scalarsPerPosition, offset);
439 }
440}
441
Brian Salomonf856fd12016-12-16 14:24:34 -0500442bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* cache,
443 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
joshualitt0d2199b2016-01-20 06:36:09 -0800444 SkScalar sx, SkScalar sy, GrColor color,
bsalomonc2878e22016-05-17 13:18:03 -0700445 SkGlyphCache* glyphCache,
joshualitt0d2199b2016-01-20 06:36:09 -0800446 SkScalar textRatio, const SkMatrix& viewMatrix) {
447 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700448 *strike = cache->getStrike(glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800449 }
450
451 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
452 skGlyph.getSubXFixed(),
453 skGlyph.getSubYFixed(),
454 GrGlyph::kDistance_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700455 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800456 if (!glyph) {
457 return true;
458 }
459
460 // fallback to color glyph support
461 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
462 return false;
463 }
464
465 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
466 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
467 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
468 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
469
470 SkScalar scale = textRatio;
471 dx *= scale;
472 dy *= scale;
473 width *= scale;
474 height *= scale;
475 sx += dx;
476 sy += dy;
477 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
478
bsalomonc2878e22016-05-17 13:18:03 -0700479 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph,
Jim Van Verth08576e62016-11-16 10:15:23 -0500480 sx - dx, sy - dy, scale, false);
joshualitt0d2199b2016-01-20 06:36:09 -0800481 return true;
482}
483
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500484void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip,
485 const SkPaint& paint, const SkMatrix& viewMatrix,
joshualitt0a42e682015-12-10 13:20:58 -0800486 const char text[], size_t byteLength, SkScalar x, SkScalar y,
487 const SkIRect& clipBounds) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500488 SkTextToPathIter iter(text, byteLength, paint, true);
joshualitt0a42e682015-12-10 13:20:58 -0800489
490 SkMatrix matrix;
491 matrix.setScale(iter.getPathScale(), iter.getPathScale());
492 matrix.postTranslate(x, y);
493
494 const SkPath* iterPath;
495 SkScalar xpos, prevXPos = 0;
496
497 while (iter.next(&iterPath, &xpos)) {
498 matrix.postTranslate(xpos - prevXPos, 0);
499 if (iterPath) {
500 const SkPaint& pnt = iter.getPaint();
Brian Osman11052242016-10-27 14:47:55 -0400501 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *iterPath,
joshualitt0a42e682015-12-10 13:20:58 -0800502 pnt, viewMatrix, &matrix, clipBounds, false);
503 }
504 prevXPos = xpos;
505 }
506}
507
508void GrTextUtils::DrawPosTextAsPath(GrContext* context,
Brian Osman11052242016-10-27 14:47:55 -0400509 GrRenderTargetContext* rtc,
joshualitt0a42e682015-12-10 13:20:58 -0800510 const SkSurfaceProps& props,
511 const GrClip& clip,
512 const SkPaint& origPaint, const SkMatrix& viewMatrix,
513 const char text[], size_t byteLength,
514 const SkScalar pos[], int scalarsPerPosition,
515 const SkPoint& offset, const SkIRect& clipBounds) {
516 // setup our std paint, in hopes of getting hits in the cache
517 SkPaint paint(origPaint);
518 SkScalar matrixScale = paint.setupForAsPaths();
519
520 SkMatrix matrix;
521 matrix.setScale(matrixScale, matrixScale);
522
523 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
524 paint.setStyle(SkPaint::kFill_Style);
525 paint.setPathEffect(nullptr);
526
robertphillipse34f17d2016-07-19 07:59:22 -0700527 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
528 paint.isDevKernText(),
529 true);
benjaminwagnerd936f632016-02-23 10:44:31 -0800530 SkAutoGlyphCache autoCache(paint, &props, nullptr);
531 SkGlyphCache* cache = autoCache.getCache();
joshualitt0a42e682015-12-10 13:20:58 -0800532
533 const char* stop = text + byteLength;
534 SkTextAlignProc alignProc(paint.getTextAlign());
535 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
536
537 // Now restore the original settings, so we "draw" with whatever style/stroking.
538 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -0500539 paint.setPathEffect(origPaint.refPathEffect());
joshualitt0a42e682015-12-10 13:20:58 -0800540
541 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800542 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0a42e682015-12-10 13:20:58 -0800543 if (glyph.fWidth) {
544 const SkPath* path = cache->findPath(glyph);
545 if (path) {
546 SkPoint tmsLoc;
547 tmsProc(pos, &tmsLoc);
548 SkPoint loc;
549 alignProc(tmsLoc, glyph, &loc);
550
551 matrix[SkMatrix::kMTransX] = loc.fX;
552 matrix[SkMatrix::kMTransY] = loc.fY;
Brian Osman11052242016-10-27 14:47:55 -0400553 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *path, paint,
joshualitt0a42e682015-12-10 13:20:58 -0800554 viewMatrix, &matrix, clipBounds, false);
555 }
556 }
557 pos += scalarsPerPosition;
558 }
559}
joshualitt8e84a1e2016-02-16 11:09:25 -0800560
561bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
reed374772b2016-10-05 17:33:02 -0700562 return paint.getMaskFilter() ||
joshualitt8e84a1e2016-02-16 11:09:25 -0800563 paint.getRasterizer() ||
564 paint.getPathEffect() ||
565 paint.isFakeBoldText() ||
566 paint.getStyle() != SkPaint::kFill_Style;
567}
568
569uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
570 uint32_t flags = paint.getFlags();
571
572 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
573 return flags;
574 }
575
576 if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
577 flags &= ~SkPaint::kLCDRenderText_Flag;
578 flags |= SkPaint::kGenA8FromLCD_Flag;
579 }
580
581 return flags;
582}