blob: 31d0291b6799117b66979093d2327928ca461ee1 [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 Osman3b655982017-03-07 16:58:08 -050021#include "SkGr.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 {
Robert Phillips26c90e02017-03-14 14:39:29 -040044 // TODO: this is the last use of GrSurfaceContextPriv
Brian Salomon6f1d36c2017-01-13 12:02:17 -050045 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
53bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
54 if (!fModifiedPaint.isValid()) {
55 fModifiedPaint.init(fOriginalPaint->skPaint());
56 fPaint = fModifiedPaint.get();
57 } else if (fFilter) {
58 // We have to reset before applying the run because the filter could have arbitrary
59 // changed the paint.
60 *fModifiedPaint.get() = fOriginalPaint->skPaint();
61 }
62 run.applyFontToPaint(fModifiedPaint.get());
63
64 if (fFilter) {
65 if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) {
66 // A false return from filter() means we should abort the current draw.
67 return false;
68 }
69 // The draw filter could have changed either the paint color or color filter.
70 this->initFilteredColor();
71 }
72 fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get()));
73 return true;
74}
75
76void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
77 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
78 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
79 const char text[], size_t byteLength, SkScalar x, SkScalar y) {
joshualitt29677982015-12-11 06:08:59 -080080 SkASSERT(byteLength == 0 || text != nullptr);
81
82 // nothing to draw
83 if (text == nullptr || byteLength == 0) {
84 return;
85 }
86
joshualitta6bf4c52016-01-19 06:59:29 -080087 // Ensure the blob is set for bitmaptext
88 blob->setHasBitmap();
89
Brian Salomonf856fd12016-12-16 14:24:34 -050090 GrAtlasTextStrike* currStrike = nullptr;
joshualitt29677982015-12-11 06:08:59 -080091
Brian Salomon6f1d36c2017-01-13 12:02:17 -050092 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -080093 SkFindAndPlaceGlyph::ProcessText(
Brian Salomon6f1d36c2017-01-13 12:02:17 -050094 paint.skPaint().getTextEncoding(), text, byteLength,
95 {x, y}, viewMatrix, paint.skPaint().getTextAlign(),
joshualitt29677982015-12-11 06:08:59 -080096 cache,
97 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -050098 position += rounding;
99 BmpAppendGlyph(
100 blob, runIndex, fontCache, &currStrike, glyph,
101 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
102 paint.filteredPremulGrColor(), cache);
joshualitt29677982015-12-11 06:08:59 -0800103 }
104 );
joshualitte76b4bb32015-12-28 07:23:58 -0800105
106 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800107}
108
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500109void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
110 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
111 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
112 const char text[], size_t byteLength, const SkScalar pos[],
113 int scalarsPerPosition, const SkPoint& offset) {
joshualitt29677982015-12-11 06:08:59 -0800114 SkASSERT(byteLength == 0 || text != nullptr);
115 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
116
117 // nothing to draw
118 if (text == nullptr || byteLength == 0) {
119 return;
120 }
121
joshualitta6bf4c52016-01-19 06:59:29 -0800122 // Ensure the blob is set for bitmaptext
123 blob->setHasBitmap();
124
Brian Salomonf856fd12016-12-16 14:24:34 -0500125 GrAtlasTextStrike* currStrike = nullptr;
joshualitt29677982015-12-11 06:08:59 -0800126
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500127 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -0800128
129 SkFindAndPlaceGlyph::ProcessPosText(
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500130 paint.skPaint().getTextEncoding(), text, byteLength,
joshualitt29677982015-12-11 06:08:59 -0800131 offset, viewMatrix, pos, scalarsPerPosition,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500132 paint.skPaint().getTextAlign(), cache,
joshualitt29677982015-12-11 06:08:59 -0800133 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
134 position += rounding;
135 BmpAppendGlyph(
136 blob, runIndex, fontCache, &currStrike, glyph,
137 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500138 paint.filteredPremulGrColor(), cache);
joshualitt29677982015-12-11 06:08:59 -0800139 }
140 );
joshualitte76b4bb32015-12-28 07:23:58 -0800141
142 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800143}
144
145void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
Brian Salomonf856fd12016-12-16 14:24:34 -0500146 GrAtlasGlyphCache* fontCache,
147 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
bsalomonc2878e22016-05-17 13:18:03 -0700148 int vx, int vy, GrColor color, SkGlyphCache* cache) {
joshualitt29677982015-12-11 06:08:59 -0800149 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700150 *strike = fontCache->getStrike(cache);
joshualitt29677982015-12-11 06:08:59 -0800151 }
152
153 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
154 skGlyph.getSubXFixed(),
155 skGlyph.getSubYFixed(),
156 GrGlyph::kCoverage_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700157 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
joshualitt29677982015-12-11 06:08:59 -0800158 if (!glyph) {
159 return;
160 }
161
162 int x = vx + glyph->fBounds.fLeft;
163 int y = vy + glyph->fBounds.fTop;
164
165 // keep them as ints until we've done the clip-test
166 int width = glyph->fBounds.width();
167 int height = glyph->fBounds.height();
168
169 SkRect r;
170 r.fLeft = SkIntToScalar(x);
171 r.fTop = SkIntToScalar(y);
172 r.fRight = r.fLeft + SkIntToScalar(width);
173 r.fBottom = r.fTop + SkIntToScalar(height);
174
bsalomonc2878e22016-05-17 13:18:03 -0700175 blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph,
Jim Van Verth08576e62016-11-16 10:15:23 -0500176 SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, true);
joshualitt29677982015-12-11 06:08:59 -0800177}
178
joshualitt0d2199b2016-01-20 06:36:09 -0800179bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
180 const SkSurfaceProps& props, const GrShaderCaps& caps) {
181 // TODO: support perspective (need getMaxScale replacement)
182 if (viewMatrix.hasPerspective()) {
183 return false;
184 }
185
186 SkScalar maxScale = viewMatrix.getMaxScale();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500187 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
joshualitt0d2199b2016-01-20 06:36:09 -0800188 // Hinted text looks far better at small resolutions
189 // Scaling up beyond 2x yields undesireable artifacts
190 if (scaledTextSize < kMinDFFontSize ||
191 scaledTextSize > kLargeDFFontLimit) {
192 return false;
193 }
194
195 bool useDFT = props.isUseDeviceIndependentFonts();
196#if SK_FORCE_DISTANCE_FIELD_TEXT
197 useDFT = true;
198#endif
199
200 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
201 return false;
202 }
203
204 // rasterizers and mask filters modify alpha, which doesn't
205 // translate well to distance
206 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
207 return false;
208 }
209
210 // TODO: add some stroking support
211 if (skPaint.getStyle() != SkPaint::kFill_Style) {
212 return false;
213 }
214
215 return true;
216}
217
218void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
219 SkPaint* skPaint,
220 SkScalar* textRatio,
221 const SkMatrix& viewMatrix) {
222 // getMaxScale doesn't support perspective, so neither do we at the moment
223 SkASSERT(!viewMatrix.hasPerspective());
224 SkScalar maxScale = viewMatrix.getMaxScale();
225 SkScalar textSize = skPaint->getTextSize();
226 SkScalar scaledTextSize = textSize;
227 // if we have non-unity scale, we need to choose our base text size
228 // based on the SkPaint's text size multiplied by the max scale factor
229 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
230 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
231 scaledTextSize *= maxScale;
232 }
233
234 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
235 // and ceiling. A scale outside of this range would require regenerating the distance fields
236 SkScalar dfMaskScaleFloor;
237 SkScalar dfMaskScaleCeil;
238 if (scaledTextSize <= kSmallDFFontLimit) {
239 dfMaskScaleFloor = kMinDFFontSize;
240 dfMaskScaleCeil = kSmallDFFontLimit;
241 *textRatio = textSize / kSmallDFFontSize;
242 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
243 } else if (scaledTextSize <= kMediumDFFontLimit) {
244 dfMaskScaleFloor = kSmallDFFontLimit;
245 dfMaskScaleCeil = kMediumDFFontLimit;
246 *textRatio = textSize / kMediumDFFontSize;
247 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
248 } else {
249 dfMaskScaleFloor = kMediumDFFontLimit;
250 dfMaskScaleCeil = kLargeDFFontLimit;
251 *textRatio = textSize / kLargeDFFontSize;
252 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
253 }
254
255 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
256 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
257 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
258 // tolerate before we'd have to move to a large mip size. When we actually test these values
259 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
260 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
261 // level)
262 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt323c2eb2016-01-20 06:48:47 -0800263 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
joshualitt0d2199b2016-01-20 06:36:09 -0800264
265 skPaint->setLCDRenderText(false);
266 skPaint->setAutohinted(false);
267 skPaint->setHinting(SkPaint::kNormal_Hinting);
268 skPaint->setSubpixelText(true);
269}
270
271void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
Brian Salomonf856fd12016-12-16 14:24:34 -0500272 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500273 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
joshualitt0d2199b2016-01-20 06:36:09 -0800274 const SkMatrix& viewMatrix,
275 const char text[], size_t byteLength,
276 SkScalar x, SkScalar y) {
277 SkASSERT(byteLength == 0 || text != nullptr);
278
279 // nothing to draw
280 if (text == nullptr || byteLength == 0) {
281 return;
282 }
283
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500284 const SkPaint& skPaint = paint.skPaint();
robertphillipse34f17d2016-07-19 07:59:22 -0700285 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
286 skPaint.isDevKernText(),
287 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800288 SkAutoDescriptor desc;
reeda9322c22016-04-12 06:47:05 -0700289 SkScalerContextEffects effects;
brianosman32f77822016-04-07 06:25:45 -0700290 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700291 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
reeda9322c22016-04-12 06:47:05 -0700292 skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
293 nullptr);
294 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects,
joshualitt0d2199b2016-01-20 06:36:09 -0800295 desc.getDesc());
296
297 SkTArray<SkScalar> positions;
298
299 const char* textPtr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700300 SkScalar stopX = 0;
301 SkScalar stopY = 0;
302 SkScalar origin = 0;
joshualitt0d2199b2016-01-20 06:36:09 -0800303 switch (skPaint.getTextAlign()) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700304 case SkPaint::kRight_Align: origin = SK_Scalar1; break;
305 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
joshualitt0d2199b2016-01-20 06:36:09 -0800306 case SkPaint::kLeft_Align: origin = 0; break;
307 }
308
309 SkAutoKern autokern;
310 const char* stop = text + byteLength;
311 while (textPtr < stop) {
312 // don't need x, y here, since all subpixel variants will have the
313 // same advance
benjaminwagnerd936f632016-02-23 10:44:31 -0800314 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
joshualitt0d2199b2016-01-20 06:36:09 -0800315
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700316 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
317 positions.push_back(stopX + origin * width);
joshualitt0d2199b2016-01-20 06:36:09 -0800318
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700319 SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
320 positions.push_back(stopY + origin * height);
joshualitt0d2199b2016-01-20 06:36:09 -0800321
322 stopX += width;
323 stopY += height;
324 }
325 SkASSERT(textPtr == stop);
326
327 SkGlyphCache::AttachCache(origPaintCache);
328
329 // now adjust starting point depending on alignment
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700330 SkScalar alignX = stopX;
331 SkScalar alignY = stopY;
joshualitt0d2199b2016-01-20 06:36:09 -0800332 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
333 alignX = SkScalarHalf(alignX);
334 alignY = SkScalarHalf(alignY);
335 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
336 alignX = 0;
337 alignY = 0;
338 }
339 x -= alignX;
340 y -= alignY;
341 SkPoint offset = SkPoint::Make(x, y);
342
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500343 DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
344 byteLength, positions.begin(), 2, offset);
joshualitt0d2199b2016-01-20 06:36:09 -0800345}
346
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500347void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
348 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
349 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
350 const char text[], size_t byteLength, const SkScalar pos[],
351 int scalarsPerPosition, const SkPoint& offset) {
joshualitt0d2199b2016-01-20 06:36:09 -0800352 SkASSERT(byteLength == 0 || text != nullptr);
353 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
354
355 // nothing to draw
356 if (text == nullptr || byteLength == 0) {
357 return;
358 }
359
360 SkTDArray<char> fallbackTxt;
361 SkTDArray<SkScalar> fallbackPos;
362
363 // Setup distance field paint and text ratio
364 SkScalar textRatio;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500365 SkPaint dfPaint(paint);
joshualitt0d2199b2016-01-20 06:36:09 -0800366 GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
367 blob->setHasDistanceField();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500368 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText());
joshualitt0d2199b2016-01-20 06:36:09 -0800369
Brian Salomonf856fd12016-12-16 14:24:34 -0500370 GrAtlasTextStrike* currStrike = nullptr;
joshualitt0d2199b2016-01-20 06:36:09 -0800371
brianosman32f77822016-04-07 06:25:45 -0700372 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700373 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
374 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -0800375 dfPaint, nullptr);
robertphillipse34f17d2016-07-19 07:59:22 -0700376 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(),
377 dfPaint.isDevKernText(),
378 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800379
380 const char* stop = text + byteLength;
381
382 if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
383 while (text < stop) {
384 const char* lastText = text;
385 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800386 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800387
388 if (glyph.fWidth) {
389 SkScalar x = offset.x() + pos[0];
390 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
391
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500392 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
393 paint.filteredPremulGrColor(), cache, textRatio, viewMatrix)) {
joshualitt0d2199b2016-01-20 06:36:09 -0800394 // couldn't append, send to fallback
395 fallbackTxt.append(SkToInt(text-lastText), lastText);
396 *fallbackPos.append() = pos[0];
397 if (2 == scalarsPerPosition) {
398 *fallbackPos.append() = pos[1];
399 }
400 }
401 }
402 pos += scalarsPerPosition;
403 }
404 } else {
405 SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
406 : SK_Scalar1;
407 while (text < stop) {
408 const char* lastText = text;
409 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800410 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800411
412 if (glyph.fWidth) {
413 SkScalar x = offset.x() + pos[0];
414 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
415
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700416 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
417 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
joshualitt0d2199b2016-01-20 06:36:09 -0800418
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500419 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
420 y - advanceY, paint.filteredPremulGrColor(), cache, textRatio,
joshualitt0d2199b2016-01-20 06:36:09 -0800421 viewMatrix)) {
422 // couldn't append, send to fallback
423 fallbackTxt.append(SkToInt(text-lastText), lastText);
424 *fallbackPos.append() = pos[0];
425 if (2 == scalarsPerPosition) {
426 *fallbackPos.append() = pos[1];
427 }
428 }
429 }
430 pos += scalarsPerPosition;
431 }
432 }
433
434 SkGlyphCache::AttachCache(cache);
435 if (fallbackTxt.count()) {
436 blob->initOverride(runIndex);
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500437 GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags,
438 viewMatrix, fallbackTxt.begin(), fallbackTxt.count(),
joshualitt0d2199b2016-01-20 06:36:09 -0800439 fallbackPos.begin(), scalarsPerPosition, offset);
440 }
441}
442
Brian Salomonf856fd12016-12-16 14:24:34 -0500443bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* cache,
444 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
joshualitt0d2199b2016-01-20 06:36:09 -0800445 SkScalar sx, SkScalar sy, GrColor color,
bsalomonc2878e22016-05-17 13:18:03 -0700446 SkGlyphCache* glyphCache,
joshualitt0d2199b2016-01-20 06:36:09 -0800447 SkScalar textRatio, const SkMatrix& viewMatrix) {
448 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700449 *strike = cache->getStrike(glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800450 }
451
452 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
453 skGlyph.getSubXFixed(),
454 skGlyph.getSubYFixed(),
455 GrGlyph::kDistance_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700456 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800457 if (!glyph) {
458 return true;
459 }
460
461 // fallback to color glyph support
462 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
463 return false;
464 }
465
466 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
467 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
468 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
469 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
470
471 SkScalar scale = textRatio;
472 dx *= scale;
473 dy *= scale;
474 width *= scale;
475 height *= scale;
476 sx += dx;
477 sy += dy;
478 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
479
bsalomonc2878e22016-05-17 13:18:03 -0700480 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph,
Jim Van Verth08576e62016-11-16 10:15:23 -0500481 sx - dx, sy - dy, scale, false);
joshualitt0d2199b2016-01-20 06:36:09 -0800482 return true;
483}
484
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500485void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip,
486 const SkPaint& paint, const SkMatrix& viewMatrix,
joshualitt0a42e682015-12-10 13:20:58 -0800487 const char text[], size_t byteLength, SkScalar x, SkScalar y,
488 const SkIRect& clipBounds) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500489 SkTextToPathIter iter(text, byteLength, paint, true);
joshualitt0a42e682015-12-10 13:20:58 -0800490
491 SkMatrix matrix;
492 matrix.setScale(iter.getPathScale(), iter.getPathScale());
493 matrix.postTranslate(x, y);
494
495 const SkPath* iterPath;
496 SkScalar xpos, prevXPos = 0;
497
498 while (iter.next(&iterPath, &xpos)) {
499 matrix.postTranslate(xpos - prevXPos, 0);
500 if (iterPath) {
501 const SkPaint& pnt = iter.getPaint();
Brian Osman11052242016-10-27 14:47:55 -0400502 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *iterPath,
joshualitt0a42e682015-12-10 13:20:58 -0800503 pnt, viewMatrix, &matrix, clipBounds, false);
504 }
505 prevXPos = xpos;
506 }
507}
508
509void GrTextUtils::DrawPosTextAsPath(GrContext* context,
Brian Osman11052242016-10-27 14:47:55 -0400510 GrRenderTargetContext* rtc,
joshualitt0a42e682015-12-10 13:20:58 -0800511 const SkSurfaceProps& props,
512 const GrClip& clip,
513 const SkPaint& origPaint, const SkMatrix& viewMatrix,
514 const char text[], size_t byteLength,
515 const SkScalar pos[], int scalarsPerPosition,
516 const SkPoint& offset, const SkIRect& clipBounds) {
517 // setup our std paint, in hopes of getting hits in the cache
518 SkPaint paint(origPaint);
519 SkScalar matrixScale = paint.setupForAsPaths();
520
521 SkMatrix matrix;
522 matrix.setScale(matrixScale, matrixScale);
523
524 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
525 paint.setStyle(SkPaint::kFill_Style);
526 paint.setPathEffect(nullptr);
527
robertphillipse34f17d2016-07-19 07:59:22 -0700528 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
529 paint.isDevKernText(),
530 true);
benjaminwagnerd936f632016-02-23 10:44:31 -0800531 SkAutoGlyphCache autoCache(paint, &props, nullptr);
532 SkGlyphCache* cache = autoCache.getCache();
joshualitt0a42e682015-12-10 13:20:58 -0800533
534 const char* stop = text + byteLength;
535 SkTextAlignProc alignProc(paint.getTextAlign());
536 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
537
538 // Now restore the original settings, so we "draw" with whatever style/stroking.
539 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -0500540 paint.setPathEffect(origPaint.refPathEffect());
joshualitt0a42e682015-12-10 13:20:58 -0800541
542 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800543 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0a42e682015-12-10 13:20:58 -0800544 if (glyph.fWidth) {
545 const SkPath* path = cache->findPath(glyph);
546 if (path) {
547 SkPoint tmsLoc;
548 tmsProc(pos, &tmsLoc);
549 SkPoint loc;
550 alignProc(tmsLoc, glyph, &loc);
551
552 matrix[SkMatrix::kMTransX] = loc.fX;
553 matrix[SkMatrix::kMTransY] = loc.fY;
Brian Osman11052242016-10-27 14:47:55 -0400554 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *path, paint,
joshualitt0a42e682015-12-10 13:20:58 -0800555 viewMatrix, &matrix, clipBounds, false);
556 }
557 }
558 pos += scalarsPerPosition;
559 }
560}
joshualitt8e84a1e2016-02-16 11:09:25 -0800561
562bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
reed374772b2016-10-05 17:33:02 -0700563 return paint.getMaskFilter() ||
joshualitt8e84a1e2016-02-16 11:09:25 -0800564 paint.getRasterizer() ||
565 paint.getPathEffect() ||
566 paint.isFakeBoldText() ||
567 paint.getStyle() != SkPaint::kFill_Style;
568}
569
570uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
571 uint32_t flags = paint.getFlags();
572
573 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
574 return flags;
575 }
576
577 if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
578 flags &= ~SkPaint::kLCDRenderText_Flag;
579 flags |= SkPaint::kGenA8FromLCD_Flag;
580 }
581
582 return flags;
583}