blob: 4ad29f3a38905d97fb6dd6bea90e7c6affbacb43 [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 Derby12e42562018-07-28 14:27:48 -040087
88void GrTextContext::drawDFGlyphRun(GrTextBlob* blob, int runIndex,
89 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
90 const GrTextUtils::Paint& paint,
91 SkScalerContextFlags scalerContextFlags,
92 const SkMatrix& viewMatrix, const SkGlyphRun& glyphRun,
93 const SkPoint& offset) const {
94 bool hasWCoord = viewMatrix.hasPerspective() || fOptions.fDistanceFieldVerticesAlwaysHaveW;
95
96 // Setup distance field paint and text ratio
97 SkScalar textRatio;
98 SkPaint dfPaint(paint);
99 SkScalerContextFlags flags;
100 InitDistanceFieldPaint(blob, &dfPaint, viewMatrix, fOptions, &textRatio, &flags);
101 blob->setHasDistanceField();
102 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
103 paint.skPaint().isAntiAlias(), hasWCoord);
104
105 FallbackGlyphRunHelper fallbackTextHelper(viewMatrix, paint, glyphCache->getGlyphSizeLimit(),
106 textRatio);
107
108 sk_sp<GrTextStrike> currStrike;
109
110 {
111 auto cache = blob->setupCache(runIndex, props, flags, dfPaint, nullptr);
112
113 const SkPoint* positionCursor = glyphRun.positions().data();
114 for (auto glyphID : glyphRun.shuntGlyphsIDs()) {
115 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID);
116 SkPoint glyphPos = offset + *positionCursor++;
117 if (glyph.fWidth > 0) {
118 if (glyph.fMaskFormat == SkMask::kSDF_Format) {
119 DfAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos.fX,
120 glyphPos.fY, paint.filteredPremulColor(), cache.get(), textRatio);
121 } else {
122 // can't append non-SDF glyph to SDF batch, send to fallback
123 fallbackTextHelper.appendText(glyph, glyphID, glyphPos);
124 }
125 }
126 }
127 }
128
129 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, paint, scalerContextFlags);
130}
131
132void GrTextContext::DrawBmpGlyphRunAsPaths(GrTextBlob* blob, int runIndex,
133 GrGlyphCache* glyphCache,
134 const SkSurfaceProps& props,
135 const GrTextUtils::Paint& origPaint,
136 SkScalerContextFlags scalerContextFlags,
137 const SkMatrix& viewMatrix,
138 const SkGlyphRun& glyphRun,
139 const SkPoint& offset) {
140
141 // setup our std paint, in hopes of getting hits in the cache
142 SkPaint pathPaint(origPaint);
143 SkScalar matrixScale = pathPaint.setupForAsPaths();
144
145 FallbackGlyphRunHelper fallbackTextHelper(
146 viewMatrix, origPaint, glyphCache->getGlyphSizeLimit(), matrixScale);
147
148 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
149 pathPaint.setStyle(SkPaint::kFill_Style);
150 pathPaint.setPathEffect(nullptr);
151
152 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
153 pathPaint, &props, SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
154
155 const SkPoint* positionCursor = glyphRun.positions().data();
156 for (auto glyphID : glyphRun.shuntGlyphsIDs()) {
157 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID);
158 SkPoint loc = offset + *positionCursor++;
159 if (glyph.fWidth > 0) {
160 if (glyph.fMaskFormat == SkMask::kARGB32_Format) {
161 fallbackTextHelper.appendText(glyph, glyphID, loc);
162 } else {
163 const SkPath* path = cache->findPath(glyph);
164 if (path != nullptr) {
165 blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
166 }
167 }
168 }
169 }
170
171 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags);
172}
173
174void GrTextContext::DrawBmpGlyphRun(GrTextBlob* blob, int runIndex,
175 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
176 const GrTextUtils::Paint& paint,
177 SkScalerContextFlags scalerContextFlags,
178 const SkMatrix& viewMatrix,
179 const SkGlyphRun& glyphRun, const SkPoint& offset) {
180
181 // Ensure the blob is set for bitmaptext
182 blob->setHasBitmap();
183
184 if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
185 DrawBmpGlyphRunAsPaths(
186 blob, runIndex, glyphCache, props, paint, scalerContextFlags, viewMatrix,
187 glyphRun, offset);
188 return;
189 }
190
191 sk_sp<GrTextStrike> currStrike;
192 auto cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
193 SkFindAndPlaceGlyph::ProcessPosText(
194 SkPaint::kGlyphID_TextEncoding,
195 (const char*)glyphRun.shuntGlyphsIDs().data(),
196 glyphRun.shuntGlyphsIDs().size() * sizeof(SkGlyphID),
197 offset, viewMatrix, (const SkScalar*)glyphRun.positions().data(), 2, cache.get(),
198 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
199 position += rounding;
200 BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
201 SkScalarFloorToScalar(position.fX),
202 SkScalarFloorToScalar(position.fY),
203 paint.filteredPremulColor(), cache.get(), SK_Scalar1, false);
204 }
205 );
206}
207
208void GrTextContext::regenerateGlyphRunList(GrTextBlob* cacheBlob,
209 GrGlyphCache* glyphCache,
210 const GrShaderCaps& shaderCaps,
211 const GrTextUtils::Paint& paint,
212 SkScalerContextFlags scalerContextFlags,
213 const SkMatrix& viewMatrix,
214 const SkSurfaceProps& props,
215 const SkGlyphRunList& glyphRunList) const {
216 SkPoint origin = glyphRunList.origin();
217 cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, origin.x(), origin.y());
218
219 // Regenerate textblob
220 GrTextUtils::RunPaint runPaint(&paint);
221 int runNum = 0;
222 for (const auto& glyphRun : glyphRunList) {
223 cacheBlob->push_back_run(runNum);
224
225 if (!runPaint.modifyForRun([glyphRun](SkPaint* p) { *p = glyphRun.paint(); })) {
226 continue;
227 }
228 cacheBlob->setRunPaintFlags(runNum, runPaint.skPaint().getFlags());
229
230 if (CanDrawAsDistanceFields(runPaint, viewMatrix, props,
231 shaderCaps.supportsDistanceFieldText(), fOptions)) {
232 this->drawDFGlyphRun(cacheBlob, runNum, glyphCache, props, runPaint,
233 scalerContextFlags, viewMatrix, glyphRun, origin);
234 } else {
235 DrawBmpGlyphRun(cacheBlob, runNum, glyphCache, props, runPaint, scalerContextFlags,
236 viewMatrix, glyphRun, origin);
237 }
238 runNum += 1;
239 }
240}
241
Herb Derbycddab252018-07-16 11:19:04 -0400242void GrTextContext::drawGlyphRunList(
243 GrContext* context, GrTextUtils::Target* target, const GrClip& clip,
Herb Derbyb935cf82018-07-26 16:54:18 -0400244 const SkMatrix& viewMatrix, const SkSurfaceProps& props, const SkGlyphRunList& glyphRunList,
Herb Derbycddab252018-07-16 11:19:04 -0400245 const SkIRect& clipBounds) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400246 SkPoint origin = glyphRunList.origin();
joshualitt9e36c1a2015-04-14 12:17:27 -0700247
Herb Derbycddab252018-07-16 11:19:04 -0400248 // Get the first paint to use as the key paint.
Herb Derbyb935cf82018-07-26 16:54:18 -0400249 const SkPaint& skPaint = glyphRunList.paint();
Herb Derbycddab252018-07-16 11:19:04 -0400250
joshualitt9b8e79e2015-04-24 09:57:12 -0700251 // If we have been abandoned, then don't draw
Khushalc421ca12018-06-26 14:38:34 -0700252 if (context->abandoned()) {
robertphillipsea461502015-05-26 11:38:03 -0700253 return;
254 }
255
Mike Reed80747ef2018-01-23 15:29:32 -0500256 SkMaskFilterBase::BlurRec blurRec;
joshualitt53b5f442015-04-13 06:33:59 -0700257 // It might be worth caching these things, but its not clear at this time
258 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
259 const SkMaskFilter* mf = skPaint.getMaskFilter();
Herb Derbyb935cf82018-07-26 16:54:18 -0400260 bool canCache = glyphRunList.canCache() && !(skPaint.getPathEffect() ||
Herb Derbycddab252018-07-16 11:19:04 -0400261 (mf && !as_MFB(mf)->asABlur(&blurRec)));
Herb Derbyd8327a82018-01-22 14:39:27 -0500262 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
joshualitt2a0e9f32015-04-13 06:12:21 -0700263
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500264 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500265 GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
266
Herb Derbycddab252018-07-16 11:19:04 -0400267 sk_sp<GrTextBlob> cacheBlob;
268 GrTextBlob::Key key;
joshualitt2a0e9f32015-04-13 06:12:21 -0700269 if (canCache) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400270 bool hasLCD = glyphRunList.anyRunsLCD();
joshualitte4cee1f2015-05-11 13:04:28 -0700271
272 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
joshualitt2c89bc12016-02-11 05:42:30 -0800273 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
Herb Derbycddab252018-07-16 11:19:04 -0400274 kUnknown_SkPixelGeometry;
joshualitte4cee1f2015-05-11 13:04:28 -0700275
joshualitt9e36c1a2015-04-14 12:17:27 -0700276 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
277 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
278 // ensure we always match the same key
279 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
Herb Derbycddab252018-07-16 11:19:04 -0400280 ComputeCanonicalColor(skPaint, hasLCD);
joshualitt9e36c1a2015-04-14 12:17:27 -0700281
joshualitte4cee1f2015-05-11 13:04:28 -0700282 key.fPixelGeometry = pixelGeometry;
Herb Derbyb935cf82018-07-26 16:54:18 -0400283 key.fUniqueID = glyphRunList.uniqueID();
joshualitt53b5f442015-04-13 06:33:59 -0700284 key.fStyle = skPaint.getStyle();
285 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700286 key.fCanonicalColor = canonicalColor;
brianosman8d7ffce2016-04-21 08:29:06 -0700287 key.fScalerContextFlags = scalerContextFlags;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500288 cacheBlob = textBlobCache->find(key);
joshualitt2a0e9f32015-04-13 06:12:21 -0700289 }
290
Brian Salomonf18b1d82017-10-27 11:30:49 -0400291 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
joshualittb7133be2015-04-08 09:08:31 -0700292 if (cacheBlob) {
Herb Derbycddab252018-07-16 11:19:04 -0400293 if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, origin.x(), origin.y())) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700294 // We have to remake the blob because changes may invalidate our masks.
295 // TODO we could probably get away reuse most of the time if the pointer is unique,
296 // but we'd have to clear the subrun information
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500297 textBlobCache->remove(cacheBlob.get());
Herb Derbycddab252018-07-16 11:19:04 -0400298 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
299 this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400300 *context->contextPriv().caps()->shaderCaps(), paint,
Herb Derbycddab252018-07-16 11:19:04 -0400301 scalerContextFlags, viewMatrix, props, glyphRunList);
joshualittb7133be2015-04-08 09:08:31 -0700302 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500303 textBlobCache->makeMRU(cacheBlob.get());
joshualitt2f2ee832016-02-10 08:52:24 -0800304
305 if (CACHE_SANITY_CHECK) {
Herb Derbyb935cf82018-07-26 16:54:18 -0400306 int glyphCount = glyphRunList.totalGlyphCount();
307 int runCount = glyphRunList.runCount();
Herb Derby86240592018-05-24 16:12:31 -0400308 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
joshualitt92303772016-02-10 11:55:52 -0800309 sanityBlob->setupKey(key, blurRec, skPaint);
Herb Derbycddab252018-07-16 11:19:04 -0400310 this->regenerateGlyphRunList(
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400311 sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(),
Herb Derbycddab252018-07-16 11:19:04 -0400312 paint, scalerContextFlags, viewMatrix, props, glyphRunList);
Herb Derby86240592018-05-24 16:12:31 -0400313 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700314 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700315 }
316 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700317 if (canCache) {
Herb Derbycddab252018-07-16 11:19:04 -0400318 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint);
joshualitt2a0e9f32015-04-13 06:12:21 -0700319 } else {
Herb Derbycddab252018-07-16 11:19:04 -0400320 cacheBlob = textBlobCache->makeBlob(glyphRunList);
joshualitt2a0e9f32015-04-13 06:12:21 -0700321 }
Herb Derbycddab252018-07-16 11:19:04 -0400322 this->regenerateGlyphRunList(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400323 *context->contextPriv().caps()->shaderCaps(), paint,
Herb Derbycddab252018-07-16 11:19:04 -0400324 scalerContextFlags, viewMatrix, props, glyphRunList);
joshualitt1d89e8d2015-04-01 12:40:54 -0700325 }
326
Robert Phillips5a66efb2018-03-07 15:13:18 -0500327 cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint,
Herb Derbycddab252018-07-16 11:19:04 -0400328 clip, viewMatrix, clipBounds, origin.x(), origin.y());
joshualitt1d89e8d2015-04-01 12:40:54 -0700329}
330
Herb Derby86240592018-05-24 16:12:31 -0400331inline sk_sp<GrTextBlob>
Herb Derby26cbe512018-05-24 14:39:01 -0400332GrTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache,
333 GrGlyphCache* glyphCache,
334 const GrShaderCaps& shaderCaps,
335 const GrTextUtils::Paint& paint,
336 SkScalerContextFlags scalerContextFlags,
337 const SkMatrix& viewMatrix,
338 const SkSurfaceProps& props,
339 const char text[], size_t byteLength,
340 const SkScalar pos[], int scalarsPerPosition, const
341 SkPoint& offset) const {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500342 int glyphCount = paint.skPaint().countText(text, byteLength);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400343 if (!glyphCount) {
344 return nullptr;
345 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700346
Herb Derby86240592018-05-24 16:12:31 -0400347 sk_sp<GrTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
joshualitt7481e752016-01-22 06:08:48 -0800348 blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
Jim Van Verth54d9c882018-02-08 16:14:48 -0500349 blob->setRunPaintFlags(0, paint.skPaint().getFlags());
joshualitt9bd2daf2015-04-17 09:30:06 -0700350
Khushal3e7548c2018-05-23 15:45:01 -0700351 if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps.supportsDistanceFieldText(),
352 fOptions)) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500353 this->drawDFPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
Brian Salomonaf597482017-11-07 16:23:34 -0500354 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700355 } else {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500356 DrawBmpPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
357 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700358 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700359 return blob;
360}
361
Herb Derby26cbe512018-05-24 14:39:01 -0400362void GrTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target,
363 const GrClip& clip, const SkPaint& skPaint,
364 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
365 const char text[], size_t byteLength, const SkScalar pos[],
366 int scalarsPerPosition, const SkPoint& offset,
367 const SkIRect& regionClipBounds) {
Brian Salomonf18b1d82017-10-27 11:30:49 -0400368 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
Khushalc421ca12018-06-26 14:38:34 -0700369 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800370 return;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500371 }
372
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500373 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500374 auto textBlobCache = context->contextPriv().getTextBlobCache();
375
Herb Derby86240592018-05-24 16:12:31 -0400376 sk_sp<GrTextBlob> blob(this->makeDrawPosTextBlob(
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400377 textBlobCache, glyphCache, *context->contextPriv().caps()->shaderCaps(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500378 ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
379 byteLength, pos, scalarsPerPosition, offset));
380 if (blob) {
Robert Phillips5a66efb2018-03-07 15:13:18 -0500381 blob->flush(target, props, fDistanceAdjustTable.get(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500382 clip, viewMatrix, regionClipBounds, offset.fX, offset.fY);
joshualitte55750e2016-02-10 12:52:21 -0800383 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700384}
385
Herb Derby86240592018-05-24 16:12:31 -0400386void GrTextContext::DrawBmpPosText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400387 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
388 const GrTextUtils::Paint& paint,
389 SkScalerContextFlags scalerContextFlags,
390 const SkMatrix& viewMatrix,
391 const char text[], size_t byteLength, const SkScalar pos[],
392 int scalarsPerPosition, const SkPoint& offset) {
Brian Salomon52db9402017-11-07 14:58:55 -0500393 SkASSERT(byteLength == 0 || text != nullptr);
394 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
395
396 // nothing to draw
397 if (text == nullptr || byteLength == 0) {
398 return;
399 }
400
401 // Ensure the blob is set for bitmaptext
402 blob->setHasBitmap();
403
Jim Van Verth54d9c882018-02-08 16:14:48 -0500404 if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500405 DrawBmpPosTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500406 viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500407 return;
408 }
409
Herb Derbyab6fd7e2018-03-07 18:05:39 +0000410 sk_sp<GrTextStrike> currStrike;
Herb Derby526819d2018-03-09 12:51:12 -0500411 auto cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
Brian Salomon52db9402017-11-07 14:58:55 -0500412 SkFindAndPlaceGlyph::ProcessPosText(
413 paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
Herb Derby1e7c6582018-05-21 16:10:17 -0400414 scalarsPerPosition, cache.get(),
Brian Salomon52db9402017-11-07 14:58:55 -0500415 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
416 position += rounding;
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500417 BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500418 SkScalarFloorToScalar(position.fX),
419 SkScalarFloorToScalar(position.fY),
Jim Van Verthb515ae72018-05-23 16:44:55 -0400420 paint.filteredPremulColor(), cache.get(), SK_Scalar1, false);
Brian Salomon52db9402017-11-07 14:58:55 -0500421 });
Brian Salomon52db9402017-11-07 14:58:55 -0500422}
423
Herb Derby86240592018-05-24 16:12:31 -0400424void GrTextContext::DrawBmpPosTextAsPaths(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400425 GrGlyphCache* glyphCache,
426 const SkSurfaceProps& props,
427 const GrTextUtils::Paint& origPaint,
428 SkScalerContextFlags scalerContextFlags,
429 const SkMatrix& viewMatrix,
430 const char text[], size_t byteLength,
431 const SkScalar pos[], int scalarsPerPosition,
432 const SkPoint& offset) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500433 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
434
435 // nothing to draw
436 if (text == nullptr || byteLength == 0) {
437 return;
438 }
439
440 // setup our std paint, in hopes of getting hits in the cache
Jim Van Verthc401bb92018-02-15 14:05:24 -0500441 SkPaint pathPaint(origPaint);
442 SkScalar matrixScale = pathPaint.setupForAsPaths();
Khushalfa8ff092018-06-06 17:46:38 -0700443 FallbackTextHelper fallbackTextHelper(viewMatrix, origPaint, glyphCache->getGlyphSizeLimit(),
444 matrixScale);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500445
446 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
Jim Van Verthc401bb92018-02-15 14:05:24 -0500447 pathPaint.setStyle(SkPaint::kFill_Style);
448 pathPaint.setPathEffect(nullptr);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500449
Jim Van Verthc401bb92018-02-15 14:05:24 -0500450 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(pathPaint.getTextEncoding(),
Jim Van Verthc401bb92018-02-15 14:05:24 -0500451 true);
Herb Derbyfa996902018-04-18 11:36:12 -0400452 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
Herb Derby4e34a012018-03-21 10:47:30 -0400453 pathPaint, &props, SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500454
455 const char* stop = text + byteLength;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500456 const char* lastText = text;
Jim Van Verth54d9c882018-02-08 16:14:48 -0500457 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
458
459 while (text < stop) {
Hal Canary4014ba62018-07-24 11:33:21 -0400460 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text, stop);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500461 if (glyph.fWidth) {
Jim Van Verthc401bb92018-02-15 14:05:24 -0500462 SkPoint loc;
Herb Derby1e7c6582018-05-21 16:10:17 -0400463 tmsProc(pos, &loc);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500464 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
465 fallbackTextHelper.appendText(glyph, text - lastText, lastText, loc);
466 } else {
467 const SkPath* path = cache->findPath(glyph);
468 if (path) {
469 blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
470 }
Jim Van Verth54d9c882018-02-08 16:14:48 -0500471 }
472 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500473 lastText = text;
Jim Van Verth54d9c882018-02-08 16:14:48 -0500474 pos += scalarsPerPosition;
475 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500476
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500477 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500478}
479
Herb Derby86240592018-05-24 16:12:31 -0400480void GrTextContext::BmpAppendGlyph(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400481 GrGlyphCache* grGlyphCache,
482 sk_sp<GrTextStrike>* strike,
483 const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
484 GrColor color, SkGlyphCache* skGlyphCache,
485 SkScalar textRatio, bool needsTransform) {
Brian Salomon52db9402017-11-07 14:58:55 -0500486 if (!*strike) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500487 *strike = grGlyphCache->getStrike(skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500488 }
489
490 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
491 skGlyph.getSubXFixed(),
492 skGlyph.getSubYFixed(),
493 GrGlyph::kCoverage_MaskStyle);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500494 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500495 if (!glyph) {
496 return;
497 }
498
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500499 SkASSERT(skGlyph.fWidth == glyph->width());
500 SkASSERT(skGlyph.fHeight == glyph->height());
501
Jim Van Verthf4c13162018-01-11 16:40:24 -0500502 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
503 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
504 SkScalar width = SkIntToScalar(glyph->fBounds.width());
505 SkScalar height = SkIntToScalar(glyph->fBounds.height());
Brian Salomon52db9402017-11-07 14:58:55 -0500506
Jim Van Verthf4c13162018-01-11 16:40:24 -0500507 dx *= textRatio;
508 dy *= textRatio;
509 width *= textRatio;
510 height *= textRatio;
Brian Salomon52db9402017-11-07 14:58:55 -0500511
Jim Van Verthf4c13162018-01-11 16:40:24 -0500512 SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
Brian Salomon52db9402017-11-07 14:58:55 -0500513
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500514 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
Jim Van Verthb515ae72018-05-23 16:44:55 -0400515 textRatio, !needsTransform);
Brian Salomon52db9402017-11-07 14:58:55 -0500516}
517
Herb Derby26cbe512018-05-24 14:39:01 -0400518void GrTextContext::SanitizeOptions(Options* options) {
Khushal3e7548c2018-05-23 15:45:01 -0700519 if (options->fMaxDistanceFieldFontSize < 0.f) {
520 options->fMaxDistanceFieldFontSize = kDefaultMaxDistanceFieldFontSize;
521 }
522 if (options->fMinDistanceFieldFontSize < 0.f) {
523 options->fMinDistanceFieldFontSize = kDefaultMinDistanceFieldFontSize;
524 }
525}
526
Herb Derby26cbe512018-05-24 14:39:01 -0400527bool GrTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
528 const SkSurfaceProps& props,
529 bool contextSupportsDistanceFieldText,
530 const Options& options) {
Brian Salomon52db9402017-11-07 14:58:55 -0500531 if (!viewMatrix.hasPerspective()) {
532 SkScalar maxScale = viewMatrix.getMaxScale();
533 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
534 // Hinted text looks far better at small resolutions
535 // Scaling up beyond 2x yields undesireable artifacts
Khushal3e7548c2018-05-23 15:45:01 -0700536 if (scaledTextSize < options.fMinDistanceFieldFontSize ||
537 scaledTextSize > options.fMaxDistanceFieldFontSize) {
Brian Salomon52db9402017-11-07 14:58:55 -0500538 return false;
539 }
540
541 bool useDFT = props.isUseDeviceIndependentFonts();
542#if SK_FORCE_DISTANCE_FIELD_TEXT
543 useDFT = true;
544#endif
545
546 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
547 return false;
548 }
549 }
550
Mike Reed8ad91a92018-01-19 19:09:32 -0500551 // mask filters modify alpha, which doesn't translate well to distance
Khushal3e7548c2018-05-23 15:45:01 -0700552 if (skPaint.getMaskFilter() || !contextSupportsDistanceFieldText) {
Brian Salomon52db9402017-11-07 14:58:55 -0500553 return false;
554 }
555
556 // TODO: add some stroking support
557 if (skPaint.getStyle() != SkPaint::kFill_Style) {
558 return false;
559 }
560
561 return true;
562}
563
Herb Derby86240592018-05-24 16:12:31 -0400564void GrTextContext::InitDistanceFieldPaint(GrTextBlob* blob,
Herb Derby26cbe512018-05-24 14:39:01 -0400565 SkPaint* skPaint,
566 const SkMatrix& viewMatrix,
567 const Options& options,
568 SkScalar* textRatio,
569 SkScalerContextFlags* flags) {
Brian Salomon52db9402017-11-07 14:58:55 -0500570 SkScalar textSize = skPaint->getTextSize();
571 SkScalar scaledTextSize = textSize;
572
573 if (viewMatrix.hasPerspective()) {
574 // for perspective, we simply force to the medium size
575 // TODO: compute a size based on approximate screen area
576 scaledTextSize = kMediumDFFontLimit;
577 } else {
578 SkScalar maxScale = viewMatrix.getMaxScale();
579 // if we have non-unity scale, we need to choose our base text size
580 // based on the SkPaint's text size multiplied by the max scale factor
581 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
582 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
583 scaledTextSize *= maxScale;
584 }
585 }
586
587 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
588 // and ceiling. A scale outside of this range would require regenerating the distance fields
589 SkScalar dfMaskScaleFloor;
590 SkScalar dfMaskScaleCeil;
591 if (scaledTextSize <= kSmallDFFontLimit) {
Khushal3e7548c2018-05-23 15:45:01 -0700592 dfMaskScaleFloor = options.fMinDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500593 dfMaskScaleCeil = kSmallDFFontLimit;
594 *textRatio = textSize / kSmallDFFontSize;
595 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
596 } else if (scaledTextSize <= kMediumDFFontLimit) {
597 dfMaskScaleFloor = kSmallDFFontLimit;
598 dfMaskScaleCeil = kMediumDFFontLimit;
599 *textRatio = textSize / kMediumDFFontSize;
600 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
601 } else {
602 dfMaskScaleFloor = kMediumDFFontLimit;
Khushal3e7548c2018-05-23 15:45:01 -0700603 dfMaskScaleCeil = options.fMaxDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500604 *textRatio = textSize / kLargeDFFontSize;
605 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
606 }
607
608 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
609 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
610 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
611 // tolerate before we'd have to move to a large mip size. When we actually test these values
612 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
613 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
614 // level)
615 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
Khushal3e7548c2018-05-23 15:45:01 -0700616 if (blob) {
617 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize,
618 dfMaskScaleCeil / scaledTextSize);
619 }
Brian Salomon52db9402017-11-07 14:58:55 -0500620
621 skPaint->setAntiAlias(true);
622 skPaint->setLCDRenderText(false);
623 skPaint->setAutohinted(false);
624 skPaint->setHinting(SkPaint::kNormal_Hinting);
625 skPaint->setSubpixelText(true);
Jim Van Verthd401da62018-05-03 10:40:30 -0400626
627 skPaint->setMaskFilter(GrSDFMaskFilter::Make());
Khushal3e7548c2018-05-23 15:45:01 -0700628
629 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
630 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
631 *flags = SkScalerContextFlags::kNone;
Brian Salomon52db9402017-11-07 14:58:55 -0500632}
633
Herb Derby86240592018-05-24 16:12:31 -0400634void GrTextContext::drawDFPosText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400635 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
636 const GrTextUtils::Paint& paint,
637 SkScalerContextFlags scalerContextFlags,
638 const SkMatrix& viewMatrix, const char text[],
639 size_t byteLength, const SkScalar pos[],
640 int scalarsPerPosition, const SkPoint& offset) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500641 SkASSERT(byteLength == 0 || text != nullptr);
642 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
643
644 // nothing to draw
645 if (text == nullptr || byteLength == 0) {
646 return;
647 }
648
Khushal3e7548c2018-05-23 15:45:01 -0700649 bool hasWCoord = viewMatrix.hasPerspective() || fOptions.fDistanceFieldVerticesAlwaysHaveW;
Brian Salomonb5086962017-12-13 10:59:33 -0500650
Brian Salomon52db9402017-11-07 14:58:55 -0500651 // Setup distance field paint and text ratio
652 SkScalar textRatio;
653 SkPaint dfPaint(paint);
Khushal3e7548c2018-05-23 15:45:01 -0700654 SkScalerContextFlags flags;
655 InitDistanceFieldPaint(blob, &dfPaint, viewMatrix, fOptions, &textRatio, &flags);
Brian Salomon52db9402017-11-07 14:58:55 -0500656 blob->setHasDistanceField();
657 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
Brian Salomon5c6ac642017-12-19 11:09:32 -0500658 paint.skPaint().isAntiAlias(), hasWCoord);
Brian Salomon52db9402017-11-07 14:58:55 -0500659
Khushalfa8ff092018-06-06 17:46:38 -0700660 FallbackTextHelper fallbackTextHelper(viewMatrix, paint, glyphCache->getGlyphSizeLimit(),
661 textRatio);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500662
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500663 sk_sp<GrTextStrike> currStrike;
Brian Salomon52db9402017-11-07 14:58:55 -0500664
Herb Derby526819d2018-03-09 12:51:12 -0500665 {
Khushal3e7548c2018-05-23 15:45:01 -0700666 auto cache = blob->setupCache(runIndex, props, flags, dfPaint, nullptr);
Herb Derby526819d2018-03-09 12:51:12 -0500667 SkPaint::GlyphCacheProc glyphCacheProc =
Herb Derbyfcac00f2018-05-01 11:57:56 -0400668 SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), true);
Brian Salomon52db9402017-11-07 14:58:55 -0500669
Herb Derby526819d2018-03-09 12:51:12 -0500670 const char* stop = text + byteLength;
Brian Salomon52db9402017-11-07 14:58:55 -0500671
Herb Derby526819d2018-03-09 12:51:12 -0500672 while (text < stop) {
673 const char* lastText = text;
674 // the last 2 parameters are ignored
Hal Canary4014ba62018-07-24 11:33:21 -0400675 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text, stop);
Brian Salomon52db9402017-11-07 14:58:55 -0500676
Herb Derby526819d2018-03-09 12:51:12 -0500677 if (glyph.fWidth) {
678 SkPoint glyphPos(offset);
Herb Derby1e7c6582018-05-21 16:10:17 -0400679 glyphPos.fX += pos[0];
680 glyphPos.fY += (2 == scalarsPerPosition ? pos[1] : 0);
Jim Van Verthf4c13162018-01-11 16:40:24 -0500681
Jim Van Verthd401da62018-05-03 10:40:30 -0400682 if (glyph.fMaskFormat == SkMask::kSDF_Format) {
Herb Derby526819d2018-03-09 12:51:12 -0500683 DfAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos.fX,
684 glyphPos.fY, paint.filteredPremulColor(), cache.get(), textRatio);
685 } else {
Jim Van Verthd401da62018-05-03 10:40:30 -0400686 // can't append non-SDF glyph to SDF batch, send to fallback
Herb Derby526819d2018-03-09 12:51:12 -0500687 fallbackTextHelper.appendText(glyph, SkToInt(text - lastText), lastText,
688 glyphPos);
689 }
Brian Salomon52db9402017-11-07 14:58:55 -0500690 }
Herb Derby526819d2018-03-09 12:51:12 -0500691 pos += scalarsPerPosition;
Brian Salomon52db9402017-11-07 14:58:55 -0500692 }
Brian Salomon52db9402017-11-07 14:58:55 -0500693 }
Herb Derbyab6fd7e2018-03-07 18:05:39 +0000694
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500695 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, paint, scalerContextFlags);
Brian Salomon52db9402017-11-07 14:58:55 -0500696}
697
Jim Van Verthf4c13162018-01-11 16:40:24 -0500698// TODO: merge with BmpAppendGlyph
Herb Derby86240592018-05-24 16:12:31 -0400699void GrTextContext::DfAppendGlyph(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400700 GrGlyphCache* grGlyphCache, sk_sp<GrTextStrike>* strike,
701 const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
702 GrColor color, SkGlyphCache* skGlyphCache,
703 SkScalar textRatio) {
Brian Salomon52db9402017-11-07 14:58:55 -0500704 if (!*strike) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500705 *strike = grGlyphCache->getStrike(skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500706 }
707
708 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
709 skGlyph.getSubXFixed(),
710 skGlyph.getSubYFixed(),
711 GrGlyph::kDistance_MaskStyle);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500712 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500713 if (!glyph) {
Jim Van Verthf4c13162018-01-11 16:40:24 -0500714 return;
Brian Salomon52db9402017-11-07 14:58:55 -0500715 }
716
717 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
718 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
719 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
720 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
721
Jim Van Verthf4c13162018-01-11 16:40:24 -0500722 dx *= textRatio;
723 dy *= textRatio;
724 width *= textRatio;
725 height *= textRatio;
726 SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
Brian Salomon52db9402017-11-07 14:58:55 -0500727
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500728 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500729 textRatio, false);
Brian Salomon52db9402017-11-07 14:58:55 -0500730}
731
joshualitt79dfb2b2015-05-11 08:58:08 -0700732///////////////////////////////////////////////////////////////////////////////////////////////////
733
Herb Derby26cbe512018-05-24 14:39:01 -0400734void GrTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, int count,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500735 const char* text, SkPoint glyphPos) {
Jim Van Verthb515ae72018-05-23 16:44:55 -0400736 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
Khushalfa8ff092018-06-06 17:46:38 -0700737 if (SkScalarNearlyZero(maxDim)) return;
738
Jim Van Verthb515ae72018-05-23 16:44:55 -0400739 if (!fUseTransformedFallback) {
740 if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
741 fUseTransformedFallback = true;
Jim Van Verthcf838c72018-03-05 14:40:36 -0500742 fMaxTextSize -= 2; // Subtract 2 to account for the bilerp pad around the glyph
Jim Van Verthc401bb92018-02-15 14:05:24 -0500743 }
744 }
745
746 fFallbackTxt.append(count, text);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400747 if (fUseTransformedFallback) {
Jim Van Verth080a9282018-03-02 10:41:43 -0500748 // If there's a glyph in the font that's particularly large, it's possible
749 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
Jim Van Verth8b7284d2018-05-17 12:33:52 -0400750 // that glyph than make the others blurry, so we set a minimum size of half the
Jim Van Verth080a9282018-03-02 10:41:43 -0500751 // maximum text size to avoid this case.
Jim Van Verthb515ae72018-05-23 16:44:55 -0400752 SkScalar glyphTextSize = SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim),
Jim Van Verth080a9282018-03-02 10:41:43 -0500753 0.5f*fMaxTextSize);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400754 fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500755 }
756 *fFallbackPos.append() = glyphPos;
757}
758
Herb Derby86240592018-05-24 16:12:31 -0400759void GrTextContext::FallbackTextHelper::drawText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400760 GrGlyphCache* glyphCache,
761 const SkSurfaceProps& props,
762 const GrTextUtils::Paint& paint,
763 SkScalerContextFlags scalerContextFlags) {
Jim Van Verthc401bb92018-02-15 14:05:24 -0500764 if (fFallbackTxt.count()) {
765 blob->initOverride(runIndex);
766 blob->setHasBitmap();
Jim Van Verthb515ae72018-05-23 16:44:55 -0400767 blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
Herb Derby526819d2018-03-09 12:51:12 -0500768 SkExclusiveStrikePtr cache;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500769 const SkPaint& skPaint = paint.skPaint();
770 SkPaint::GlyphCacheProc glyphCacheProc =
Herb Derbyfcac00f2018-05-01 11:57:56 -0400771 SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), true);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500772 SkColor textColor = paint.filteredPremulColor();
Khushalfa8ff092018-06-06 17:46:38 -0700773
Jim Van Verthc401bb92018-02-15 14:05:24 -0500774 SkScalar textRatio = SK_Scalar1;
Khushalfa8ff092018-06-06 17:46:38 -0700775 SkPaint fallbackPaint(skPaint);
776 SkMatrix matrix = fViewMatrix;
777 this->initializeForDraw(&fallbackPaint, &textRatio, &matrix);
778 cache = blob->setupCache(runIndex, props, scalerContextFlags, fallbackPaint, &matrix);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500779
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500780 sk_sp<GrTextStrike> currStrike;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500781 const char* text = fFallbackTxt.begin();
782 const char* stop = text + fFallbackTxt.count();
783 SkPoint* glyphPos = fFallbackPos.begin();
784 while (text < stop) {
Hal Canary4014ba62018-07-24 11:33:21 -0400785 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text, stop);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400786 if (!fUseTransformedFallback) {
787 fViewMatrix.mapPoints(glyphPos, 1);
Jim Van Verth76e85162018-03-29 13:46:56 -0400788 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
789 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
790 }
Herb Derby26cbe512018-05-24 14:39:01 -0400791 GrTextContext::BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500792 glyphPos->fX, glyphPos->fY, textColor,
Jim Van Verthb515ae72018-05-23 16:44:55 -0400793 cache.get(), textRatio, fUseTransformedFallback);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500794 glyphPos++;
795 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500796 }
797}
798
Khushalfa8ff092018-06-06 17:46:38 -0700799void GrTextContext::FallbackTextHelper::initializeForDraw(SkPaint* paint, SkScalar* textRatio,
800 SkMatrix* matrix) const {
801 if (!fUseTransformedFallback) return;
802
803 paint->setTextSize(fTransformedFallbackTextSize);
804 *textRatio = fTextSize / fTransformedFallbackTextSize;
805 *matrix = SkMatrix::I();
806}
807
Jim Van Verthc401bb92018-02-15 14:05:24 -0500808///////////////////////////////////////////////////////////////////////////////////////////////////
809
Herb Derbyf4f6bbf2018-07-27 11:58:37 -0400810void GrTextContext::FallbackGlyphRunHelper::appendText(
811 const SkGlyph& glyph, SkGlyphID glyphID, SkPoint glyphPos) {
812 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
813 if (SkScalarNearlyZero(maxDim)) return;
814
815 if (!fUseTransformedFallback) {
816 if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
817 fUseTransformedFallback = true;
818 fMaxTextSize -= 2; // Subtract 2 to account for the bilerp pad around the glyph
819 }
820 }
821
822 fFallbackTxt.push_back(glyphID);
823 if (fUseTransformedFallback) {
824 // If there's a glyph in the font that's particularly large, it's possible
825 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
826 // that glyph than make the others blurry, so we set a minimum size of half the
827 // maximum text size to avoid this case.
828 SkScalar glyphTextSize =
829 SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim), 0.5f*fMaxTextSize);
830 fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
831 }
832 fFallbackPos.push_back(glyphPos);
833}
834
835void GrTextContext::FallbackGlyphRunHelper::drawText(
836 GrTextBlob* blob, int runIndex, GrGlyphCache* glyphCache, const SkSurfaceProps& props,
837 const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags) {
838 if (!fFallbackTxt.empty()) {
839 blob->initOverride(runIndex);
840 blob->setHasBitmap();
841 blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
842 const SkPaint& skPaint = paint.skPaint();
843 SkColor textColor = paint.filteredPremulColor();
844
845 SkScalar textRatio = SK_Scalar1;
846 SkPaint fallbackPaint(skPaint);
847 SkMatrix matrix = fViewMatrix;
848 this->initializeForDraw(&fallbackPaint, &textRatio, &matrix);
849 SkExclusiveStrikePtr cache =
850 blob->setupCache(runIndex, props, scalerContextFlags, fallbackPaint, &matrix);
851
852 sk_sp<GrTextStrike> currStrike;
853 auto glyphPos = fFallbackPos.begin();
854 for (auto glyphID : fFallbackTxt) {
855 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID);
856 if (!fUseTransformedFallback) {
857 fViewMatrix.mapPoints(&*glyphPos, 1);
858 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
859 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
860 }
861 GrTextContext::BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
862 glyphPos->fX, glyphPos->fY, textColor,
863 cache.get(), textRatio, fUseTransformedFallback);
864 glyphPos++;
865 }
866 }
867}
868
869void GrTextContext::FallbackGlyphRunHelper::initializeForDraw(
870 SkPaint* paint, SkScalar* textRatio, SkMatrix* matrix) const {
871 if (!fUseTransformedFallback) return;
872
873 paint->setTextSize(fTransformedFallbackTextSize);
874 *textRatio = fTextSize / fTransformedFallbackTextSize;
875 *matrix = SkMatrix::I();
876}
877
878///////////////////////////////////////////////////////////////////////////////////////////////////
879
880
Hal Canary6f6961e2017-01-31 13:50:44 -0500881#if GR_TEST_UTILS
joshualitt79dfb2b2015-05-11 08:58:08 -0700882
Brian Salomonf18b1d82017-10-27 11:30:49 -0400883#include "GrRenderTargetContext.h"
884
Herb Derby26cbe512018-05-24 14:39:01 -0400885std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context,
886 GrTextContext* textContext,
887 GrRenderTargetContext* rtc,
888 const SkPaint& skPaint,
889 const SkMatrix& viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -0400890 const char* text,
891 int x,
892 int y) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500893 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500894
895 static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
896
897 size_t textLen = (int)strlen(text);
898
899 GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
900
901 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
902 // test the text op with this unit test, that is okay.
Herb Derby41f4f312018-06-06 17:45:53 +0000903
904 auto origin = SkPoint::Make(x, y);
Herb Derby59d997a2018-06-07 12:44:09 -0400905 SkGlyphRunBuilder builder;
Herb Derbyc434ade2018-07-11 16:07:01 -0400906 builder.drawText(skPaint, text, textLen, origin);
Herb Derby59d997a2018-06-07 12:44:09 -0400907 sk_sp<GrTextBlob> blob;
Herb Derby41f4f312018-06-06 17:45:53 +0000908
Herb Derby8a6348e2018-07-12 15:30:35 -0400909 auto glyphRunList = builder.useGlyphRunList();
Herb Derbyb935cf82018-07-26 16:54:18 -0400910 if (!glyphRunList.empty()) {
911 auto glyphRun = glyphRunList[0];
Herb Derby8a6348e2018-07-12 15:30:35 -0400912 // Use the text and textLen below, because we don't want to mess with the paint.
913 glyphRun.temporaryShuntToCallback(
Herb Derby59d997a2018-06-07 12:44:09 -0400914 [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
915 blob = textContext->makeDrawPosTextBlob(
916 context->contextPriv().getTextBlobCache(), glyphCache,
917 *context->contextPriv().caps()->shaderCaps(), utilsPaint,
Herb Derby8a6348e2018-07-12 15:30:35 -0400918 GrTextContext::kTextBlobOpScalerContextFlags, viewMatrix, surfaceProps,
Herb Derbycddab252018-07-16 11:19:04 -0400919 text, textLen, pos, 2, origin);
Herb Derby59d997a2018-06-07 12:44:09 -0400920 });
Herb Derby8a6348e2018-07-12 15:30:35 -0400921 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500922
923 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, surfaceProps,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500924 textContext->dfAdjustTable(), rtc->textTarget());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500925}
926
Brian Salomon44acb5b2017-07-18 19:59:24 -0400927GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
joshualitt79dfb2b2015-05-11 08:58:08 -0700928 static uint32_t gContextID = SK_InvalidGenID;
Herb Derby26cbe512018-05-24 14:39:01 -0400929 static std::unique_ptr<GrTextContext> gTextContext;
robertphillipsfcf78292015-06-19 11:49:52 -0700930 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -0700931
932 if (context->uniqueID() != gContextID) {
933 gContextID = context->uniqueID();
Herb Derby26cbe512018-05-24 14:39:01 -0400934 gTextContext = GrTextContext::Make(GrTextContext::Options());
joshualitt79dfb2b2015-05-11 08:58:08 -0700935 }
936
Brian Osman11052242016-10-27 14:47:55 -0400937 // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500938 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Brian Osman11052242016-10-27 14:47:55 -0400939 SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
brianosman8fe485b2016-07-25 12:31:51 -0700940
joshualitt6c891102015-05-13 08:51:49 -0700941 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
Brian Salomon44acb5b2017-07-18 19:59:24 -0400942
943 // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
944 // param.
joshualitt79dfb2b2015-05-11 08:58:08 -0700945 SkPaint skPaint;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500946 skPaint.setColor(random->nextU());
joshualitt79dfb2b2015-05-11 08:58:08 -0700947 skPaint.setLCDRenderText(random->nextBool());
948 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
949 skPaint.setSubpixelText(random->nextBool());
950
joshualitt79dfb2b2015-05-11 08:58:08 -0700951 const char* text = "The quick brown fox jumps over the lazy dog.";
joshualitt79dfb2b2015-05-11 08:58:08 -0700952
joshualittb8d86492016-02-24 09:23:03 -0800953 // create some random x/y offsets, including negative offsets
954 static const int kMaxTrans = 1024;
955 int xPos = (random->nextU() % 2) * 2 - 1;
956 int yPos = (random->nextU() % 2) * 2 - 1;
957 int xInt = (random->nextU() % kMaxTrans) * xPos;
958 int yInt = (random->nextU() % kMaxTrans) * yPos;
halcanary9d524f22016-03-29 09:03:52 -0700959
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500960 return gTextContext->createOp_TestingOnly(context, gTextContext.get(), rtc.get(),
961 skPaint, viewMatrix, text, xInt, yInt);
joshualitt79dfb2b2015-05-11 08:58:08 -0700962}
963
964#endif