blob: 45145f6432026f4c164edb2fa50b6ff15cd13f0d [file] [log] [blame]
joshualitt1d89e8d2015-04-01 12:40:54 -07001/*
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 */
Hal Canaryc640d0d2018-06-13 09:59:02 -04007
Herb Derby26cbe512018-05-24 14:39:01 -04008#include "GrTextContext.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Brian Salomonc7fe0f72018-05-11 10:14:21 -040010#include "GrCaps.h"
robertphillips73c4e642016-03-02 11:36:59 -080011#include "GrContext.h"
Robert Phillipsf35fd8d2018-01-22 10:48:15 -050012#include "GrContextPriv.h"
Jim Van Verthd401da62018-05-03 10:40:30 -040013#include "GrSDFMaskFilter.h"
joshualittb7133be2015-04-08 09:08:31 -070014#include "GrTextBlobCache.h"
Brian Salomon52db9402017-11-07 14:58:55 -050015#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070016#include "SkDraw.h"
Jim Van Verth54d9c882018-02-08 16:14:48 -050017#include "SkDrawProcs.h"
Brian Salomon52db9402017-11-07 14:58:55 -050018#include "SkFindAndPlaceGlyph.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040019#include "SkGlyphRun.h"
Brian Osman3b655982017-03-07 16:58:08 -050020#include "SkGr.h"
Jim Van Verthc65b65d2018-01-16 16:26:35 -050021#include "SkGraphics.h"
Brian Salomonaf597482017-11-07 16:23:34 -050022#include "SkMakeUnique.h"
Mike Reed80747ef2018-01-23 15:29:32 -050023#include "SkMaskFilterBase.h"
Herb Derbyeb3f6742018-03-05 14:36:45 -050024#include "SkPaintPriv.h"
Jim Van Verth54d9c882018-02-08 16:14:48 -050025#include "SkTextMapStateProc.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040026#include "SkTo.h"
Brian Salomon649a3412017-03-09 13:50:43 -050027#include "ops/GrMeshDrawOp.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070028
Brian Salomonaf597482017-11-07 16:23:34 -050029// DF sizes and thresholds for usage of the small and medium sizes. For example, above
30// kSmallDFFontLimit we will use the medium size. The large size is used up until the size at
31// which we switch over to drawing as paths as controlled by Options.
32static const int kSmallDFFontSize = 32;
33static const int kSmallDFFontLimit = 32;
Jim Van Verth825d4d72018-01-30 20:37:03 +000034static const int kMediumDFFontSize = 72;
35static const int kMediumDFFontLimit = 72;
36static const int kLargeDFFontSize = 162;
Brian Salomonaf597482017-11-07 16:23:34 -050037
38static const int kDefaultMinDistanceFieldFontSize = 18;
39#ifdef SK_BUILD_FOR_ANDROID
40static const int kDefaultMaxDistanceFieldFontSize = 384;
41#else
42static const int kDefaultMaxDistanceFieldFontSize = 2 * kLargeDFFontSize;
43#endif
44
Herb Derby26cbe512018-05-24 14:39:01 -040045GrTextContext::GrTextContext(const Options& options)
Khushal3e7548c2018-05-23 15:45:01 -070046 : fDistanceAdjustTable(new GrDistanceFieldAdjustTable), fOptions(options) {
47 SanitizeOptions(&fOptions);
joshualitt9bd2daf2015-04-17 09:30:06 -070048}
49
Herb Derby26cbe512018-05-24 14:39:01 -040050std::unique_ptr<GrTextContext> GrTextContext::Make(const Options& options) {
51 return std::unique_ptr<GrTextContext>(new GrTextContext(options));
joshualitt1d89e8d2015-04-01 12:40:54 -070052}
53
Herb Derby26cbe512018-05-24 14:39:01 -040054SkColor GrTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -050055 SkColor canonicalColor = paint.computeLuminanceColor();
joshualitt9e36c1a2015-04-14 12:17:27 -070056 if (lcd) {
57 // This is the correct computation, but there are tons of cases where LCD can be overridden.
58 // For now we just regenerate if any run in a textblob has LCD.
59 // TODO figure out where all of these overrides are and see if we can incorporate that logic
60 // at a higher level *OR* use sRGB
61 SkASSERT(false);
62 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
63 } else {
64 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
65 // gamma corrected masks anyways, nor color
66 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
67 SkColorGetG(canonicalColor),
68 SkColorGetB(canonicalColor));
69 // reduce to our finite number of bits
70 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
71 }
72 return canonicalColor;
73}
74
Herb Derby26cbe512018-05-24 14:39:01 -040075SkScalerContextFlags GrTextContext::ComputeScalerContextFlags(
Herb Derbyd8327a82018-01-22 14:39:27 -050076 const GrColorSpaceInfo& colorSpaceInfo) {
Brian Osman34ec3742018-07-03 10:40:57 -040077 // If we're doing linear blending, then we can disable the gamma hacks.
brianosman5280dcb2016-04-14 06:02:59 -070078 // Otherwise, leave them on. In either case, we still want the contrast boost:
Brian Osman34ec3742018-07-03 10:40:57 -040079 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
80 if (colorSpaceInfo.isLinearlyBlended()) {
Herb Derbyd8327a82018-01-22 14:39:27 -050081 return SkScalerContextFlags::kBoostContrast;
brianosman32f77822016-04-07 06:25:45 -070082 } else {
Herb Derbyd8327a82018-01-22 14:39:27 -050083 return SkScalerContextFlags::kFakeGammaAndBoostContrast;
brianosman32f77822016-04-07 06:25:45 -070084 }
85}
86
Herb Derbye42dc2d2018-08-01 14:49:26 -040087bool glyph_too_big_for_atlas(const SkGlyph& glyph) {
88 return GrDrawOpAtlas::GlyphTooLargeForAtlas(glyph.fWidth, glyph.fHeight);
89}
90
91static SkRect rect_to_draw(
92 const SkGlyph& glyph, SkPoint origin, SkScalar textScale, GrGlyph::MaskStyle maskStyle) {
93
94 SkScalar dx = SkIntToScalar(glyph.fLeft);
95 SkScalar dy = SkIntToScalar(glyph.fTop);
96 SkScalar width = SkIntToScalar(glyph.fWidth);
97 SkScalar height = SkIntToScalar(glyph.fHeight);
98
99 if (maskStyle == GrGlyph::kDistance_MaskStyle) {
100 dx += SK_DistanceFieldInset;
101 dy += SK_DistanceFieldInset;
102 width -= 2 * SK_DistanceFieldInset;
103 height -= 2 * SK_DistanceFieldInset;
104 }
105
106 dx *= textScale;
107 dy *= textScale;
108 width *= textScale;
109 height *= textScale;
110
111 return SkRect::MakeXYWH(origin.x() + dx, origin.y() + dy, width, height);
112}
Herb Derby12e42562018-07-28 14:27:48 -0400113
Herb Derby12e42562018-07-28 14:27:48 -0400114void GrTextContext::regenerateGlyphRunList(GrTextBlob* cacheBlob,
115 GrGlyphCache* glyphCache,
116 const GrShaderCaps& shaderCaps,
117 const GrTextUtils::Paint& paint,
118 SkScalerContextFlags scalerContextFlags,
119 const SkMatrix& viewMatrix,
120 const SkSurfaceProps& props,
Herb Derby74c6ed32018-07-28 18:07:54 -0400121 const SkGlyphRunList& glyphRunList,
122 SkGlyphRunListDrawer* glyphDrawer) {
Herb Derby12e42562018-07-28 14:27:48 -0400123 SkPoint origin = glyphRunList.origin();
124 cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, origin.x(), origin.y());
125
126 // Regenerate textblob
127 GrTextUtils::RunPaint runPaint(&paint);
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400128 int runIndex = 0;
Herb Derby12e42562018-07-28 14:27:48 -0400129 for (const auto& glyphRun : glyphRunList) {
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400130 cacheBlob->push_back_run(runIndex);
Herb Derby12e42562018-07-28 14:27:48 -0400131
132 if (!runPaint.modifyForRun([glyphRun](SkPaint* p) { *p = glyphRun.paint(); })) {
133 continue;
134 }
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400135 cacheBlob->setRunPaintFlags(runIndex, runPaint.skPaint().getFlags());
Herb Derby12e42562018-07-28 14:27:48 -0400136
137 if (CanDrawAsDistanceFields(runPaint, viewMatrix, props,
138 shaderCaps.supportsDistanceFieldText(), fOptions)) {
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400139 bool hasWCoord = viewMatrix.hasPerspective()
140 || fOptions.fDistanceFieldVerticesAlwaysHaveW;
141
142 // Setup distance field runPaint and text ratio
143 SkScalar textRatio;
144 SkPaint dfPaint(runPaint);
145 SkScalerContextFlags flags;
146 InitDistanceFieldPaint(cacheBlob, &dfPaint, viewMatrix, fOptions, &textRatio, &flags);
147 cacheBlob->setHasDistanceField();
148 cacheBlob->setSubRunHasDistanceFields(runIndex, runPaint.skPaint().isLCDRenderText(),
149 runPaint.skPaint().isAntiAlias(), hasWCoord);
150
151 FallbackGlyphRunHelper fallbackTextHelper(
152 viewMatrix, runPaint, glyphCache->getGlyphSizeLimit(), textRatio);
153
154 sk_sp<GrTextStrike> currStrike;
155
156 {
157 auto cache = cacheBlob->setupCache(runIndex, props, flags, dfPaint, nullptr);
158
159 const SkPoint* positionCursor = glyphRun.positions().data();
160 for (auto glyphID : glyphRun.shuntGlyphsIDs()) {
161 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID);
162 SkPoint glyphPos = origin + *positionCursor++;
163 if (glyph.fWidth > 0) {
164 if (glyph.fMaskFormat == SkMask::kSDF_Format) {
Herb Derbye42dc2d2018-08-01 14:49:26 -0400165
166 SkScalar sx = glyphPos.fX,
167 sy = glyphPos.fY;
168
169 if (glyph_too_big_for_atlas(glyph)) {
170 SkRect glyphRect =
171 rect_to_draw(glyph, glyphPos, textRatio,
172 GrGlyph::kDistance_MaskStyle);
173 if (!glyphRect.isEmpty()) {
174 const SkPath* glyphPath = cache->findPath(glyph);
175 if (glyphPath != nullptr) {
176 cacheBlob->appendPathGlyph(
177 runIndex, *glyphPath, sx, sy, textRatio, false);
178 }
179 }
180 } else {
181 AppendGlyph(cacheBlob, runIndex, glyphCache, &currStrike,
182 glyph, GrGlyph::kDistance_MaskStyle, sx, sy,
183 runPaint.filteredPremulColor(),
184 cache.get(), textRatio, true);
185 }
186
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400187 } else {
188 // can't append non-SDF glyph to SDF batch, send to fallback
189 fallbackTextHelper.appendText(glyph, glyphID, glyphPos);
190 }
191 }
192 }
193 }
194
195 fallbackTextHelper.drawText(
196 cacheBlob, runIndex, glyphCache, props, runPaint, scalerContextFlags);
197
198 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
199 // Ensure the blob is set for bitmaptext
200 cacheBlob->setHasBitmap();
201
202 // setup our std runPaint, in hopes of getting hits in the cache
203 SkPaint pathPaint(runPaint);
204 SkScalar matrixScale = pathPaint.setupForAsPaths();
205
206 FallbackGlyphRunHelper fallbackTextHelper(
207 viewMatrix, runPaint, glyphCache->getGlyphSizeLimit(), matrixScale);
208
209 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
210 pathPaint.setStyle(SkPaint::kFill_Style);
211 pathPaint.setPathEffect(nullptr);
212
213 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
214 pathPaint, &props, SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
215
Herb Derby0ab5ce12018-07-30 10:10:40 -0400216 auto drawOnePath =
217 [&fallbackTextHelper, matrixScale, runIndex, cacheBlob]
218 (const SkPath* path, const SkGlyph& glyph, SkPoint position) {
219 if (glyph.fMaskFormat == SkMask::kARGB32_Format) {
220 fallbackTextHelper.appendText(glyph, glyph.getGlyphID(), position);
221 } else {
222 if (path != nullptr) {
223 cacheBlob->appendPathGlyph(
224 runIndex, *path, position.fX, position.fY, matrixScale, false);
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400225 }
226 }
Herb Derby0ab5ce12018-07-30 10:10:40 -0400227 };
228
229 glyphDrawer->drawUsingPaths(glyphRun, origin, cache.get(), drawOnePath);
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400230
231 fallbackTextHelper.drawText(
232 cacheBlob, runIndex, glyphCache, props, runPaint, scalerContextFlags);
233
Herb Derby12e42562018-07-28 14:27:48 -0400234 } else {
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400235 // Ensure the blob is set for bitmaptext
236 cacheBlob->setHasBitmap();
237 sk_sp<GrTextStrike> currStrike;
238 auto cache = cacheBlob->setupCache(
239 runIndex, props, scalerContextFlags, runPaint, &viewMatrix);
Herb Derby74c6ed32018-07-28 18:07:54 -0400240
241 auto drawOneGlyph =
242 [cacheBlob, runIndex, glyphCache, &currStrike, runPaint, cache{cache.get()}]
243 (const SkMask& mask, const SkGlyph& glyph, SkPoint position) {
Herb Derbye42dc2d2018-08-01 14:49:26 -0400244 SkScalar sx = SkScalarFloorToScalar(position.fX),
245 sy = SkScalarFloorToScalar(position.fY);
246
247 if (glyph_too_big_for_atlas(glyph)) {
248 SkRect glyphRect =
249 rect_to_draw(glyph, {sx, sy}, SK_Scalar1,
250 GrGlyph::kCoverage_MaskStyle);
251 if (!glyphRect.isEmpty()) {
252 const SkPath* glyphPath = cache->findPath(glyph);
253 if (glyphPath != nullptr) {
254 cacheBlob->appendPathGlyph(
255 runIndex, *glyphPath, sx, sy, SK_Scalar1, true);
256 }
257 }
258 } else {
259 AppendGlyph(cacheBlob, runIndex, glyphCache, &currStrike,
260 glyph, GrGlyph::kCoverage_MaskStyle, sx, sy,
261 runPaint.filteredPremulColor(), cache, SK_Scalar1, false);
262 }
Herb Derby74c6ed32018-07-28 18:07:54 -0400263 };
264
265 glyphDrawer->drawUsingMasks(cache.get(), glyphRun, origin, viewMatrix, drawOneGlyph);
Herb Derby12e42562018-07-28 14:27:48 -0400266 }
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400267 runIndex += 1;
Herb Derby12e42562018-07-28 14:27:48 -0400268 }
269}
270
Herb Derbye42dc2d2018-08-01 14:49:26 -0400271void GrTextContext::AppendGlyph(GrTextBlob* blob, int runIndex,
272 GrGlyphCache* grGlyphCache,
273 sk_sp<GrTextStrike>* strike,
274 const SkGlyph& skGlyph, GrGlyph::MaskStyle maskStyle,
275 SkScalar sx, SkScalar sy,
276 GrColor color, SkGlyphCache* skGlyphCache,
277 SkScalar textRatio, bool needsTransform) {
278 if (!*strike) {
279 *strike = grGlyphCache->getStrike(skGlyphCache);
280 }
281
282 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
283 skGlyph.getSubXFixed(),
284 skGlyph.getSubYFixed(),
285 maskStyle);
286 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
287 if (!glyph) {
288 return;
289 }
290
291 SkASSERT(skGlyph.fWidth == glyph->width());
292 SkASSERT(skGlyph.fHeight == glyph->height());
293
294 SkRect glyphRect = rect_to_draw(skGlyph, {sx, sy}, textRatio, maskStyle);
295
296 if (!glyphRect.isEmpty()) {
297 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph,
298 skGlyphCache, skGlyph, sx, sy, textRatio, !needsTransform);
299 }
300}
301
Herb Derbycddab252018-07-16 11:19:04 -0400302void GrTextContext::drawGlyphRunList(
303 GrContext* context, GrTextUtils::Target* target, const GrClip& clip,
Herb Derbyb935cf82018-07-26 16:54:18 -0400304 const SkMatrix& viewMatrix, const SkSurfaceProps& props, const SkGlyphRunList& glyphRunList,
Herb Derbycddab252018-07-16 11:19:04 -0400305 const SkIRect& clipBounds) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400306 SkPoint origin = glyphRunList.origin();
joshualitt9e36c1a2015-04-14 12:17:27 -0700307
Herb Derbycddab252018-07-16 11:19:04 -0400308 // Get the first paint to use as the key paint.
Herb Derbyb935cf82018-07-26 16:54:18 -0400309 const SkPaint& skPaint = glyphRunList.paint();
Herb Derbycddab252018-07-16 11:19:04 -0400310
joshualitt9b8e79e2015-04-24 09:57:12 -0700311 // If we have been abandoned, then don't draw
Khushalc421ca12018-06-26 14:38:34 -0700312 if (context->abandoned()) {
robertphillipsea461502015-05-26 11:38:03 -0700313 return;
314 }
315
Mike Reed80747ef2018-01-23 15:29:32 -0500316 SkMaskFilterBase::BlurRec blurRec;
joshualitt53b5f442015-04-13 06:33:59 -0700317 // It might be worth caching these things, but its not clear at this time
318 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
319 const SkMaskFilter* mf = skPaint.getMaskFilter();
Herb Derbyb935cf82018-07-26 16:54:18 -0400320 bool canCache = glyphRunList.canCache() && !(skPaint.getPathEffect() ||
Herb Derbycddab252018-07-16 11:19:04 -0400321 (mf && !as_MFB(mf)->asABlur(&blurRec)));
Herb Derbyd8327a82018-01-22 14:39:27 -0500322 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
joshualitt2a0e9f32015-04-13 06:12:21 -0700323
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500324 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500325 GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
326
Herb Derbycddab252018-07-16 11:19:04 -0400327 sk_sp<GrTextBlob> cacheBlob;
328 GrTextBlob::Key key;
joshualitt2a0e9f32015-04-13 06:12:21 -0700329 if (canCache) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400330 bool hasLCD = glyphRunList.anyRunsLCD();
joshualitte4cee1f2015-05-11 13:04:28 -0700331
332 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
joshualitt2c89bc12016-02-11 05:42:30 -0800333 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
Herb Derbycddab252018-07-16 11:19:04 -0400334 kUnknown_SkPixelGeometry;
joshualitte4cee1f2015-05-11 13:04:28 -0700335
joshualitt9e36c1a2015-04-14 12:17:27 -0700336 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
337 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
338 // ensure we always match the same key
339 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
Herb Derbycddab252018-07-16 11:19:04 -0400340 ComputeCanonicalColor(skPaint, hasLCD);
joshualitt9e36c1a2015-04-14 12:17:27 -0700341
joshualitte4cee1f2015-05-11 13:04:28 -0700342 key.fPixelGeometry = pixelGeometry;
Herb Derbyb935cf82018-07-26 16:54:18 -0400343 key.fUniqueID = glyphRunList.uniqueID();
joshualitt53b5f442015-04-13 06:33:59 -0700344 key.fStyle = skPaint.getStyle();
345 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700346 key.fCanonicalColor = canonicalColor;
brianosman8d7ffce2016-04-21 08:29:06 -0700347 key.fScalerContextFlags = scalerContextFlags;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500348 cacheBlob = textBlobCache->find(key);
joshualitt2a0e9f32015-04-13 06:12:21 -0700349 }
350
Brian Salomonf18b1d82017-10-27 11:30:49 -0400351 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
joshualittb7133be2015-04-08 09:08:31 -0700352 if (cacheBlob) {
Herb Derbycddab252018-07-16 11:19:04 -0400353 if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, origin.x(), origin.y())) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700354 // We have to remake the blob because changes may invalidate our masks.
355 // TODO we could probably get away reuse most of the time if the pointer is unique,
356 // but we'd have to clear the subrun information
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500357 textBlobCache->remove(cacheBlob.get());
Herb Derbycddab252018-07-16 11:19:04 -0400358 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
359 this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400360 *context->contextPriv().caps()->shaderCaps(), paint,
Herb Derby74c6ed32018-07-28 18:07:54 -0400361 scalerContextFlags, viewMatrix, props, glyphRunList,
362 target->glyphDrawer());
joshualittb7133be2015-04-08 09:08:31 -0700363 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500364 textBlobCache->makeMRU(cacheBlob.get());
joshualitt2f2ee832016-02-10 08:52:24 -0800365
366 if (CACHE_SANITY_CHECK) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400367 int glyphCount = glyphRunList.totalGlyphCount();
368 int runCount = glyphRunList.runCount();
Herb Derby86240592018-05-24 16:12:31 -0400369 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
joshualitt92303772016-02-10 11:55:52 -0800370 sanityBlob->setupKey(key, blurRec, skPaint);
Herb Derbycddab252018-07-16 11:19:04 -0400371 this->regenerateGlyphRunList(
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400372 sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(),
Herb Derby74c6ed32018-07-28 18:07:54 -0400373 paint, scalerContextFlags, viewMatrix, props, glyphRunList,
374 target->glyphDrawer());
Herb Derby86240592018-05-24 16:12:31 -0400375 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700376 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700377 }
378 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700379 if (canCache) {
Herb Derbycddab252018-07-16 11:19:04 -0400380 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
joshualitt2a0e9f32015-04-13 06:12:21 -0700381 } else {
Herb Derbycddab252018-07-16 11:19:04 -0400382 cacheBlob = textBlobCache->makeBlob(glyphRunList);
joshualitt2a0e9f32015-04-13 06:12:21 -0700383 }
Herb Derbycddab252018-07-16 11:19:04 -0400384 this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400385 *context->contextPriv().caps()->shaderCaps(), paint,
Herb Derby74c6ed32018-07-28 18:07:54 -0400386 scalerContextFlags, viewMatrix, props, glyphRunList,
387 target->glyphDrawer());
joshualitt1d89e8d2015-04-01 12:40:54 -0700388 }
389
Robert Phillips5a66efb2018-03-07 15:13:18 -0500390 cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint,
Herb Derbycddab252018-07-16 11:19:04 -0400391 clip, viewMatrix, clipBounds, origin.x(), origin.y());
joshualitt1d89e8d2015-04-01 12:40:54 -0700392}
393
Herb Derby86240592018-05-24 16:12:31 -0400394inline sk_sp<GrTextBlob>
Herb Derby26cbe512018-05-24 14:39:01 -0400395GrTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache,
396 GrGlyphCache* glyphCache,
397 const GrShaderCaps& shaderCaps,
398 const GrTextUtils::Paint& paint,
399 SkScalerContextFlags scalerContextFlags,
400 const SkMatrix& viewMatrix,
401 const SkSurfaceProps& props,
402 const char text[], size_t byteLength,
403 const SkScalar pos[], int scalarsPerPosition, const
404 SkPoint& offset) const {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500405 int glyphCount = paint.skPaint().countText(text, byteLength);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400406 if (!glyphCount) {
407 return nullptr;
408 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700409
Herb Derby86240592018-05-24 16:12:31 -0400410 sk_sp<GrTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
joshualitt7481e752016-01-22 06:08:48 -0800411 blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
Jim Van Verth54d9c882018-02-08 16:14:48 -0500412 blob->setRunPaintFlags(0, paint.skPaint().getFlags());
joshualitt9bd2daf2015-04-17 09:30:06 -0700413
Khushal3e7548c2018-05-23 15:45:01 -0700414 if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps.supportsDistanceFieldText(),
415 fOptions)) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500416 this->drawDFPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
Brian Salomonaf597482017-11-07 16:23:34 -0500417 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700418 } else {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500419 DrawBmpPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
420 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700421 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700422 return blob;
423}
424
Herb Derby26cbe512018-05-24 14:39:01 -0400425void GrTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target,
426 const GrClip& clip, const SkPaint& skPaint,
427 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
428 const char text[], size_t byteLength, const SkScalar pos[],
429 int scalarsPerPosition, const SkPoint& offset,
430 const SkIRect& regionClipBounds) {
Brian Salomonf18b1d82017-10-27 11:30:49 -0400431 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
Khushalc421ca12018-06-26 14:38:34 -0700432 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800433 return;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500434 }
435
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500436 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500437 auto textBlobCache = context->contextPriv().getTextBlobCache();
438
Herb Derby86240592018-05-24 16:12:31 -0400439 sk_sp<GrTextBlob> blob(this->makeDrawPosTextBlob(
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400440 textBlobCache, glyphCache, *context->contextPriv().caps()->shaderCaps(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500441 ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
442 byteLength, pos, scalarsPerPosition, offset));
443 if (blob) {
Robert Phillips5a66efb2018-03-07 15:13:18 -0500444 blob->flush(target, props, fDistanceAdjustTable.get(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500445 clip, viewMatrix, regionClipBounds, offset.fX, offset.fY);
joshualitte55750e2016-02-10 12:52:21 -0800446 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700447}
448
Herb Derby86240592018-05-24 16:12:31 -0400449void GrTextContext::DrawBmpPosText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400450 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
451 const GrTextUtils::Paint& paint,
452 SkScalerContextFlags scalerContextFlags,
453 const SkMatrix& viewMatrix,
454 const char text[], size_t byteLength, const SkScalar pos[],
455 int scalarsPerPosition, const SkPoint& offset) {
Brian Salomon52db9402017-11-07 14:58:55 -0500456 SkASSERT(byteLength == 0 || text != nullptr);
457 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
458
459 // nothing to draw
460 if (text == nullptr || byteLength == 0) {
461 return;
462 }
463
464 // Ensure the blob is set for bitmaptext
465 blob->setHasBitmap();
466
Jim Van Verth54d9c882018-02-08 16:14:48 -0500467 if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500468 DrawBmpPosTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500469 viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500470 return;
471 }
472
Herb Derbyab6fd7e2018-03-07 18:05:39 +0000473 sk_sp<GrTextStrike> currStrike;
Herb Derby526819d2018-03-09 12:51:12 -0500474 auto cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
Brian Salomon52db9402017-11-07 14:58:55 -0500475 SkFindAndPlaceGlyph::ProcessPosText(
476 paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
Herb Derby1e7c6582018-05-21 16:10:17 -0400477 scalarsPerPosition, cache.get(),
Brian Salomon52db9402017-11-07 14:58:55 -0500478 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
479 position += rounding;
Herb Derby049b5d92018-08-01 13:56:26 -0400480 AppendGlyph(blob, runIndex, glyphCache, &currStrike,
481 glyph, GrGlyph::kCoverage_MaskStyle,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500482 SkScalarFloorToScalar(position.fX),
483 SkScalarFloorToScalar(position.fY),
Jim Van Verthb515ae72018-05-23 16:44:55 -0400484 paint.filteredPremulColor(), cache.get(), SK_Scalar1, false);
Brian Salomon52db9402017-11-07 14:58:55 -0500485 });
Brian Salomon52db9402017-11-07 14:58:55 -0500486}
487
Herb Derby86240592018-05-24 16:12:31 -0400488void GrTextContext::DrawBmpPosTextAsPaths(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400489 GrGlyphCache* glyphCache,
490 const SkSurfaceProps& props,
491 const GrTextUtils::Paint& origPaint,
492 SkScalerContextFlags scalerContextFlags,
493 const SkMatrix& viewMatrix,
494 const char text[], size_t byteLength,
495 const SkScalar pos[], int scalarsPerPosition,
496 const SkPoint& offset) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500497 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
498
499 // nothing to draw
500 if (text == nullptr || byteLength == 0) {
501 return;
502 }
503
504 // setup our std paint, in hopes of getting hits in the cache
Jim Van Verthc401bb92018-02-15 14:05:24 -0500505 SkPaint pathPaint(origPaint);
506 SkScalar matrixScale = pathPaint.setupForAsPaths();
Khushalfa8ff092018-06-06 17:46:38 -0700507 FallbackTextHelper fallbackTextHelper(viewMatrix, origPaint, glyphCache->getGlyphSizeLimit(),
508 matrixScale);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500509
510 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
Jim Van Verthc401bb92018-02-15 14:05:24 -0500511 pathPaint.setStyle(SkPaint::kFill_Style);
512 pathPaint.setPathEffect(nullptr);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500513
Jim Van Verthc401bb92018-02-15 14:05:24 -0500514 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(pathPaint.getTextEncoding(),
Jim Van Verthc401bb92018-02-15 14:05:24 -0500515 true);
Herb Derbyfa996902018-04-18 11:36:12 -0400516 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
Herb Derby4e34a012018-03-21 10:47:30 -0400517 pathPaint, &props, SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500518
519 const char* stop = text + byteLength;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500520 const char* lastText = text;
Jim Van Verth54d9c882018-02-08 16:14:48 -0500521 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
522
523 while (text < stop) {
Hal Canary4014ba62018-07-24 11:33:21 -0400524 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text, stop);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500525 if (glyph.fWidth) {
Jim Van Verthc401bb92018-02-15 14:05:24 -0500526 SkPoint loc;
Herb Derby1e7c6582018-05-21 16:10:17 -0400527 tmsProc(pos, &loc);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500528 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
529 fallbackTextHelper.appendText(glyph, text - lastText, lastText, loc);
530 } else {
531 const SkPath* path = cache->findPath(glyph);
532 if (path) {
533 blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
534 }
Jim Van Verth54d9c882018-02-08 16:14:48 -0500535 }
536 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500537 lastText = text;
Jim Van Verth54d9c882018-02-08 16:14:48 -0500538 pos += scalarsPerPosition;
539 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500540
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500541 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500542}
543
Herb Derby26cbe512018-05-24 14:39:01 -0400544void GrTextContext::SanitizeOptions(Options* options) {
Khushal3e7548c2018-05-23 15:45:01 -0700545 if (options->fMaxDistanceFieldFontSize < 0.f) {
546 options->fMaxDistanceFieldFontSize = kDefaultMaxDistanceFieldFontSize;
547 }
548 if (options->fMinDistanceFieldFontSize < 0.f) {
549 options->fMinDistanceFieldFontSize = kDefaultMinDistanceFieldFontSize;
550 }
551}
552
Herb Derby26cbe512018-05-24 14:39:01 -0400553bool GrTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
554 const SkSurfaceProps& props,
555 bool contextSupportsDistanceFieldText,
556 const Options& options) {
Brian Salomon52db9402017-11-07 14:58:55 -0500557 if (!viewMatrix.hasPerspective()) {
558 SkScalar maxScale = viewMatrix.getMaxScale();
559 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
560 // Hinted text looks far better at small resolutions
561 // Scaling up beyond 2x yields undesireable artifacts
Khushal3e7548c2018-05-23 15:45:01 -0700562 if (scaledTextSize < options.fMinDistanceFieldFontSize ||
563 scaledTextSize > options.fMaxDistanceFieldFontSize) {
Brian Salomon52db9402017-11-07 14:58:55 -0500564 return false;
565 }
566
567 bool useDFT = props.isUseDeviceIndependentFonts();
568#if SK_FORCE_DISTANCE_FIELD_TEXT
569 useDFT = true;
570#endif
571
572 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
573 return false;
574 }
575 }
576
Mike Reed8ad91a92018-01-19 19:09:32 -0500577 // mask filters modify alpha, which doesn't translate well to distance
Khushal3e7548c2018-05-23 15:45:01 -0700578 if (skPaint.getMaskFilter() || !contextSupportsDistanceFieldText) {
Brian Salomon52db9402017-11-07 14:58:55 -0500579 return false;
580 }
581
582 // TODO: add some stroking support
583 if (skPaint.getStyle() != SkPaint::kFill_Style) {
584 return false;
585 }
586
587 return true;
588}
589
Herb Derby86240592018-05-24 16:12:31 -0400590void GrTextContext::InitDistanceFieldPaint(GrTextBlob* blob,
Herb Derby26cbe512018-05-24 14:39:01 -0400591 SkPaint* skPaint,
592 const SkMatrix& viewMatrix,
593 const Options& options,
594 SkScalar* textRatio,
595 SkScalerContextFlags* flags) {
Brian Salomon52db9402017-11-07 14:58:55 -0500596 SkScalar textSize = skPaint->getTextSize();
597 SkScalar scaledTextSize = textSize;
598
599 if (viewMatrix.hasPerspective()) {
600 // for perspective, we simply force to the medium size
601 // TODO: compute a size based on approximate screen area
602 scaledTextSize = kMediumDFFontLimit;
603 } else {
604 SkScalar maxScale = viewMatrix.getMaxScale();
605 // if we have non-unity scale, we need to choose our base text size
606 // based on the SkPaint's text size multiplied by the max scale factor
607 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
608 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
609 scaledTextSize *= maxScale;
610 }
611 }
612
613 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
614 // and ceiling. A scale outside of this range would require regenerating the distance fields
615 SkScalar dfMaskScaleFloor;
616 SkScalar dfMaskScaleCeil;
617 if (scaledTextSize <= kSmallDFFontLimit) {
Khushal3e7548c2018-05-23 15:45:01 -0700618 dfMaskScaleFloor = options.fMinDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500619 dfMaskScaleCeil = kSmallDFFontLimit;
620 *textRatio = textSize / kSmallDFFontSize;
621 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
622 } else if (scaledTextSize <= kMediumDFFontLimit) {
623 dfMaskScaleFloor = kSmallDFFontLimit;
624 dfMaskScaleCeil = kMediumDFFontLimit;
625 *textRatio = textSize / kMediumDFFontSize;
626 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
627 } else {
628 dfMaskScaleFloor = kMediumDFFontLimit;
Khushal3e7548c2018-05-23 15:45:01 -0700629 dfMaskScaleCeil = options.fMaxDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500630 *textRatio = textSize / kLargeDFFontSize;
631 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
632 }
633
634 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
635 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
636 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
637 // tolerate before we'd have to move to a large mip size. When we actually test these values
638 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
639 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
640 // level)
641 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
Khushal3e7548c2018-05-23 15:45:01 -0700642 if (blob) {
643 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize,
644 dfMaskScaleCeil / scaledTextSize);
645 }
Brian Salomon52db9402017-11-07 14:58:55 -0500646
647 skPaint->setAntiAlias(true);
648 skPaint->setLCDRenderText(false);
649 skPaint->setAutohinted(false);
650 skPaint->setHinting(SkPaint::kNormal_Hinting);
651 skPaint->setSubpixelText(true);
Jim Van Verthd401da62018-05-03 10:40:30 -0400652
653 skPaint->setMaskFilter(GrSDFMaskFilter::Make());
Khushal3e7548c2018-05-23 15:45:01 -0700654
655 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
656 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
657 *flags = SkScalerContextFlags::kNone;
Brian Salomon52db9402017-11-07 14:58:55 -0500658}
659
Herb Derby86240592018-05-24 16:12:31 -0400660void GrTextContext::drawDFPosText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400661 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
662 const GrTextUtils::Paint& paint,
663 SkScalerContextFlags scalerContextFlags,
664 const SkMatrix& viewMatrix, const char text[],
665 size_t byteLength, const SkScalar pos[],
666 int scalarsPerPosition, const SkPoint& offset) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500667 SkASSERT(byteLength == 0 || text != nullptr);
668 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
669
670 // nothing to draw
671 if (text == nullptr || byteLength == 0) {
672 return;
673 }
674
Khushal3e7548c2018-05-23 15:45:01 -0700675 bool hasWCoord = viewMatrix.hasPerspective() || fOptions.fDistanceFieldVerticesAlwaysHaveW;
Brian Salomonb5086962017-12-13 10:59:33 -0500676
Brian Salomon52db9402017-11-07 14:58:55 -0500677 // Setup distance field paint and text ratio
678 SkScalar textRatio;
679 SkPaint dfPaint(paint);
Khushal3e7548c2018-05-23 15:45:01 -0700680 SkScalerContextFlags flags;
681 InitDistanceFieldPaint(blob, &dfPaint, viewMatrix, fOptions, &textRatio, &flags);
Brian Salomon52db9402017-11-07 14:58:55 -0500682 blob->setHasDistanceField();
683 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
Brian Salomon5c6ac642017-12-19 11:09:32 -0500684 paint.skPaint().isAntiAlias(), hasWCoord);
Brian Salomon52db9402017-11-07 14:58:55 -0500685
Khushalfa8ff092018-06-06 17:46:38 -0700686 FallbackTextHelper fallbackTextHelper(viewMatrix, paint, glyphCache->getGlyphSizeLimit(),
687 textRatio);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500688
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500689 sk_sp<GrTextStrike> currStrike;
Brian Salomon52db9402017-11-07 14:58:55 -0500690
Herb Derby526819d2018-03-09 12:51:12 -0500691 {
Khushal3e7548c2018-05-23 15:45:01 -0700692 auto cache = blob->setupCache(runIndex, props, flags, dfPaint, nullptr);
Herb Derby526819d2018-03-09 12:51:12 -0500693 SkPaint::GlyphCacheProc glyphCacheProc =
Herb Derbyfcac00f2018-05-01 11:57:56 -0400694 SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), true);
Brian Salomon52db9402017-11-07 14:58:55 -0500695
Herb Derby526819d2018-03-09 12:51:12 -0500696 const char* stop = text + byteLength;
Brian Salomon52db9402017-11-07 14:58:55 -0500697
Herb Derby526819d2018-03-09 12:51:12 -0500698 while (text < stop) {
699 const char* lastText = text;
700 // the last 2 parameters are ignored
Hal Canary4014ba62018-07-24 11:33:21 -0400701 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text, stop);
Brian Salomon52db9402017-11-07 14:58:55 -0500702
Herb Derby526819d2018-03-09 12:51:12 -0500703 if (glyph.fWidth) {
704 SkPoint glyphPos(offset);
Herb Derby1e7c6582018-05-21 16:10:17 -0400705 glyphPos.fX += pos[0];
706 glyphPos.fY += (2 == scalarsPerPosition ? pos[1] : 0);
Jim Van Verthf4c13162018-01-11 16:40:24 -0500707
Jim Van Verthd401da62018-05-03 10:40:30 -0400708 if (glyph.fMaskFormat == SkMask::kSDF_Format) {
Herb Derby049b5d92018-08-01 13:56:26 -0400709 AppendGlyph(blob, runIndex, glyphCache, &currStrike,
710 glyph, GrGlyph::kDistance_MaskStyle,
711 glyphPos.fX, glyphPos.fY, paint.filteredPremulColor(),
712 cache.get(), textRatio, true);
Herb Derby526819d2018-03-09 12:51:12 -0500713 } else {
Jim Van Verthd401da62018-05-03 10:40:30 -0400714 // can't append non-SDF glyph to SDF batch, send to fallback
Herb Derby526819d2018-03-09 12:51:12 -0500715 fallbackTextHelper.appendText(glyph, SkToInt(text - lastText), lastText,
716 glyphPos);
717 }
Brian Salomon52db9402017-11-07 14:58:55 -0500718 }
Herb Derby526819d2018-03-09 12:51:12 -0500719 pos += scalarsPerPosition;
Brian Salomon52db9402017-11-07 14:58:55 -0500720 }
Brian Salomon52db9402017-11-07 14:58:55 -0500721 }
Herb Derbyab6fd7e2018-03-07 18:05:39 +0000722
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500723 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, paint, scalerContextFlags);
Brian Salomon52db9402017-11-07 14:58:55 -0500724}
725
joshualitt79dfb2b2015-05-11 08:58:08 -0700726///////////////////////////////////////////////////////////////////////////////////////////////////
727
Herb Derby26cbe512018-05-24 14:39:01 -0400728void GrTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, int count,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500729 const char* text, SkPoint glyphPos) {
Jim Van Verthb515ae72018-05-23 16:44:55 -0400730 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
Khushalfa8ff092018-06-06 17:46:38 -0700731 if (SkScalarNearlyZero(maxDim)) return;
732
Jim Van Verthb515ae72018-05-23 16:44:55 -0400733 if (!fUseTransformedFallback) {
734 if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
735 fUseTransformedFallback = true;
Jim Van Verthcf838c72018-03-05 14:40:36 -0500736 fMaxTextSize -= 2; // Subtract 2 to account for the bilerp pad around the glyph
Jim Van Verthc401bb92018-02-15 14:05:24 -0500737 }
738 }
739
740 fFallbackTxt.append(count, text);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400741 if (fUseTransformedFallback) {
Jim Van Verth080a9282018-03-02 10:41:43 -0500742 // If there's a glyph in the font that's particularly large, it's possible
743 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
Jim Van Verth8b7284d2018-05-17 12:33:52 -0400744 // that glyph than make the others blurry, so we set a minimum size of half the
Jim Van Verth080a9282018-03-02 10:41:43 -0500745 // maximum text size to avoid this case.
Jim Van Verthb515ae72018-05-23 16:44:55 -0400746 SkScalar glyphTextSize = SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim),
Jim Van Verth080a9282018-03-02 10:41:43 -0500747 0.5f*fMaxTextSize);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400748 fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500749 }
750 *fFallbackPos.append() = glyphPos;
751}
752
Herb Derby86240592018-05-24 16:12:31 -0400753void GrTextContext::FallbackTextHelper::drawText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400754 GrGlyphCache* glyphCache,
755 const SkSurfaceProps& props,
756 const GrTextUtils::Paint& paint,
757 SkScalerContextFlags scalerContextFlags) {
Jim Van Verthc401bb92018-02-15 14:05:24 -0500758 if (fFallbackTxt.count()) {
759 blob->initOverride(runIndex);
760 blob->setHasBitmap();
Jim Van Verthb515ae72018-05-23 16:44:55 -0400761 blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
Herb Derby526819d2018-03-09 12:51:12 -0500762 SkExclusiveStrikePtr cache;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500763 const SkPaint& skPaint = paint.skPaint();
764 SkPaint::GlyphCacheProc glyphCacheProc =
Herb Derbyfcac00f2018-05-01 11:57:56 -0400765 SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), true);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500766 SkColor textColor = paint.filteredPremulColor();
Khushalfa8ff092018-06-06 17:46:38 -0700767
Jim Van Verthc401bb92018-02-15 14:05:24 -0500768 SkScalar textRatio = SK_Scalar1;
Khushalfa8ff092018-06-06 17:46:38 -0700769 SkPaint fallbackPaint(skPaint);
770 SkMatrix matrix = fViewMatrix;
771 this->initializeForDraw(&fallbackPaint, &textRatio, &matrix);
772 cache = blob->setupCache(runIndex, props, scalerContextFlags, fallbackPaint, &matrix);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500773
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500774 sk_sp<GrTextStrike> currStrike;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500775 const char* text = fFallbackTxt.begin();
776 const char* stop = text + fFallbackTxt.count();
777 SkPoint* glyphPos = fFallbackPos.begin();
778 while (text < stop) {
Hal Canary4014ba62018-07-24 11:33:21 -0400779 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text, stop);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400780 if (!fUseTransformedFallback) {
781 fViewMatrix.mapPoints(glyphPos, 1);
Jim Van Verth76e85162018-03-29 13:46:56 -0400782 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
783 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
784 }
Herb Derby049b5d92018-08-01 13:56:26 -0400785 GrTextContext::AppendGlyph(blob, runIndex, glyphCache, &currStrike,
786 glyph, GrGlyph::kCoverage_MaskStyle,
787 glyphPos->fX, glyphPos->fY, textColor,
788 cache.get(), textRatio, fUseTransformedFallback);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500789 glyphPos++;
790 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500791 }
792}
793
Khushalfa8ff092018-06-06 17:46:38 -0700794void GrTextContext::FallbackTextHelper::initializeForDraw(SkPaint* paint, SkScalar* textRatio,
795 SkMatrix* matrix) const {
796 if (!fUseTransformedFallback) return;
797
798 paint->setTextSize(fTransformedFallbackTextSize);
799 *textRatio = fTextSize / fTransformedFallbackTextSize;
800 *matrix = SkMatrix::I();
801}
802
Jim Van Verthc401bb92018-02-15 14:05:24 -0500803///////////////////////////////////////////////////////////////////////////////////////////////////
804
Herb Derbyf4f6bbf2018-07-27 11:58:37 -0400805void GrTextContext::FallbackGlyphRunHelper::appendText(
806 const SkGlyph& glyph, SkGlyphID glyphID, SkPoint glyphPos) {
807 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
808 if (SkScalarNearlyZero(maxDim)) return;
809
810 if (!fUseTransformedFallback) {
811 if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
812 fUseTransformedFallback = true;
813 fMaxTextSize -= 2; // Subtract 2 to account for the bilerp pad around the glyph
814 }
815 }
816
817 fFallbackTxt.push_back(glyphID);
818 if (fUseTransformedFallback) {
819 // If there's a glyph in the font that's particularly large, it's possible
820 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
821 // that glyph than make the others blurry, so we set a minimum size of half the
822 // maximum text size to avoid this case.
823 SkScalar glyphTextSize =
824 SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim), 0.5f*fMaxTextSize);
825 fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
826 }
827 fFallbackPos.push_back(glyphPos);
828}
829
830void GrTextContext::FallbackGlyphRunHelper::drawText(
831 GrTextBlob* blob, int runIndex, GrGlyphCache* glyphCache, const SkSurfaceProps& props,
832 const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags) {
833 if (!fFallbackTxt.empty()) {
834 blob->initOverride(runIndex);
835 blob->setHasBitmap();
836 blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
837 const SkPaint& skPaint = paint.skPaint();
838 SkColor textColor = paint.filteredPremulColor();
839
840 SkScalar textRatio = SK_Scalar1;
841 SkPaint fallbackPaint(skPaint);
842 SkMatrix matrix = fViewMatrix;
843 this->initializeForDraw(&fallbackPaint, &textRatio, &matrix);
844 SkExclusiveStrikePtr cache =
845 blob->setupCache(runIndex, props, scalerContextFlags, fallbackPaint, &matrix);
846
847 sk_sp<GrTextStrike> currStrike;
848 auto glyphPos = fFallbackPos.begin();
849 for (auto glyphID : fFallbackTxt) {
850 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID);
851 if (!fUseTransformedFallback) {
852 fViewMatrix.mapPoints(&*glyphPos, 1);
853 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
854 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
855 }
Herb Derby049b5d92018-08-01 13:56:26 -0400856 GrTextContext::AppendGlyph(blob, runIndex, glyphCache, &currStrike,
857 glyph, GrGlyph::kCoverage_MaskStyle,
Herb Derbyf4f6bbf2018-07-27 11:58:37 -0400858 glyphPos->fX, glyphPos->fY, textColor,
859 cache.get(), textRatio, fUseTransformedFallback);
860 glyphPos++;
861 }
862 }
863}
864
865void GrTextContext::FallbackGlyphRunHelper::initializeForDraw(
866 SkPaint* paint, SkScalar* textRatio, SkMatrix* matrix) const {
867 if (!fUseTransformedFallback) return;
868
869 paint->setTextSize(fTransformedFallbackTextSize);
870 *textRatio = fTextSize / fTransformedFallbackTextSize;
871 *matrix = SkMatrix::I();
872}
873
874///////////////////////////////////////////////////////////////////////////////////////////////////
875
876
Hal Canary6f6961e2017-01-31 13:50:44 -0500877#if GR_TEST_UTILS
joshualitt79dfb2b2015-05-11 08:58:08 -0700878
Brian Salomonf18b1d82017-10-27 11:30:49 -0400879#include "GrRenderTargetContext.h"
880
Herb Derby26cbe512018-05-24 14:39:01 -0400881std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context,
882 GrTextContext* textContext,
883 GrRenderTargetContext* rtc,
884 const SkPaint& skPaint,
885 const SkMatrix& viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -0400886 const char* text,
887 int x,
888 int y) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500889 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500890
891 static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
892
893 size_t textLen = (int)strlen(text);
894
895 GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
896
897 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
898 // test the text op with this unit test, that is okay.
Herb Derby41f4f312018-06-06 17:45:53 +0000899
900 auto origin = SkPoint::Make(x, y);
Herb Derby59d997a2018-06-07 12:44:09 -0400901 SkGlyphRunBuilder builder;
Herb Derbyc434ade2018-07-11 16:07:01 -0400902 builder.drawText(skPaint, text, textLen, origin);
Herb Derby59d997a2018-06-07 12:44:09 -0400903 sk_sp<GrTextBlob> blob;
Herb Derby41f4f312018-06-06 17:45:53 +0000904
Herb Derby8a6348e2018-07-12 15:30:35 -0400905 auto glyphRunList = builder.useGlyphRunList();
Herb Derbyb935cf82018-07-26 16:54:18 -0400906 if (!glyphRunList.empty()) {
907 auto glyphRun = glyphRunList[0];
Herb Derby8a6348e2018-07-12 15:30:35 -0400908 // Use the text and textLen below, because we don't want to mess with the paint.
909 glyphRun.temporaryShuntToCallback(
Herb Derby59d997a2018-06-07 12:44:09 -0400910 [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
911 blob = textContext->makeDrawPosTextBlob(
912 context->contextPriv().getTextBlobCache(), glyphCache,
913 *context->contextPriv().caps()->shaderCaps(), utilsPaint,
Herb Derby8a6348e2018-07-12 15:30:35 -0400914 GrTextContext::kTextBlobOpScalerContextFlags, viewMatrix, surfaceProps,
Herb Derbycddab252018-07-16 11:19:04 -0400915 text, textLen, pos, 2, origin);
Herb Derby59d997a2018-06-07 12:44:09 -0400916 });
Herb Derby8a6348e2018-07-12 15:30:35 -0400917 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500918
919 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, surfaceProps,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500920 textContext->dfAdjustTable(), rtc->textTarget());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500921}
922
Brian Salomon44acb5b2017-07-18 19:59:24 -0400923GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
joshualitt79dfb2b2015-05-11 08:58:08 -0700924 static uint32_t gContextID = SK_InvalidGenID;
Herb Derby26cbe512018-05-24 14:39:01 -0400925 static std::unique_ptr<GrTextContext> gTextContext;
robertphillipsfcf78292015-06-19 11:49:52 -0700926 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -0700927
928 if (context->uniqueID() != gContextID) {
929 gContextID = context->uniqueID();
Herb Derby26cbe512018-05-24 14:39:01 -0400930 gTextContext = GrTextContext::Make(GrTextContext::Options());
joshualitt79dfb2b2015-05-11 08:58:08 -0700931 }
932
Brian Osman11052242016-10-27 14:47:55 -0400933 // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500934 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Brian Osman11052242016-10-27 14:47:55 -0400935 SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
brianosman8fe485b2016-07-25 12:31:51 -0700936
joshualitt6c891102015-05-13 08:51:49 -0700937 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
Brian Salomon44acb5b2017-07-18 19:59:24 -0400938
939 // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
940 // param.
joshualitt79dfb2b2015-05-11 08:58:08 -0700941 SkPaint skPaint;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500942 skPaint.setColor(random->nextU());
joshualitt79dfb2b2015-05-11 08:58:08 -0700943 skPaint.setLCDRenderText(random->nextBool());
944 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
945 skPaint.setSubpixelText(random->nextBool());
946
joshualitt79dfb2b2015-05-11 08:58:08 -0700947 const char* text = "The quick brown fox jumps over the lazy dog.";
joshualitt79dfb2b2015-05-11 08:58:08 -0700948
joshualittb8d86492016-02-24 09:23:03 -0800949 // create some random x/y offsets, including negative offsets
950 static const int kMaxTrans = 1024;
951 int xPos = (random->nextU() % 2) * 2 - 1;
952 int yPos = (random->nextU() % 2) * 2 - 1;
953 int xInt = (random->nextU() % kMaxTrans) * xPos;
954 int yInt = (random->nextU() % kMaxTrans) * yPos;
halcanary9d524f22016-03-29 09:03:52 -0700955
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500956 return gTextContext->createOp_TestingOnly(context, gTextContext.get(), rtc.get(),
957 skPaint, viewMatrix, text, xInt, yInt);
joshualitt79dfb2b2015-05-11 08:58:08 -0700958}
959
960#endif