blob: 943ce8cb4e56bad5376c814d37dcb8479b9c6a8b [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
Herb Derby82c11e02018-08-06 16:40:12 -0400126 // Regenerate GrTextBlob
Herb Derby12e42562018-07-28 14:27:48 -0400127 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
Herb Derby82c11e02018-08-06 16:40:12 -0400189 fallbackTextHelper.appendGlyph(glyph, glyphID, glyphPos);
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400190 }
191 }
192 }
193 }
194
Herb Derby82c11e02018-08-06 16:40:12 -0400195 fallbackTextHelper.drawGlyphs(
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400196 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) {
Herb Derby82c11e02018-08-06 16:40:12 -0400220 fallbackTextHelper.appendGlyph(glyph, glyph.getGlyphID(), position);
Herb Derby0ab5ce12018-07-30 10:10:40 -0400221 } 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
Herb Derby82c11e02018-08-06 16:40:12 -0400231 fallbackTextHelper.drawGlyphs(
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400232 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
Herb Derby9a52a392018-08-01 21:13:23 -0400241 auto perGlyph =
Herb Derby74c6ed32018-07-28 18:07:54 -0400242 [cacheBlob, runIndex, glyphCache, &currStrike, runPaint, cache{cache.get()}]
Herb Derby9a52a392018-08-01 21:13:23 -0400243 (const SkGlyph& glyph, SkPoint mappedPt) {
244 SkScalar sx = SkScalarFloorToScalar(mappedPt.fX),
245 sy = SkScalarFloorToScalar(mappedPt.fY);
246 AppendGlyph(cacheBlob, runIndex, glyphCache, &currStrike,
247 glyph, GrGlyph::kCoverage_MaskStyle, sx, sy,
248 runPaint.filteredPremulColor(), cache, SK_Scalar1, false);
Herb Derby74c6ed32018-07-28 18:07:54 -0400249 };
250
Herb Derby9a52a392018-08-01 21:13:23 -0400251 auto perPath =
252 [cacheBlob, runIndex]
253 (const SkPath* path, const SkGlyph& glyph, SkPoint position) {
254 SkScalar sx = SkScalarFloorToScalar(position.fX),
255 sy = SkScalarFloorToScalar(position.fY);
256 cacheBlob->appendPathGlyph(
257 runIndex, *path, sx, sy, SK_Scalar1, true);
258 };
259
260 glyphDrawer->drawGlyphRunAsGlyphWithPathFallback(
261 cache.get(), glyphRun, origin, viewMatrix, perGlyph, perPath);
Herb Derby12e42562018-07-28 14:27:48 -0400262 }
Herb Derbyf9dfbc32018-07-28 16:16:56 -0400263 runIndex += 1;
Herb Derby12e42562018-07-28 14:27:48 -0400264 }
265}
266
Herb Derbye42dc2d2018-08-01 14:49:26 -0400267void GrTextContext::AppendGlyph(GrTextBlob* blob, int runIndex,
268 GrGlyphCache* grGlyphCache,
269 sk_sp<GrTextStrike>* strike,
270 const SkGlyph& skGlyph, GrGlyph::MaskStyle maskStyle,
271 SkScalar sx, SkScalar sy,
272 GrColor color, SkGlyphCache* skGlyphCache,
273 SkScalar textRatio, bool needsTransform) {
274 if (!*strike) {
275 *strike = grGlyphCache->getStrike(skGlyphCache);
276 }
277
278 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
279 skGlyph.getSubXFixed(),
280 skGlyph.getSubYFixed(),
281 maskStyle);
282 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
283 if (!glyph) {
284 return;
285 }
286
287 SkASSERT(skGlyph.fWidth == glyph->width());
288 SkASSERT(skGlyph.fHeight == glyph->height());
289
290 SkRect glyphRect = rect_to_draw(skGlyph, {sx, sy}, textRatio, maskStyle);
291
292 if (!glyphRect.isEmpty()) {
293 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph,
294 skGlyphCache, skGlyph, sx, sy, textRatio, !needsTransform);
295 }
296}
297
Herb Derbycddab252018-07-16 11:19:04 -0400298void GrTextContext::drawGlyphRunList(
299 GrContext* context, GrTextUtils::Target* target, const GrClip& clip,
Herb Derbyb935cf82018-07-26 16:54:18 -0400300 const SkMatrix& viewMatrix, const SkSurfaceProps& props, const SkGlyphRunList& glyphRunList,
Herb Derbycddab252018-07-16 11:19:04 -0400301 const SkIRect& clipBounds) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400302 SkPoint origin = glyphRunList.origin();
joshualitt9e36c1a2015-04-14 12:17:27 -0700303
Herb Derbycddab252018-07-16 11:19:04 -0400304 // Get the first paint to use as the key paint.
Herb Derbyb935cf82018-07-26 16:54:18 -0400305 const SkPaint& skPaint = glyphRunList.paint();
Herb Derbycddab252018-07-16 11:19:04 -0400306
joshualitt9b8e79e2015-04-24 09:57:12 -0700307 // If we have been abandoned, then don't draw
Khushalc421ca12018-06-26 14:38:34 -0700308 if (context->abandoned()) {
robertphillipsea461502015-05-26 11:38:03 -0700309 return;
310 }
311
Mike Reed80747ef2018-01-23 15:29:32 -0500312 SkMaskFilterBase::BlurRec blurRec;
joshualitt53b5f442015-04-13 06:33:59 -0700313 // It might be worth caching these things, but its not clear at this time
314 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
315 const SkMaskFilter* mf = skPaint.getMaskFilter();
Herb Derbyb935cf82018-07-26 16:54:18 -0400316 bool canCache = glyphRunList.canCache() && !(skPaint.getPathEffect() ||
Herb Derbycddab252018-07-16 11:19:04 -0400317 (mf && !as_MFB(mf)->asABlur(&blurRec)));
Herb Derbyd8327a82018-01-22 14:39:27 -0500318 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
joshualitt2a0e9f32015-04-13 06:12:21 -0700319
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500320 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500321 GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
322
Herb Derbycddab252018-07-16 11:19:04 -0400323 sk_sp<GrTextBlob> cacheBlob;
324 GrTextBlob::Key key;
joshualitt2a0e9f32015-04-13 06:12:21 -0700325 if (canCache) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400326 bool hasLCD = glyphRunList.anyRunsLCD();
joshualitte4cee1f2015-05-11 13:04:28 -0700327
328 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
joshualitt2c89bc12016-02-11 05:42:30 -0800329 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
Herb Derbycddab252018-07-16 11:19:04 -0400330 kUnknown_SkPixelGeometry;
joshualitte4cee1f2015-05-11 13:04:28 -0700331
joshualitt9e36c1a2015-04-14 12:17:27 -0700332 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
333 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
334 // ensure we always match the same key
335 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
Herb Derbycddab252018-07-16 11:19:04 -0400336 ComputeCanonicalColor(skPaint, hasLCD);
joshualitt9e36c1a2015-04-14 12:17:27 -0700337
joshualitte4cee1f2015-05-11 13:04:28 -0700338 key.fPixelGeometry = pixelGeometry;
Herb Derbyb935cf82018-07-26 16:54:18 -0400339 key.fUniqueID = glyphRunList.uniqueID();
joshualitt53b5f442015-04-13 06:33:59 -0700340 key.fStyle = skPaint.getStyle();
341 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700342 key.fCanonicalColor = canonicalColor;
brianosman8d7ffce2016-04-21 08:29:06 -0700343 key.fScalerContextFlags = scalerContextFlags;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500344 cacheBlob = textBlobCache->find(key);
joshualitt2a0e9f32015-04-13 06:12:21 -0700345 }
346
Brian Salomonf18b1d82017-10-27 11:30:49 -0400347 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
joshualittb7133be2015-04-08 09:08:31 -0700348 if (cacheBlob) {
Herb Derbycddab252018-07-16 11:19:04 -0400349 if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, origin.x(), origin.y())) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700350 // We have to remake the blob because changes may invalidate our masks.
351 // TODO we could probably get away reuse most of the time if the pointer is unique,
352 // but we'd have to clear the subrun information
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500353 textBlobCache->remove(cacheBlob.get());
Herb Derbycddab252018-07-16 11:19:04 -0400354 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
355 this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400356 *context->contextPriv().caps()->shaderCaps(), paint,
Herb Derby74c6ed32018-07-28 18:07:54 -0400357 scalerContextFlags, viewMatrix, props, glyphRunList,
358 target->glyphDrawer());
joshualittb7133be2015-04-08 09:08:31 -0700359 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500360 textBlobCache->makeMRU(cacheBlob.get());
joshualitt2f2ee832016-02-10 08:52:24 -0800361
362 if (CACHE_SANITY_CHECK) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400363 int glyphCount = glyphRunList.totalGlyphCount();
364 int runCount = glyphRunList.runCount();
Herb Derby86240592018-05-24 16:12:31 -0400365 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
joshualitt92303772016-02-10 11:55:52 -0800366 sanityBlob->setupKey(key, blurRec, skPaint);
Herb Derbycddab252018-07-16 11:19:04 -0400367 this->regenerateGlyphRunList(
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400368 sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(),
Herb Derby74c6ed32018-07-28 18:07:54 -0400369 paint, scalerContextFlags, viewMatrix, props, glyphRunList,
370 target->glyphDrawer());
Herb Derby86240592018-05-24 16:12:31 -0400371 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700372 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700373 }
374 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700375 if (canCache) {
Herb Derbycddab252018-07-16 11:19:04 -0400376 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
joshualitt2a0e9f32015-04-13 06:12:21 -0700377 } else {
Herb Derbycddab252018-07-16 11:19:04 -0400378 cacheBlob = textBlobCache->makeBlob(glyphRunList);
joshualitt2a0e9f32015-04-13 06:12:21 -0700379 }
Herb Derbycddab252018-07-16 11:19:04 -0400380 this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400381 *context->contextPriv().caps()->shaderCaps(), paint,
Herb Derby74c6ed32018-07-28 18:07:54 -0400382 scalerContextFlags, viewMatrix, props, glyphRunList,
383 target->glyphDrawer());
joshualitt1d89e8d2015-04-01 12:40:54 -0700384 }
385
Robert Phillips5a66efb2018-03-07 15:13:18 -0500386 cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint,
Herb Derbycddab252018-07-16 11:19:04 -0400387 clip, viewMatrix, clipBounds, origin.x(), origin.y());
joshualitt1d89e8d2015-04-01 12:40:54 -0700388}
389
Herb Derby26cbe512018-05-24 14:39:01 -0400390void GrTextContext::SanitizeOptions(Options* options) {
Khushal3e7548c2018-05-23 15:45:01 -0700391 if (options->fMaxDistanceFieldFontSize < 0.f) {
392 options->fMaxDistanceFieldFontSize = kDefaultMaxDistanceFieldFontSize;
393 }
394 if (options->fMinDistanceFieldFontSize < 0.f) {
395 options->fMinDistanceFieldFontSize = kDefaultMinDistanceFieldFontSize;
396 }
397}
398
Herb Derby26cbe512018-05-24 14:39:01 -0400399bool GrTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
400 const SkSurfaceProps& props,
401 bool contextSupportsDistanceFieldText,
402 const Options& options) {
Brian Salomon52db9402017-11-07 14:58:55 -0500403 if (!viewMatrix.hasPerspective()) {
404 SkScalar maxScale = viewMatrix.getMaxScale();
405 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
406 // Hinted text looks far better at small resolutions
407 // Scaling up beyond 2x yields undesireable artifacts
Khushal3e7548c2018-05-23 15:45:01 -0700408 if (scaledTextSize < options.fMinDistanceFieldFontSize ||
409 scaledTextSize > options.fMaxDistanceFieldFontSize) {
Brian Salomon52db9402017-11-07 14:58:55 -0500410 return false;
411 }
412
413 bool useDFT = props.isUseDeviceIndependentFonts();
414#if SK_FORCE_DISTANCE_FIELD_TEXT
415 useDFT = true;
416#endif
417
418 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
419 return false;
420 }
421 }
422
Mike Reed8ad91a92018-01-19 19:09:32 -0500423 // mask filters modify alpha, which doesn't translate well to distance
Khushal3e7548c2018-05-23 15:45:01 -0700424 if (skPaint.getMaskFilter() || !contextSupportsDistanceFieldText) {
Brian Salomon52db9402017-11-07 14:58:55 -0500425 return false;
426 }
427
428 // TODO: add some stroking support
429 if (skPaint.getStyle() != SkPaint::kFill_Style) {
430 return false;
431 }
432
433 return true;
434}
435
Herb Derby86240592018-05-24 16:12:31 -0400436void GrTextContext::InitDistanceFieldPaint(GrTextBlob* blob,
Herb Derby26cbe512018-05-24 14:39:01 -0400437 SkPaint* skPaint,
438 const SkMatrix& viewMatrix,
439 const Options& options,
440 SkScalar* textRatio,
441 SkScalerContextFlags* flags) {
Brian Salomon52db9402017-11-07 14:58:55 -0500442 SkScalar textSize = skPaint->getTextSize();
443 SkScalar scaledTextSize = textSize;
444
445 if (viewMatrix.hasPerspective()) {
446 // for perspective, we simply force to the medium size
447 // TODO: compute a size based on approximate screen area
448 scaledTextSize = kMediumDFFontLimit;
449 } else {
450 SkScalar maxScale = viewMatrix.getMaxScale();
451 // if we have non-unity scale, we need to choose our base text size
452 // based on the SkPaint's text size multiplied by the max scale factor
453 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
454 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
455 scaledTextSize *= maxScale;
456 }
457 }
458
459 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
460 // and ceiling. A scale outside of this range would require regenerating the distance fields
461 SkScalar dfMaskScaleFloor;
462 SkScalar dfMaskScaleCeil;
463 if (scaledTextSize <= kSmallDFFontLimit) {
Khushal3e7548c2018-05-23 15:45:01 -0700464 dfMaskScaleFloor = options.fMinDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500465 dfMaskScaleCeil = kSmallDFFontLimit;
466 *textRatio = textSize / kSmallDFFontSize;
467 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
468 } else if (scaledTextSize <= kMediumDFFontLimit) {
469 dfMaskScaleFloor = kSmallDFFontLimit;
470 dfMaskScaleCeil = kMediumDFFontLimit;
471 *textRatio = textSize / kMediumDFFontSize;
472 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
473 } else {
474 dfMaskScaleFloor = kMediumDFFontLimit;
Khushal3e7548c2018-05-23 15:45:01 -0700475 dfMaskScaleCeil = options.fMaxDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500476 *textRatio = textSize / kLargeDFFontSize;
477 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
478 }
479
480 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
481 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
482 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
483 // tolerate before we'd have to move to a large mip size. When we actually test these values
484 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
485 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
486 // level)
487 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
Khushal3e7548c2018-05-23 15:45:01 -0700488 if (blob) {
489 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize,
490 dfMaskScaleCeil / scaledTextSize);
491 }
Brian Salomon52db9402017-11-07 14:58:55 -0500492
493 skPaint->setAntiAlias(true);
494 skPaint->setLCDRenderText(false);
495 skPaint->setAutohinted(false);
496 skPaint->setHinting(SkPaint::kNormal_Hinting);
497 skPaint->setSubpixelText(true);
Jim Van Verthd401da62018-05-03 10:40:30 -0400498
499 skPaint->setMaskFilter(GrSDFMaskFilter::Make());
Khushal3e7548c2018-05-23 15:45:01 -0700500
501 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
502 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
503 *flags = SkScalerContextFlags::kNone;
Brian Salomon52db9402017-11-07 14:58:55 -0500504}
505
joshualitt79dfb2b2015-05-11 08:58:08 -0700506///////////////////////////////////////////////////////////////////////////////////////////////////
507
Herb Derby82c11e02018-08-06 16:40:12 -0400508void GrTextContext::FallbackGlyphRunHelper::appendGlyph(
Herb Derbyf4f6bbf2018-07-27 11:58:37 -0400509 const SkGlyph& glyph, SkGlyphID glyphID, SkPoint glyphPos) {
510 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
511 if (SkScalarNearlyZero(maxDim)) return;
512
513 if (!fUseTransformedFallback) {
514 if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
515 fUseTransformedFallback = true;
516 fMaxTextSize -= 2; // Subtract 2 to account for the bilerp pad around the glyph
517 }
518 }
519
520 fFallbackTxt.push_back(glyphID);
521 if (fUseTransformedFallback) {
522 // If there's a glyph in the font that's particularly large, it's possible
523 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
524 // that glyph than make the others blurry, so we set a minimum size of half the
525 // maximum text size to avoid this case.
526 SkScalar glyphTextSize =
527 SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim), 0.5f*fMaxTextSize);
528 fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
529 }
530 fFallbackPos.push_back(glyphPos);
531}
532
Herb Derby82c11e02018-08-06 16:40:12 -0400533void GrTextContext::FallbackGlyphRunHelper::drawGlyphs(
Herb Derbyf4f6bbf2018-07-27 11:58:37 -0400534 GrTextBlob* blob, int runIndex, GrGlyphCache* glyphCache, const SkSurfaceProps& props,
535 const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags) {
536 if (!fFallbackTxt.empty()) {
537 blob->initOverride(runIndex);
538 blob->setHasBitmap();
539 blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
540 const SkPaint& skPaint = paint.skPaint();
541 SkColor textColor = paint.filteredPremulColor();
542
543 SkScalar textRatio = SK_Scalar1;
544 SkPaint fallbackPaint(skPaint);
545 SkMatrix matrix = fViewMatrix;
546 this->initializeForDraw(&fallbackPaint, &textRatio, &matrix);
547 SkExclusiveStrikePtr cache =
548 blob->setupCache(runIndex, props, scalerContextFlags, fallbackPaint, &matrix);
549
550 sk_sp<GrTextStrike> currStrike;
551 auto glyphPos = fFallbackPos.begin();
552 for (auto glyphID : fFallbackTxt) {
553 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID);
554 if (!fUseTransformedFallback) {
555 fViewMatrix.mapPoints(&*glyphPos, 1);
556 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
557 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
558 }
Herb Derby049b5d92018-08-01 13:56:26 -0400559 GrTextContext::AppendGlyph(blob, runIndex, glyphCache, &currStrike,
560 glyph, GrGlyph::kCoverage_MaskStyle,
Herb Derbyf4f6bbf2018-07-27 11:58:37 -0400561 glyphPos->fX, glyphPos->fY, textColor,
562 cache.get(), textRatio, fUseTransformedFallback);
563 glyphPos++;
564 }
565 }
566}
567
568void GrTextContext::FallbackGlyphRunHelper::initializeForDraw(
569 SkPaint* paint, SkScalar* textRatio, SkMatrix* matrix) const {
570 if (!fUseTransformedFallback) return;
571
572 paint->setTextSize(fTransformedFallbackTextSize);
573 *textRatio = fTextSize / fTransformedFallbackTextSize;
574 *matrix = SkMatrix::I();
575}
576
577///////////////////////////////////////////////////////////////////////////////////////////////////
578
579
Hal Canary6f6961e2017-01-31 13:50:44 -0500580#if GR_TEST_UTILS
joshualitt79dfb2b2015-05-11 08:58:08 -0700581
Brian Salomonf18b1d82017-10-27 11:30:49 -0400582#include "GrRenderTargetContext.h"
583
Herb Derby26cbe512018-05-24 14:39:01 -0400584std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context,
585 GrTextContext* textContext,
586 GrRenderTargetContext* rtc,
587 const SkPaint& skPaint,
588 const SkMatrix& viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -0400589 const char* text,
590 int x,
591 int y) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500592 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500593
594 static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
595
596 size_t textLen = (int)strlen(text);
597
598 GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
599
Herb Derby41f4f312018-06-06 17:45:53 +0000600 auto origin = SkPoint::Make(x, y);
Herb Derby59d997a2018-06-07 12:44:09 -0400601 SkGlyphRunBuilder builder;
Herb Derbyc434ade2018-07-11 16:07:01 -0400602 builder.drawText(skPaint, text, textLen, origin);
Herb Derby82c11e02018-08-06 16:40:12 -0400603
Herb Derby41f4f312018-06-06 17:45:53 +0000604
Herb Derby8a6348e2018-07-12 15:30:35 -0400605 auto glyphRunList = builder.useGlyphRunList();
Herb Derby82c11e02018-08-06 16:40:12 -0400606 sk_sp<GrTextBlob> blob;
Herb Derbyb935cf82018-07-26 16:54:18 -0400607 if (!glyphRunList.empty()) {
Herb Derby82c11e02018-08-06 16:40:12 -0400608 blob = context->contextPriv().getTextBlobCache()->makeBlob(glyphRunList);
Herb Derby8a6348e2018-07-12 15:30:35 -0400609 // Use the text and textLen below, because we don't want to mess with the paint.
Herb Derby82c11e02018-08-06 16:40:12 -0400610 SkScalerContextFlags scalerContextFlags =
611 ComputeScalerContextFlags(rtc->colorSpaceInfo());
612 textContext->regenerateGlyphRunList(
613 blob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(), utilsPaint,
614 scalerContextFlags, viewMatrix, surfaceProps, glyphRunList,
615 rtc->textTarget()->glyphDrawer());
Herb Derby8a6348e2018-07-12 15:30:35 -0400616 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500617
618 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, surfaceProps,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500619 textContext->dfAdjustTable(), rtc->textTarget());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500620}
621
Brian Salomon44acb5b2017-07-18 19:59:24 -0400622GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
joshualitt79dfb2b2015-05-11 08:58:08 -0700623 static uint32_t gContextID = SK_InvalidGenID;
Herb Derby26cbe512018-05-24 14:39:01 -0400624 static std::unique_ptr<GrTextContext> gTextContext;
robertphillipsfcf78292015-06-19 11:49:52 -0700625 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -0700626
627 if (context->uniqueID() != gContextID) {
628 gContextID = context->uniqueID();
Herb Derby26cbe512018-05-24 14:39:01 -0400629 gTextContext = GrTextContext::Make(GrTextContext::Options());
joshualitt79dfb2b2015-05-11 08:58:08 -0700630 }
631
Brian Osman11052242016-10-27 14:47:55 -0400632 // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500633 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Brian Osman11052242016-10-27 14:47:55 -0400634 SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
brianosman8fe485b2016-07-25 12:31:51 -0700635
joshualitt6c891102015-05-13 08:51:49 -0700636 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
Brian Salomon44acb5b2017-07-18 19:59:24 -0400637
638 // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
639 // param.
joshualitt79dfb2b2015-05-11 08:58:08 -0700640 SkPaint skPaint;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500641 skPaint.setColor(random->nextU());
joshualitt79dfb2b2015-05-11 08:58:08 -0700642 skPaint.setLCDRenderText(random->nextBool());
643 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
644 skPaint.setSubpixelText(random->nextBool());
645
joshualitt79dfb2b2015-05-11 08:58:08 -0700646 const char* text = "The quick brown fox jumps over the lazy dog.";
joshualitt79dfb2b2015-05-11 08:58:08 -0700647
joshualittb8d86492016-02-24 09:23:03 -0800648 // create some random x/y offsets, including negative offsets
649 static const int kMaxTrans = 1024;
650 int xPos = (random->nextU() % 2) * 2 - 1;
651 int yPos = (random->nextU() % 2) * 2 - 1;
652 int xInt = (random->nextU() % kMaxTrans) * xPos;
653 int yInt = (random->nextU() % kMaxTrans) * yPos;
halcanary9d524f22016-03-29 09:03:52 -0700654
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500655 return gTextContext->createOp_TestingOnly(context, gTextContext.get(), rtc.get(),
656 skPaint, viewMatrix, text, xInt, yInt);
joshualitt79dfb2b2015-05-11 08:58:08 -0700657}
658
659#endif