blob: 9baf3412dc87be2b223139f63e7662e2ea8f647e [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
Brian Osmanec8f8b02017-05-11 10:57:37 -040053void GrTextUtils::Paint::initFilteredColor() {
54 // This mirrors the logic in skpaint_to_grpaint_impl for handling paint colors
55 if (fDstColorSpace) {
56 GrColor4f filteredColor = SkColorToUnpremulGrColor4f(fPaint->getColor(), fDstColorSpace,
57 fColorXformFromSRGB);
58 if (fPaint->getColorFilter()) {
59 filteredColor = GrColor4f::FromSkColor4f(
60 fPaint->getColorFilter()->filterColor4f(filteredColor.toSkColor4f()));
61 }
62 fFilteredPremulColor = filteredColor.premul().toGrColor();
63 fFilteredUnpremulColor = filteredColor.toGrColor();
64 } else {
65 SkColor filteredSkColor = fPaint->getColor();
66 if (fPaint->getColorFilter()) {
67 filteredSkColor = fPaint->getColorFilter()->filterColor(filteredSkColor);
68 }
69 fFilteredPremulColor = SkColorToPremulGrColor(filteredSkColor);
70 fFilteredUnpremulColor = SkColorToUnpremulGrColor(filteredSkColor);
71 }
72}
73
Brian Salomon6f1d36c2017-01-13 12:02:17 -050074bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
75 if (!fModifiedPaint.isValid()) {
76 fModifiedPaint.init(fOriginalPaint->skPaint());
77 fPaint = fModifiedPaint.get();
78 } else if (fFilter) {
79 // We have to reset before applying the run because the filter could have arbitrary
80 // changed the paint.
81 *fModifiedPaint.get() = fOriginalPaint->skPaint();
82 }
83 run.applyFontToPaint(fModifiedPaint.get());
84
85 if (fFilter) {
86 if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) {
87 // A false return from filter() means we should abort the current draw.
88 return false;
89 }
90 // The draw filter could have changed either the paint color or color filter.
91 this->initFilteredColor();
92 }
93 fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get()));
94 return true;
95}
96
97void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
98 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
99 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
100 const char text[], size_t byteLength, SkScalar x, SkScalar y) {
joshualitt29677982015-12-11 06:08:59 -0800101 SkASSERT(byteLength == 0 || text != nullptr);
102
103 // nothing to draw
104 if (text == nullptr || byteLength == 0) {
105 return;
106 }
107
joshualitta6bf4c52016-01-19 06:59:29 -0800108 // Ensure the blob is set for bitmaptext
109 blob->setHasBitmap();
110
Brian Salomonf856fd12016-12-16 14:24:34 -0500111 GrAtlasTextStrike* currStrike = nullptr;
joshualitt29677982015-12-11 06:08:59 -0800112
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500113 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -0800114 SkFindAndPlaceGlyph::ProcessText(
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500115 paint.skPaint().getTextEncoding(), text, byteLength,
116 {x, y}, viewMatrix, paint.skPaint().getTextAlign(),
joshualitt29677982015-12-11 06:08:59 -0800117 cache,
118 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500119 position += rounding;
120 BmpAppendGlyph(
121 blob, runIndex, fontCache, &currStrike, glyph,
122 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
Brian Osmanec8f8b02017-05-11 10:57:37 -0400123 paint.filteredPremulColor(), cache);
joshualitt29677982015-12-11 06:08:59 -0800124 }
125 );
joshualitte76b4bb32015-12-28 07:23:58 -0800126
127 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800128}
129
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500130void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
131 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
132 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
133 const char text[], size_t byteLength, const SkScalar pos[],
134 int scalarsPerPosition, const SkPoint& offset) {
joshualitt29677982015-12-11 06:08:59 -0800135 SkASSERT(byteLength == 0 || text != nullptr);
136 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
137
138 // nothing to draw
139 if (text == nullptr || byteLength == 0) {
140 return;
141 }
142
joshualitta6bf4c52016-01-19 06:59:29 -0800143 // Ensure the blob is set for bitmaptext
144 blob->setHasBitmap();
145
Brian Salomonf856fd12016-12-16 14:24:34 -0500146 GrAtlasTextStrike* currStrike = nullptr;
joshualitt29677982015-12-11 06:08:59 -0800147
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500148 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
joshualitt29677982015-12-11 06:08:59 -0800149
150 SkFindAndPlaceGlyph::ProcessPosText(
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500151 paint.skPaint().getTextEncoding(), text, byteLength,
joshualitt29677982015-12-11 06:08:59 -0800152 offset, viewMatrix, pos, scalarsPerPosition,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500153 paint.skPaint().getTextAlign(), cache,
joshualitt29677982015-12-11 06:08:59 -0800154 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
155 position += rounding;
156 BmpAppendGlyph(
157 blob, runIndex, fontCache, &currStrike, glyph,
158 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
Brian Osmanec8f8b02017-05-11 10:57:37 -0400159 paint.filteredPremulColor(), cache);
joshualitt29677982015-12-11 06:08:59 -0800160 }
161 );
joshualitte76b4bb32015-12-28 07:23:58 -0800162
163 SkGlyphCache::AttachCache(cache);
joshualitt29677982015-12-11 06:08:59 -0800164}
165
166void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
Brian Salomonf856fd12016-12-16 14:24:34 -0500167 GrAtlasGlyphCache* fontCache,
168 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
bsalomonc2878e22016-05-17 13:18:03 -0700169 int vx, int vy, GrColor color, SkGlyphCache* cache) {
joshualitt29677982015-12-11 06:08:59 -0800170 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700171 *strike = fontCache->getStrike(cache);
joshualitt29677982015-12-11 06:08:59 -0800172 }
173
174 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
175 skGlyph.getSubXFixed(),
176 skGlyph.getSubYFixed(),
177 GrGlyph::kCoverage_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700178 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
joshualitt29677982015-12-11 06:08:59 -0800179 if (!glyph) {
180 return;
181 }
182
183 int x = vx + glyph->fBounds.fLeft;
184 int y = vy + glyph->fBounds.fTop;
185
186 // keep them as ints until we've done the clip-test
187 int width = glyph->fBounds.width();
188 int height = glyph->fBounds.height();
189
190 SkRect r;
191 r.fLeft = SkIntToScalar(x);
192 r.fTop = SkIntToScalar(y);
193 r.fRight = r.fLeft + SkIntToScalar(width);
194 r.fBottom = r.fTop + SkIntToScalar(height);
195
bsalomonc2878e22016-05-17 13:18:03 -0700196 blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph,
Jim Van Verth08576e62016-11-16 10:15:23 -0500197 SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, true);
joshualitt29677982015-12-11 06:08:59 -0800198}
199
joshualitt0d2199b2016-01-20 06:36:09 -0800200bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
201 const SkSurfaceProps& props, const GrShaderCaps& caps) {
202 // TODO: support perspective (need getMaxScale replacement)
203 if (viewMatrix.hasPerspective()) {
204 return false;
205 }
206
207 SkScalar maxScale = viewMatrix.getMaxScale();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500208 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
joshualitt0d2199b2016-01-20 06:36:09 -0800209 // Hinted text looks far better at small resolutions
210 // Scaling up beyond 2x yields undesireable artifacts
211 if (scaledTextSize < kMinDFFontSize ||
212 scaledTextSize > kLargeDFFontLimit) {
213 return false;
214 }
215
216 bool useDFT = props.isUseDeviceIndependentFonts();
217#if SK_FORCE_DISTANCE_FIELD_TEXT
218 useDFT = true;
219#endif
220
221 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
222 return false;
223 }
224
225 // rasterizers and mask filters modify alpha, which doesn't
226 // translate well to distance
227 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
228 return false;
229 }
230
231 // TODO: add some stroking support
232 if (skPaint.getStyle() != SkPaint::kFill_Style) {
233 return false;
234 }
235
236 return true;
237}
238
239void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
240 SkPaint* skPaint,
241 SkScalar* textRatio,
242 const SkMatrix& viewMatrix) {
243 // getMaxScale doesn't support perspective, so neither do we at the moment
244 SkASSERT(!viewMatrix.hasPerspective());
245 SkScalar maxScale = viewMatrix.getMaxScale();
246 SkScalar textSize = skPaint->getTextSize();
247 SkScalar scaledTextSize = textSize;
248 // if we have non-unity scale, we need to choose our base text size
249 // based on the SkPaint's text size multiplied by the max scale factor
250 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
251 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
252 scaledTextSize *= maxScale;
253 }
254
255 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
256 // and ceiling. A scale outside of this range would require regenerating the distance fields
257 SkScalar dfMaskScaleFloor;
258 SkScalar dfMaskScaleCeil;
259 if (scaledTextSize <= kSmallDFFontLimit) {
260 dfMaskScaleFloor = kMinDFFontSize;
261 dfMaskScaleCeil = kSmallDFFontLimit;
262 *textRatio = textSize / kSmallDFFontSize;
263 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
264 } else if (scaledTextSize <= kMediumDFFontLimit) {
265 dfMaskScaleFloor = kSmallDFFontLimit;
266 dfMaskScaleCeil = kMediumDFFontLimit;
267 *textRatio = textSize / kMediumDFFontSize;
268 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
269 } else {
270 dfMaskScaleFloor = kMediumDFFontLimit;
271 dfMaskScaleCeil = kLargeDFFontLimit;
272 *textRatio = textSize / kLargeDFFontSize;
273 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
274 }
275
276 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
277 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
278 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
279 // tolerate before we'd have to move to a large mip size. When we actually test these values
280 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
281 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
282 // level)
283 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt323c2eb2016-01-20 06:48:47 -0800284 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
joshualitt0d2199b2016-01-20 06:36:09 -0800285
286 skPaint->setLCDRenderText(false);
287 skPaint->setAutohinted(false);
288 skPaint->setHinting(SkPaint::kNormal_Hinting);
289 skPaint->setSubpixelText(true);
290}
291
292void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
Brian Salomonf856fd12016-12-16 14:24:34 -0500293 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500294 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
joshualitt0d2199b2016-01-20 06:36:09 -0800295 const SkMatrix& viewMatrix,
296 const char text[], size_t byteLength,
297 SkScalar x, SkScalar y) {
298 SkASSERT(byteLength == 0 || text != nullptr);
299
300 // nothing to draw
301 if (text == nullptr || byteLength == 0) {
302 return;
303 }
304
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500305 const SkPaint& skPaint = paint.skPaint();
robertphillipse34f17d2016-07-19 07:59:22 -0700306 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
307 skPaint.isDevKernText(),
308 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800309 SkAutoDescriptor desc;
reeda9322c22016-04-12 06:47:05 -0700310 SkScalerContextEffects effects;
brianosman32f77822016-04-07 06:25:45 -0700311 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700312 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
reeda9322c22016-04-12 06:47:05 -0700313 skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
314 nullptr);
315 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects,
joshualitt0d2199b2016-01-20 06:36:09 -0800316 desc.getDesc());
317
318 SkTArray<SkScalar> positions;
319
320 const char* textPtr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700321 SkScalar stopX = 0;
322 SkScalar stopY = 0;
323 SkScalar origin = 0;
joshualitt0d2199b2016-01-20 06:36:09 -0800324 switch (skPaint.getTextAlign()) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700325 case SkPaint::kRight_Align: origin = SK_Scalar1; break;
326 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
joshualitt0d2199b2016-01-20 06:36:09 -0800327 case SkPaint::kLeft_Align: origin = 0; break;
328 }
329
330 SkAutoKern autokern;
331 const char* stop = text + byteLength;
332 while (textPtr < stop) {
333 // don't need x, y here, since all subpixel variants will have the
334 // same advance
benjaminwagnerd936f632016-02-23 10:44:31 -0800335 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
joshualitt0d2199b2016-01-20 06:36:09 -0800336
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700337 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
338 positions.push_back(stopX + origin * width);
joshualitt0d2199b2016-01-20 06:36:09 -0800339
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700340 SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
341 positions.push_back(stopY + origin * height);
joshualitt0d2199b2016-01-20 06:36:09 -0800342
343 stopX += width;
344 stopY += height;
345 }
346 SkASSERT(textPtr == stop);
347
348 SkGlyphCache::AttachCache(origPaintCache);
349
350 // now adjust starting point depending on alignment
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700351 SkScalar alignX = stopX;
352 SkScalar alignY = stopY;
joshualitt0d2199b2016-01-20 06:36:09 -0800353 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
354 alignX = SkScalarHalf(alignX);
355 alignY = SkScalarHalf(alignY);
356 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
357 alignX = 0;
358 alignY = 0;
359 }
360 x -= alignX;
361 y -= alignY;
362 SkPoint offset = SkPoint::Make(x, y);
363
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500364 DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
365 byteLength, positions.begin(), 2, offset);
joshualitt0d2199b2016-01-20 06:36:09 -0800366}
367
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500368void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
369 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
370 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
371 const char text[], size_t byteLength, const SkScalar pos[],
372 int scalarsPerPosition, const SkPoint& offset) {
joshualitt0d2199b2016-01-20 06:36:09 -0800373 SkASSERT(byteLength == 0 || text != nullptr);
374 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
375
376 // nothing to draw
377 if (text == nullptr || byteLength == 0) {
378 return;
379 }
380
381 SkTDArray<char> fallbackTxt;
382 SkTDArray<SkScalar> fallbackPos;
383
384 // Setup distance field paint and text ratio
385 SkScalar textRatio;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500386 SkPaint dfPaint(paint);
joshualitt0d2199b2016-01-20 06:36:09 -0800387 GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
388 blob->setHasDistanceField();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500389 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText());
joshualitt0d2199b2016-01-20 06:36:09 -0800390
Brian Salomonf856fd12016-12-16 14:24:34 -0500391 GrAtlasTextStrike* currStrike = nullptr;
joshualitt0d2199b2016-01-20 06:36:09 -0800392
brianosman32f77822016-04-07 06:25:45 -0700393 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
brianosmana1e8f8d2016-04-08 06:47:54 -0700394 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
395 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -0800396 dfPaint, nullptr);
robertphillipse34f17d2016-07-19 07:59:22 -0700397 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(),
398 dfPaint.isDevKernText(),
399 true);
joshualitt0d2199b2016-01-20 06:36:09 -0800400
401 const char* stop = text + byteLength;
402
403 if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
404 while (text < stop) {
405 const char* lastText = text;
406 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800407 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800408
409 if (glyph.fWidth) {
410 SkScalar x = offset.x() + pos[0];
411 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
412
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500413 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
Brian Osmanec8f8b02017-05-11 10:57:37 -0400414 paint.filteredPremulColor(), cache, textRatio, viewMatrix)) {
joshualitt0d2199b2016-01-20 06:36:09 -0800415 // couldn't append, send to fallback
416 fallbackTxt.append(SkToInt(text-lastText), lastText);
417 *fallbackPos.append() = pos[0];
418 if (2 == scalarsPerPosition) {
419 *fallbackPos.append() = pos[1];
420 }
421 }
422 }
423 pos += scalarsPerPosition;
424 }
425 } else {
426 SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
427 : SK_Scalar1;
428 while (text < stop) {
429 const char* lastText = text;
430 // the last 2 parameters are ignored
benjaminwagnerd936f632016-02-23 10:44:31 -0800431 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0d2199b2016-01-20 06:36:09 -0800432
433 if (glyph.fWidth) {
434 SkScalar x = offset.x() + pos[0];
435 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
436
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700437 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
438 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
joshualitt0d2199b2016-01-20 06:36:09 -0800439
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500440 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
Brian Osmanec8f8b02017-05-11 10:57:37 -0400441 y - advanceY, paint.filteredPremulColor(), cache, textRatio,
joshualitt0d2199b2016-01-20 06:36:09 -0800442 viewMatrix)) {
443 // couldn't append, send to fallback
444 fallbackTxt.append(SkToInt(text-lastText), lastText);
445 *fallbackPos.append() = pos[0];
446 if (2 == scalarsPerPosition) {
447 *fallbackPos.append() = pos[1];
448 }
449 }
450 }
451 pos += scalarsPerPosition;
452 }
453 }
454
455 SkGlyphCache::AttachCache(cache);
456 if (fallbackTxt.count()) {
457 blob->initOverride(runIndex);
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500458 GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags,
459 viewMatrix, fallbackTxt.begin(), fallbackTxt.count(),
joshualitt0d2199b2016-01-20 06:36:09 -0800460 fallbackPos.begin(), scalarsPerPosition, offset);
461 }
462}
463
Brian Salomonf856fd12016-12-16 14:24:34 -0500464bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* cache,
465 GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
joshualitt0d2199b2016-01-20 06:36:09 -0800466 SkScalar sx, SkScalar sy, GrColor color,
bsalomonc2878e22016-05-17 13:18:03 -0700467 SkGlyphCache* glyphCache,
joshualitt0d2199b2016-01-20 06:36:09 -0800468 SkScalar textRatio, const SkMatrix& viewMatrix) {
469 if (!*strike) {
bsalomonc2878e22016-05-17 13:18:03 -0700470 *strike = cache->getStrike(glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800471 }
472
473 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
474 skGlyph.getSubXFixed(),
475 skGlyph.getSubYFixed(),
476 GrGlyph::kDistance_MaskStyle);
bsalomonc2878e22016-05-17 13:18:03 -0700477 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
joshualitt0d2199b2016-01-20 06:36:09 -0800478 if (!glyph) {
479 return true;
480 }
481
482 // fallback to color glyph support
483 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
484 return false;
485 }
486
487 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
488 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
489 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
490 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
491
492 SkScalar scale = textRatio;
493 dx *= scale;
494 dy *= scale;
495 width *= scale;
496 height *= scale;
497 sx += dx;
498 sy += dy;
499 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
500
bsalomonc2878e22016-05-17 13:18:03 -0700501 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph,
Jim Van Verth08576e62016-11-16 10:15:23 -0500502 sx - dx, sy - dy, scale, false);
joshualitt0d2199b2016-01-20 06:36:09 -0800503 return true;
504}
505
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500506void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip,
507 const SkPaint& paint, const SkMatrix& viewMatrix,
joshualitt0a42e682015-12-10 13:20:58 -0800508 const char text[], size_t byteLength, SkScalar x, SkScalar y,
509 const SkIRect& clipBounds) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500510 SkTextToPathIter iter(text, byteLength, paint, true);
joshualitt0a42e682015-12-10 13:20:58 -0800511
512 SkMatrix matrix;
513 matrix.setScale(iter.getPathScale(), iter.getPathScale());
514 matrix.postTranslate(x, y);
515
516 const SkPath* iterPath;
517 SkScalar xpos, prevXPos = 0;
518
519 while (iter.next(&iterPath, &xpos)) {
520 matrix.postTranslate(xpos - prevXPos, 0);
521 if (iterPath) {
522 const SkPaint& pnt = iter.getPaint();
Brian Osman11052242016-10-27 14:47:55 -0400523 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *iterPath,
joshualitt0a42e682015-12-10 13:20:58 -0800524 pnt, viewMatrix, &matrix, clipBounds, false);
525 }
526 prevXPos = xpos;
527 }
528}
529
530void GrTextUtils::DrawPosTextAsPath(GrContext* context,
Brian Osman11052242016-10-27 14:47:55 -0400531 GrRenderTargetContext* rtc,
joshualitt0a42e682015-12-10 13:20:58 -0800532 const SkSurfaceProps& props,
533 const GrClip& clip,
534 const SkPaint& origPaint, const SkMatrix& viewMatrix,
535 const char text[], size_t byteLength,
536 const SkScalar pos[], int scalarsPerPosition,
537 const SkPoint& offset, const SkIRect& clipBounds) {
538 // setup our std paint, in hopes of getting hits in the cache
539 SkPaint paint(origPaint);
540 SkScalar matrixScale = paint.setupForAsPaths();
541
542 SkMatrix matrix;
543 matrix.setScale(matrixScale, matrixScale);
544
545 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
546 paint.setStyle(SkPaint::kFill_Style);
547 paint.setPathEffect(nullptr);
548
robertphillipse34f17d2016-07-19 07:59:22 -0700549 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
550 paint.isDevKernText(),
551 true);
benjaminwagnerd936f632016-02-23 10:44:31 -0800552 SkAutoGlyphCache autoCache(paint, &props, nullptr);
553 SkGlyphCache* cache = autoCache.getCache();
joshualitt0a42e682015-12-10 13:20:58 -0800554
555 const char* stop = text + byteLength;
556 SkTextAlignProc alignProc(paint.getTextAlign());
557 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
558
559 // Now restore the original settings, so we "draw" with whatever style/stroking.
560 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -0500561 paint.setPathEffect(origPaint.refPathEffect());
joshualitt0a42e682015-12-10 13:20:58 -0800562
563 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800564 const SkGlyph& glyph = glyphCacheProc(cache, &text);
joshualitt0a42e682015-12-10 13:20:58 -0800565 if (glyph.fWidth) {
566 const SkPath* path = cache->findPath(glyph);
567 if (path) {
568 SkPoint tmsLoc;
569 tmsProc(pos, &tmsLoc);
570 SkPoint loc;
571 alignProc(tmsLoc, glyph, &loc);
572
573 matrix[SkMatrix::kMTransX] = loc.fX;
574 matrix[SkMatrix::kMTransY] = loc.fY;
Brian Osman11052242016-10-27 14:47:55 -0400575 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *path, paint,
joshualitt0a42e682015-12-10 13:20:58 -0800576 viewMatrix, &matrix, clipBounds, false);
577 }
578 }
579 pos += scalarsPerPosition;
580 }
581}
joshualitt8e84a1e2016-02-16 11:09:25 -0800582
583bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
reed374772b2016-10-05 17:33:02 -0700584 return paint.getMaskFilter() ||
joshualitt8e84a1e2016-02-16 11:09:25 -0800585 paint.getRasterizer() ||
586 paint.getPathEffect() ||
587 paint.isFakeBoldText() ||
588 paint.getStyle() != SkPaint::kFill_Style;
589}
590
591uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
592 uint32_t flags = paint.getFlags();
593
594 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
595 return flags;
596 }
597
598 if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
599 flags &= ~SkPaint::kLCDRenderText_Flag;
600 flags |= SkPaint::kGenA8FromLCD_Flag;
601 }
602
603 return flags;
604}