blob: 946bf1371a1ed8cb90349f0ca2ae4822b8331a48 [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 */
7#include "GrAtlasTextContext.h"
robertphillips73c4e642016-03-02 11:36:59 -08008#include "GrContext.h"
Robert Phillipsf35fd8d2018-01-22 10:48:15 -05009#include "GrContextPriv.h"
joshualittb7133be2015-04-08 09:08:31 -070010#include "GrTextBlobCache.h"
Brian Salomon52db9402017-11-07 14:58:55 -050011#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070012#include "SkDraw.h"
13#include "SkDrawFilter.h"
Brian Salomon52db9402017-11-07 14:58:55 -050014#include "SkFindAndPlaceGlyph.h"
Brian Osman3b655982017-03-07 16:58:08 -050015#include "SkGr.h"
Jim Van Verthc65b65d2018-01-16 16:26:35 -050016#include "SkGraphics.h"
Brian Salomonaf597482017-11-07 16:23:34 -050017#include "SkMakeUnique.h"
Brian Salomon649a3412017-03-09 13:50:43 -050018#include "ops/GrMeshDrawOp.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070019
Brian Salomonaf597482017-11-07 16:23:34 -050020// DF sizes and thresholds for usage of the small and medium sizes. For example, above
21// kSmallDFFontLimit we will use the medium size. The large size is used up until the size at
22// which we switch over to drawing as paths as controlled by Options.
23static const int kSmallDFFontSize = 32;
24static const int kSmallDFFontLimit = 32;
25static const int kMediumDFFontSize = 72;
26static const int kMediumDFFontLimit = 72;
27static const int kLargeDFFontSize = 162;
28
29static const int kDefaultMinDistanceFieldFontSize = 18;
30#ifdef SK_BUILD_FOR_ANDROID
31static const int kDefaultMaxDistanceFieldFontSize = 384;
32#else
33static const int kDefaultMaxDistanceFieldFontSize = 2 * kLargeDFFontSize;
34#endif
35
36GrAtlasTextContext::GrAtlasTextContext(const Options& options)
37 : fDistanceAdjustTable(new GrDistanceFieldAdjustTable) {
38 fMaxDistanceFieldFontSize = options.fMaxDistanceFieldFontSize < 0.f
39 ? kDefaultMaxDistanceFieldFontSize
40 : options.fMaxDistanceFieldFontSize;
41 fMinDistanceFieldFontSize = options.fMinDistanceFieldFontSize < 0.f
42 ? kDefaultMinDistanceFieldFontSize
43 : options.fMinDistanceFieldFontSize;
Brian Salomonb5086962017-12-13 10:59:33 -050044 fDistanceFieldVerticesAlwaysHaveW = options.fDistanceFieldVerticesAlwaysHaveW;
joshualitt9bd2daf2015-04-17 09:30:06 -070045}
46
Brian Salomonaf597482017-11-07 16:23:34 -050047std::unique_ptr<GrAtlasTextContext> GrAtlasTextContext::Make(const Options& options) {
48 return std::unique_ptr<GrAtlasTextContext>(new GrAtlasTextContext(options));
joshualitt1d89e8d2015-04-01 12:40:54 -070049}
50
Jim Van Verthc65b65d2018-01-16 16:26:35 -050051bool GrAtlasTextContext::canDraw(const GrAtlasGlyphCache* fontCache,
52 const SkPaint& skPaint,
joshualitt2c89bc12016-02-11 05:42:30 -080053 const SkMatrix& viewMatrix,
joshualitt27004b72016-02-11 12:00:33 -080054 const SkSurfaceProps& props,
55 const GrShaderCaps& shaderCaps) {
Brian Salomonaf597482017-11-07 16:23:34 -050056 return this->canDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) ||
Jim Van Verthc65b65d2018-01-16 16:26:35 -050057 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix, fontCache->getGlyphSizeLimit());
joshualitt1d89e8d2015-04-01 12:40:54 -070058}
59
Brian Salomon6f1d36c2017-01-13 12:02:17 -050060SkColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
61 SkColor canonicalColor = paint.computeLuminanceColor();
joshualitt9e36c1a2015-04-14 12:17:27 -070062 if (lcd) {
63 // This is the correct computation, but there are tons of cases where LCD can be overridden.
64 // For now we just regenerate if any run in a textblob has LCD.
65 // TODO figure out where all of these overrides are and see if we can incorporate that logic
66 // at a higher level *OR* use sRGB
67 SkASSERT(false);
68 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
69 } else {
70 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
71 // gamma corrected masks anyways, nor color
72 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
73 SkColorGetG(canonicalColor),
74 SkColorGetB(canonicalColor));
75 // reduce to our finite number of bits
76 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
77 }
78 return canonicalColor;
79}
80
Brian Salomonf3569f02017-10-24 12:52:33 -040081uint32_t GrAtlasTextContext::ComputeScalerContextFlags(const GrColorSpaceInfo& colorSpaceInfo) {
brianosman5280dcb2016-04-14 06:02:59 -070082 // If we're doing gamma-correct rendering, then we can disable the gamma hacks.
83 // Otherwise, leave them on. In either case, we still want the contrast boost:
Brian Salomonf3569f02017-10-24 12:52:33 -040084 if (colorSpaceInfo.isGammaCorrect()) {
brianosmana1e8f8d2016-04-08 06:47:54 -070085 return SkPaint::kBoostContrast_ScalerContextFlag;
brianosman32f77822016-04-07 06:25:45 -070086 } else {
brianosmana1e8f8d2016-04-08 06:47:54 -070087 return SkPaint::kFakeGammaAndBoostContrast_ScalerContextFlags;
brianosman32f77822016-04-07 06:25:45 -070088 }
89}
90
joshualitt9e36c1a2015-04-14 12:17:27 -070091// TODO if this function ever shows up in profiling, then we can compute this value when the
92// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
93// run so this is not a big deal to compute here.
94bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -070095 SkTextBlobRunIterator it(blob);
joshualitt9e36c1a2015-04-14 12:17:27 -070096 for (; !it.done(); it.next()) {
97 if (it.isLCD()) {
98 return true;
99 }
100 }
101 return false;
102}
103
Brian Salomonf18b1d82017-10-27 11:30:49 -0400104void GrAtlasTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target,
robertphillipsccb1b572015-05-27 11:02:55 -0700105 const GrClip& clip, const SkPaint& skPaint,
Brian Salomonf18b1d82017-10-27 11:30:49 -0400106 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
107 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700108 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700109 // If we have been abandoned, then don't draw
joshualitt27004b72016-02-11 12:00:33 -0800110 if (context->abandoned()) {
robertphillipsea461502015-05-26 11:38:03 -0700111 return;
112 }
113
Hal Canary144caf52016-11-07 17:57:18 -0500114 sk_sp<GrAtlasTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700115 SkMaskFilter::BlurRec blurRec;
joshualitt374b2f72015-07-21 08:05:03 -0700116 GrAtlasTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700117 // It might be worth caching these things, but its not clear at this time
118 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
119 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700120 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700121 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700122 drawFilter);
Brian Salomonf18b1d82017-10-27 11:30:49 -0400123 uint32_t scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
joshualitt2a0e9f32015-04-13 06:12:21 -0700124
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500125 auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
126 GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
127
joshualitt2a0e9f32015-04-13 06:12:21 -0700128 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700129 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700130
131 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
joshualitt2c89bc12016-02-11 05:42:30 -0800132 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700133 kUnknown_SkPixelGeometry;
134
joshualitt9e36c1a2015-04-14 12:17:27 -0700135 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
136 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
137 // ensure we always match the same key
138 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
139 ComputeCanonicalColor(skPaint, hasLCD);
140
joshualitte4cee1f2015-05-11 13:04:28 -0700141 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700142 key.fUniqueID = blob->uniqueID();
143 key.fStyle = skPaint.getStyle();
144 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700145 key.fCanonicalColor = canonicalColor;
brianosman8d7ffce2016-04-21 08:29:06 -0700146 key.fScalerContextFlags = scalerContextFlags;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500147 cacheBlob = textBlobCache->find(key);
joshualitt2a0e9f32015-04-13 06:12:21 -0700148 }
149
Brian Salomonf18b1d82017-10-27 11:30:49 -0400150 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
joshualittb7133be2015-04-08 09:08:31 -0700151 if (cacheBlob) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500152 if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700153 // We have to remake the blob because changes may invalidate our masks.
154 // TODO we could probably get away reuse most of the time if the pointer is unique,
155 // but we'd have to clear the subrun information
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500156 textBlobCache->remove(cacheBlob.get());
157 cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
158 this->regenerateTextBlob(cacheBlob.get(), atlasGlyphCache,
Brian Salomonaf597482017-11-07 16:23:34 -0500159 *context->caps()->shaderCaps(), paint, scalerContextFlags,
160 viewMatrix, props, blob, x, y, drawFilter);
joshualittb7133be2015-04-08 09:08:31 -0700161 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500162 textBlobCache->makeMRU(cacheBlob.get());
joshualitt2f2ee832016-02-10 08:52:24 -0800163
164 if (CACHE_SANITY_CHECK) {
joshualitt259fbf12015-07-21 11:39:34 -0700165 int glyphCount = 0;
166 int runCount = 0;
167 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500168 sk_sp<GrAtlasTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
joshualitt92303772016-02-10 11:55:52 -0800169 sanityBlob->setupKey(key, blurRec, skPaint);
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500170 this->regenerateTextBlob(sanityBlob.get(), atlasGlyphCache,
Brian Salomonaf597482017-11-07 16:23:34 -0500171 *context->caps()->shaderCaps(), paint, scalerContextFlags,
172 viewMatrix, props, blob, x, y, drawFilter);
joshualitt259fbf12015-07-21 11:39:34 -0700173 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
174 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700175 }
176 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700177 if (canCache) {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500178 cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
joshualitt2a0e9f32015-04-13 06:12:21 -0700179 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500180 cacheBlob = textBlobCache->makeBlob(blob);
joshualitt2a0e9f32015-04-13 06:12:21 -0700181 }
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500182 this->regenerateTextBlob(cacheBlob.get(), atlasGlyphCache,
Brian Salomonaf597482017-11-07 16:23:34 -0500183 *context->caps()->shaderCaps(), paint, scalerContextFlags,
184 viewMatrix, props, blob, x, y, drawFilter);
joshualitt1d89e8d2015-04-01 12:40:54 -0700185 }
186
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500187 cacheBlob->flushCached(atlasGlyphCache, target, blob, props, fDistanceAdjustTable.get(), paint,
Brian Salomonf18b1d82017-10-27 11:30:49 -0400188 drawFilter, clip, viewMatrix, clipBounds, x, y);
joshualitt1d89e8d2015-04-01 12:40:54 -0700189}
190
Brian Salomonaf597482017-11-07 16:23:34 -0500191void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
Brian Salomonf856fd12016-12-16 14:24:34 -0500192 GrAtlasGlyphCache* fontCache,
joshualitt27004b72016-02-11 12:00:33 -0800193 const GrShaderCaps& shaderCaps,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500194 const GrTextUtils::Paint& paint,
Jim Van Verth58c3cce2017-10-19 15:50:24 -0400195 uint32_t scalerContextFlags,
196 const SkMatrix& viewMatrix,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500197 const SkSurfaceProps& props, const SkTextBlob* blob,
Brian Salomonaf597482017-11-07 16:23:34 -0500198 SkScalar x, SkScalar y,
199 SkDrawFilter* drawFilter) const {
Jim Van Verthbc2cdd12017-06-08 11:14:35 -0400200 cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y);
joshualitt1d89e8d2015-04-01 12:40:54 -0700201
202 // Regenerate textblob
halcanary33779752015-10-27 14:01:05 -0700203 SkTextBlobRunIterator it(blob);
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500204 GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
joshualitt1d89e8d2015-04-01 12:40:54 -0700205 for (int run = 0; !it.done(); it.next(), run++) {
206 int glyphCount = it.glyphCount();
207 size_t textLen = glyphCount * sizeof(uint16_t);
208 const SkPoint& offset = it.offset();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500209 cacheBlob->push_back_run(run);
210 if (!runPaint.modifyForRun(it)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700211 continue;
212 }
Brian Salomonaf597482017-11-07 16:23:34 -0500213 if (this->canDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700214 switch (it.positioning()) {
215 case SkTextBlob::kDefault_Positioning: {
Brian Salomonaf597482017-11-07 16:23:34 -0500216 this->drawDFText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
217 viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
218 y + offset.y());
joshualittfcfb9fc2015-04-21 07:35:10 -0700219 break;
220 }
221 case SkTextBlob::kHorizontal_Positioning: {
joshualitt5425a9a2015-12-11 11:05:43 -0800222 SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
Brian Salomonaf597482017-11-07 16:23:34 -0500223 this->drawDFPosText(cacheBlob, run, fontCache, props, runPaint,
224 scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
225 textLen, it.pos(), 1, dfOffset);
joshualittfcfb9fc2015-04-21 07:35:10 -0700226 break;
227 }
228 case SkTextBlob::kFull_Positioning: {
joshualitt5425a9a2015-12-11 11:05:43 -0800229 SkPoint dfOffset = SkPoint::Make(x, y);
Brian Salomonaf597482017-11-07 16:23:34 -0500230 this->drawDFPosText(cacheBlob, run, fontCache, props, runPaint,
231 scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
232 textLen, it.pos(), 2, dfOffset);
joshualittfcfb9fc2015-04-21 07:35:10 -0700233 break;
234 }
235 }
joshualittfcfb9fc2015-04-21 07:35:10 -0700236 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
Jim Van Verthf4c13162018-01-11 16:40:24 -0500237 cacheBlob->setRunTooBigForAtlas(run);
joshualittfcfb9fc2015-04-21 07:35:10 -0700238 } else {
joshualittfcfb9fc2015-04-21 07:35:10 -0700239 switch (it.positioning()) {
240 case SkTextBlob::kDefault_Positioning:
Brian Salomon52db9402017-11-07 14:58:55 -0500241 DrawBmpText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
242 viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
243 y + offset.y());
joshualittfcfb9fc2015-04-21 07:35:10 -0700244 break;
245 case SkTextBlob::kHorizontal_Positioning:
Brian Salomon52db9402017-11-07 14:58:55 -0500246 DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
247 viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500248 SkPoint::Make(x, y + offset.y()), SK_Scalar1);
joshualittfcfb9fc2015-04-21 07:35:10 -0700249 break;
250 case SkTextBlob::kFull_Positioning:
Brian Salomon52db9402017-11-07 14:58:55 -0500251 DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
252 viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500253 SkPoint::Make(x, y), SK_Scalar1);
joshualittfcfb9fc2015-04-21 07:35:10 -0700254 break;
255 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700256 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700257 }
258}
259
Florin Malitac337c9e2017-03-10 18:02:29 +0000260inline sk_sp<GrAtlasTextBlob>
Brian Salomonaf597482017-11-07 16:23:34 -0500261GrAtlasTextContext::makeDrawTextBlob(GrTextBlobCache* blobCache,
Florin Malitac337c9e2017-03-10 18:02:29 +0000262 GrAtlasGlyphCache* fontCache,
263 const GrShaderCaps& shaderCaps,
264 const GrTextUtils::Paint& paint,
265 uint32_t scalerContextFlags,
266 const SkMatrix& viewMatrix,
267 const SkSurfaceProps& props,
268 const char text[], size_t byteLength,
Brian Salomonaf597482017-11-07 16:23:34 -0500269 SkScalar x, SkScalar y) const {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500270 int glyphCount = paint.skPaint().countText(text, byteLength);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400271 if (!glyphCount) {
272 return nullptr;
273 }
Florin Malitac337c9e2017-03-10 18:02:29 +0000274 sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
joshualitt7481e752016-01-22 06:08:48 -0800275 blob->initThrowawayBlob(viewMatrix, x, y);
joshualitta6bf4c52016-01-19 06:59:29 -0800276
Brian Salomonaf597482017-11-07 16:23:34 -0500277 if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
278 this->drawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
279 text, byteLength, x, y);
joshualitt9bd2daf2015-04-17 09:30:06 -0700280 } else {
Brian Salomon52db9402017-11-07 14:58:55 -0500281 DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
282 byteLength, x, y);
joshualitt9bd2daf2015-04-17 09:30:06 -0700283 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700284 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700285}
286
Florin Malitac337c9e2017-03-10 18:02:29 +0000287inline sk_sp<GrAtlasTextBlob>
Brian Salomonaf597482017-11-07 16:23:34 -0500288GrAtlasTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache,
Florin Malitac337c9e2017-03-10 18:02:29 +0000289 GrAtlasGlyphCache* fontCache,
290 const GrShaderCaps& shaderCaps,
291 const GrTextUtils::Paint& paint,
292 uint32_t scalerContextFlags,
293 const SkMatrix& viewMatrix,
294 const SkSurfaceProps& props,
295 const char text[], size_t byteLength,
296 const SkScalar pos[], int scalarsPerPosition, const
Brian Salomonaf597482017-11-07 16:23:34 -0500297 SkPoint& offset) const {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500298 int glyphCount = paint.skPaint().countText(text, byteLength);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400299 if (!glyphCount) {
300 return nullptr;
301 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700302
Florin Malitac337c9e2017-03-10 18:02:29 +0000303 sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
joshualitt7481e752016-01-22 06:08:48 -0800304 blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
joshualitt9bd2daf2015-04-17 09:30:06 -0700305
Brian Salomonaf597482017-11-07 16:23:34 -0500306 if (this->canDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
307 this->drawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
308 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700309 } else {
Brian Salomon52db9402017-11-07 14:58:55 -0500310 DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500311 byteLength, pos, scalarsPerPosition, offset, SK_Scalar1);
joshualitt9bd2daf2015-04-17 09:30:06 -0700312 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700313 return blob;
314}
315
Brian Salomonf18b1d82017-10-27 11:30:49 -0400316void GrAtlasTextContext::drawText(GrContext* context, GrTextUtils::Target* target,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500317 const GrClip& clip, const SkPaint& skPaint,
Brian Salomon82f44312017-01-11 13:42:54 -0500318 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
319 const char text[], size_t byteLength, SkScalar x, SkScalar y,
320 const SkIRect& regionClipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800321 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800322 return;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500323 }
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500324
325 auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
326 auto textBlobCache = context->contextPriv().getTextBlobCache();
327
Brian Salomonf18b1d82017-10-27 11:30:49 -0400328 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500329 if (this->canDraw(atlasGlyphCache, skPaint, viewMatrix, props,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500330 *context->caps()->shaderCaps())) {
Hal Canary144caf52016-11-07 17:57:18 -0500331 sk_sp<GrAtlasTextBlob> blob(
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500332 this->makeDrawTextBlob(textBlobCache, atlasGlyphCache,
Brian Salomonaf597482017-11-07 16:23:34 -0500333 *context->caps()->shaderCaps(), paint,
334 ComputeScalerContextFlags(target->colorSpaceInfo()),
335 viewMatrix, props, text, byteLength, x, y));
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400336 if (blob) {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500337 blob->flushThrowaway(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint,
338 clip, viewMatrix, regionClipBounds, x, y);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400339 }
joshualitte55750e2016-02-10 12:52:21 -0800340 return;
341 }
342
Jim Van Verthf4c13162018-01-11 16:40:24 -0500343 // fall back to drawing as a path or scaled glyph
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500344 GrTextUtils::DrawBigText(target, clip, paint, viewMatrix, text, byteLength, x, y,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500345 regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700346}
347
Brian Salomonf18b1d82017-10-27 11:30:49 -0400348void GrAtlasTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target,
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500349 const GrClip& clip, const SkPaint& skPaint,
Brian Salomon82f44312017-01-11 13:42:54 -0500350 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
351 const char text[], size_t byteLength, const SkScalar pos[],
352 int scalarsPerPosition, const SkPoint& offset,
353 const SkIRect& regionClipBounds) {
Brian Salomonf18b1d82017-10-27 11:30:49 -0400354 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
joshualitt27004b72016-02-11 12:00:33 -0800355 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800356 return;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500357 }
358
359 auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
360 auto textBlobCache = context->contextPriv().getTextBlobCache();
361
362 if (this->canDraw(atlasGlyphCache, skPaint, viewMatrix, props,
363 *context->caps()->shaderCaps())) {
Brian Salomonaf597482017-11-07 16:23:34 -0500364 sk_sp<GrAtlasTextBlob> blob(this->makeDrawPosTextBlob(
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500365 textBlobCache, atlasGlyphCache,
Brian Salomonaf597482017-11-07 16:23:34 -0500366 *context->caps()->shaderCaps(), paint,
367 ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
368 byteLength, pos, scalarsPerPosition, offset));
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400369 if (blob) {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500370 blob->flushThrowaway(atlasGlyphCache, target, props, fDistanceAdjustTable.get(), paint,
371 clip, viewMatrix, regionClipBounds, offset.fX, offset.fY);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400372 }
joshualitte55750e2016-02-10 12:52:21 -0800373 return;
374 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700375
Jim Van Verthf4c13162018-01-11 16:40:24 -0500376 // fall back to drawing as a path or scaled glyph
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500377 GrTextUtils::DrawBigPosText(target, props, clip, paint, viewMatrix, text,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500378 byteLength, pos, scalarsPerPosition, offset, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700379}
380
Brian Salomon52db9402017-11-07 14:58:55 -0500381void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
382 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
383 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
384 const SkMatrix& viewMatrix, const char text[],
385 size_t byteLength, SkScalar x, SkScalar y) {
386 SkASSERT(byteLength == 0 || text != nullptr);
387
388 // nothing to draw
389 if (text == nullptr || byteLength == 0) {
390 return;
391 }
392
393 // Ensure the blob is set for bitmaptext
394 blob->setHasBitmap();
395
396 GrAtlasTextStrike* currStrike = nullptr;
397
398 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
399 SkFindAndPlaceGlyph::ProcessText(paint.skPaint().getTextEncoding(), text, byteLength, {x, y},
400 viewMatrix, paint.skPaint().getTextAlign(), cache,
401 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
402 position += rounding;
403 BmpAppendGlyph(blob, runIndex, fontCache, &currStrike,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500404 glyph, SkScalarFloorToScalar(position.fX),
405 SkScalarFloorToScalar(position.fY),
Jim Van Verthf4c13162018-01-11 16:40:24 -0500406 paint.filteredPremulColor(), cache,
407 SK_Scalar1);
Brian Salomon52db9402017-11-07 14:58:55 -0500408 });
409
410 SkGlyphCache::AttachCache(cache);
411}
412
413void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
414 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
415 const GrTextUtils::Paint& paint,
416 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
417 const char text[], size_t byteLength, const SkScalar pos[],
Jim Van Verthf4c13162018-01-11 16:40:24 -0500418 int scalarsPerPosition, const SkPoint& offset,
419 SkScalar textRatio) {
Brian Salomon52db9402017-11-07 14:58:55 -0500420 SkASSERT(byteLength == 0 || text != nullptr);
421 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
422
423 // nothing to draw
424 if (text == nullptr || byteLength == 0) {
425 return;
426 }
427
428 // Ensure the blob is set for bitmaptext
429 blob->setHasBitmap();
430
431 GrAtlasTextStrike* currStrike = nullptr;
432
433 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
434
435 SkFindAndPlaceGlyph::ProcessPosText(
436 paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
437 scalarsPerPosition, paint.skPaint().getTextAlign(), cache,
438 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
439 position += rounding;
440 BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500441 SkScalarFloorToScalar(position.fX),
442 SkScalarFloorToScalar(position.fY),
Jim Van Verthf4c13162018-01-11 16:40:24 -0500443 paint.filteredPremulColor(), cache, textRatio);
Brian Salomon52db9402017-11-07 14:58:55 -0500444 });
445
446 SkGlyphCache::AttachCache(cache);
447}
448
449void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
450 GrAtlasGlyphCache* fontCache, GrAtlasTextStrike** strike,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500451 const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
452 GrColor color, SkGlyphCache* glyphCache,
453 SkScalar textRatio) {
Brian Salomon52db9402017-11-07 14:58:55 -0500454 if (!*strike) {
Jim Van Verthf4c13162018-01-11 16:40:24 -0500455 *strike = fontCache->getStrike(glyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500456 }
457
458 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
459 skGlyph.getSubXFixed(),
460 skGlyph.getSubYFixed(),
461 GrGlyph::kCoverage_MaskStyle);
Jim Van Verthf4c13162018-01-11 16:40:24 -0500462 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500463 if (!glyph) {
464 return;
465 }
466
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500467 SkASSERT(skGlyph.fWidth == glyph->width());
468 SkASSERT(skGlyph.fHeight == glyph->height());
469
Jim Van Verthf4c13162018-01-11 16:40:24 -0500470 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
471 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
472 SkScalar width = SkIntToScalar(glyph->fBounds.width());
473 SkScalar height = SkIntToScalar(glyph->fBounds.height());
Brian Salomon52db9402017-11-07 14:58:55 -0500474
Jim Van Verthf4c13162018-01-11 16:40:24 -0500475 dx *= textRatio;
476 dy *= textRatio;
477 width *= textRatio;
478 height *= textRatio;
Brian Salomon52db9402017-11-07 14:58:55 -0500479
Jim Van Verthf4c13162018-01-11 16:40:24 -0500480 SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
Brian Salomon52db9402017-11-07 14:58:55 -0500481
Jim Van Verthf4c13162018-01-11 16:40:24 -0500482 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx, sy,
483 textRatio, true);
Brian Salomon52db9402017-11-07 14:58:55 -0500484}
485
Brian Salomonaf597482017-11-07 16:23:34 -0500486bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
Brian Salomon52db9402017-11-07 14:58:55 -0500487 const SkSurfaceProps& props,
Brian Salomonaf597482017-11-07 16:23:34 -0500488 const GrShaderCaps& caps) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500489 if (!viewMatrix.hasPerspective()) {
490 SkScalar maxScale = viewMatrix.getMaxScale();
491 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
492 // Hinted text looks far better at small resolutions
493 // Scaling up beyond 2x yields undesireable artifacts
Brian Salomonaf597482017-11-07 16:23:34 -0500494 if (scaledTextSize < fMinDistanceFieldFontSize ||
495 scaledTextSize > fMaxDistanceFieldFontSize) {
Brian Salomon52db9402017-11-07 14:58:55 -0500496 return false;
497 }
498
499 bool useDFT = props.isUseDeviceIndependentFonts();
500#if SK_FORCE_DISTANCE_FIELD_TEXT
501 useDFT = true;
502#endif
503
504 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
505 return false;
506 }
507 }
508
Mike Reed8ad91a92018-01-19 19:09:32 -0500509 // mask filters modify alpha, which doesn't translate well to distance
510 if (skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
Brian Salomon52db9402017-11-07 14:58:55 -0500511 return false;
512 }
513
514 // TODO: add some stroking support
515 if (skPaint.getStyle() != SkPaint::kFill_Style) {
516 return false;
517 }
518
519 return true;
520}
521
Brian Salomonaf597482017-11-07 16:23:34 -0500522void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
Brian Salomon52db9402017-11-07 14:58:55 -0500523 SkPaint* skPaint,
524 SkScalar* textRatio,
Brian Salomonaf597482017-11-07 16:23:34 -0500525 const SkMatrix& viewMatrix) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500526 SkScalar textSize = skPaint->getTextSize();
527 SkScalar scaledTextSize = textSize;
528
529 if (viewMatrix.hasPerspective()) {
530 // for perspective, we simply force to the medium size
531 // TODO: compute a size based on approximate screen area
532 scaledTextSize = kMediumDFFontLimit;
533 } else {
534 SkScalar maxScale = viewMatrix.getMaxScale();
535 // if we have non-unity scale, we need to choose our base text size
536 // based on the SkPaint's text size multiplied by the max scale factor
537 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
538 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
539 scaledTextSize *= maxScale;
540 }
541 }
542
543 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
544 // and ceiling. A scale outside of this range would require regenerating the distance fields
545 SkScalar dfMaskScaleFloor;
546 SkScalar dfMaskScaleCeil;
547 if (scaledTextSize <= kSmallDFFontLimit) {
Brian Salomonaf597482017-11-07 16:23:34 -0500548 dfMaskScaleFloor = fMinDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500549 dfMaskScaleCeil = kSmallDFFontLimit;
550 *textRatio = textSize / kSmallDFFontSize;
551 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
552 } else if (scaledTextSize <= kMediumDFFontLimit) {
553 dfMaskScaleFloor = kSmallDFFontLimit;
554 dfMaskScaleCeil = kMediumDFFontLimit;
555 *textRatio = textSize / kMediumDFFontSize;
556 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
557 } else {
558 dfMaskScaleFloor = kMediumDFFontLimit;
Brian Salomonaf597482017-11-07 16:23:34 -0500559 dfMaskScaleCeil = fMaxDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500560 *textRatio = textSize / kLargeDFFontSize;
561 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
562 }
563
564 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
565 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
566 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
567 // tolerate before we'd have to move to a large mip size. When we actually test these values
568 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
569 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
570 // level)
571 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
572 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
573
574 skPaint->setAntiAlias(true);
575 skPaint->setLCDRenderText(false);
576 skPaint->setAutohinted(false);
577 skPaint->setHinting(SkPaint::kNormal_Hinting);
578 skPaint->setSubpixelText(true);
579}
580
Brian Salomonaf597482017-11-07 16:23:34 -0500581void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex,
Brian Salomon52db9402017-11-07 14:58:55 -0500582 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
583 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
584 const SkMatrix& viewMatrix, const char text[],
Brian Salomonaf597482017-11-07 16:23:34 -0500585 size_t byteLength, SkScalar x, SkScalar y) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500586 SkASSERT(byteLength == 0 || text != nullptr);
587
588 // nothing to draw
589 if (text == nullptr || byteLength == 0) {
590 return;
591 }
592
593 const SkPaint& skPaint = paint.skPaint();
594 SkPaint::GlyphCacheProc glyphCacheProc =
595 SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), skPaint.isDevKernText(), true);
596 SkAutoDescriptor desc;
597 SkScalerContextEffects effects;
598 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
599 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
600 skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
601 nullptr);
602 SkGlyphCache* origPaintCache =
603 SkGlyphCache::DetachCache(skPaint.getTypeface(), effects, desc.getDesc());
604
605 SkTArray<SkScalar> positions;
606
607 const char* textPtr = text;
608 SkScalar stopX = 0;
609 SkScalar stopY = 0;
610 SkScalar origin = 0;
611 switch (skPaint.getTextAlign()) {
612 case SkPaint::kRight_Align: origin = SK_Scalar1; break;
613 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
614 case SkPaint::kLeft_Align: origin = 0; break;
615 }
616
617 SkAutoKern autokern;
618 const char* stop = text + byteLength;
619 while (textPtr < stop) {
620 // don't need x, y here, since all subpixel variants will have the
621 // same advance
622 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
623
624 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
625 positions.push_back(stopX + origin * width);
626
627 SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
628 positions.push_back(stopY + origin * height);
629
630 stopX += width;
631 stopY += height;
632 }
633 SkASSERT(textPtr == stop);
634
635 SkGlyphCache::AttachCache(origPaintCache);
636
637 // now adjust starting point depending on alignment
638 SkScalar alignX = stopX;
639 SkScalar alignY = stopY;
640 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
641 alignX = SkScalarHalf(alignX);
642 alignY = SkScalarHalf(alignY);
643 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
644 alignX = 0;
645 alignY = 0;
646 }
647 x -= alignX;
648 y -= alignY;
649 SkPoint offset = SkPoint::Make(x, y);
650
Brian Salomonaf597482017-11-07 16:23:34 -0500651 this->drawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix,
652 text, byteLength, positions.begin(), 2, offset);
Brian Salomon52db9402017-11-07 14:58:55 -0500653}
654
Brian Salomonaf597482017-11-07 16:23:34 -0500655void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex,
Brian Salomon52db9402017-11-07 14:58:55 -0500656 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
657 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
658 const SkMatrix& viewMatrix, const char text[],
659 size_t byteLength, const SkScalar pos[],
Brian Salomonaf597482017-11-07 16:23:34 -0500660 int scalarsPerPosition, const SkPoint& offset) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500661 SkASSERT(byteLength == 0 || text != nullptr);
662 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
663
664 // nothing to draw
665 if (text == nullptr || byteLength == 0) {
666 return;
667 }
668
669 SkTDArray<char> fallbackTxt;
670 SkTDArray<SkScalar> fallbackPos;
671
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500672 SkTDArray<char> bigFallbackTxt;
673 SkTDArray<SkScalar> bigFallbackPos;
674 SkScalar textSize = paint.skPaint().getTextSize();
675 SkScalar maxTextSize = fontCache->getGlyphSizeLimit();
676 SkScalar bigFallbackTextSize = maxTextSize;
677 SkScalar maxScale = viewMatrix.getMaxScale();
678
Brian Salomonb5086962017-12-13 10:59:33 -0500679 bool hasWCoord = viewMatrix.hasPerspective() || fDistanceFieldVerticesAlwaysHaveW;
680
Brian Salomon52db9402017-11-07 14:58:55 -0500681 // Setup distance field paint and text ratio
682 SkScalar textRatio;
683 SkPaint dfPaint(paint);
Brian Salomonaf597482017-11-07 16:23:34 -0500684 this->initDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
Brian Salomon52db9402017-11-07 14:58:55 -0500685 blob->setHasDistanceField();
686 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
Brian Salomon5c6ac642017-12-19 11:09:32 -0500687 paint.skPaint().isAntiAlias(), hasWCoord);
Brian Salomon52db9402017-11-07 14:58:55 -0500688
689 GrAtlasTextStrike* currStrike = nullptr;
690
691 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
692 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
693 SkGlyphCache* cache =
694 blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags, dfPaint, nullptr);
695 SkPaint::GlyphCacheProc glyphCacheProc =
696 SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), dfPaint.isDevKernText(), true);
697
698 const char* stop = text + byteLength;
699
Jim Van Verthf4c13162018-01-11 16:40:24 -0500700 SkPaint::Align align = dfPaint.getTextAlign();
701 SkScalar alignMul = SkPaint::kCenter_Align == align ? SK_ScalarHalf :
702 (SkPaint::kRight_Align == align ? SK_Scalar1 : 0);
703 while (text < stop) {
704 const char* lastText = text;
705 // the last 2 parameters are ignored
706 const SkGlyph& glyph = glyphCacheProc(cache, &text);
Brian Salomon52db9402017-11-07 14:58:55 -0500707
Jim Van Verthf4c13162018-01-11 16:40:24 -0500708 if (glyph.fWidth) {
709 SkScalar x = offset.x() + pos[0];
710 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
Brian Salomon52db9402017-11-07 14:58:55 -0500711
Jim Van Verthf4c13162018-01-11 16:40:24 -0500712 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
713 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
714
715 if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
716 DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500717 y - advanceY, paint.filteredPremulColor(), cache, textRatio);
Jim Van Verthf4c13162018-01-11 16:40:24 -0500718 } else {
719 // can't append color glyph to SDF batch, send to fallback
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500720 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*textRatio;
721 SkScalar scaledGlyphSize = maxDim*maxScale;
722
723 if (!viewMatrix.hasPerspective() && scaledGlyphSize > maxTextSize) {
724 bigFallbackTxt.append(SkToInt(text - lastText), lastText);
725 *bigFallbackPos.append() = maxScale*pos[0];
726 if (2 == scalarsPerPosition) {
727 *bigFallbackPos.append() = maxScale*pos[1];
728 }
729 SkScalar glyphTextSize = SkScalarFloorToScalar(maxTextSize*textSize/maxDim);
730 bigFallbackTextSize = SkTMin(glyphTextSize, bigFallbackTextSize);
731 } else {
732 fallbackTxt.append(SkToInt(text - lastText), lastText);
733 *fallbackPos.append() = pos[0];
734 if (2 == scalarsPerPosition) {
735 *fallbackPos.append() = pos[1];
736 }
Brian Salomon52db9402017-11-07 14:58:55 -0500737 }
738 }
Brian Salomon52db9402017-11-07 14:58:55 -0500739 }
Jim Van Verthf4c13162018-01-11 16:40:24 -0500740 pos += scalarsPerPosition;
Brian Salomon52db9402017-11-07 14:58:55 -0500741 }
742
743 SkGlyphCache::AttachCache(cache);
744 if (fallbackTxt.count()) {
745 blob->initOverride(runIndex);
746 GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, paint,
747 scalerContextFlags, viewMatrix, fallbackTxt.begin(),
748 fallbackTxt.count(), fallbackPos.begin(),
Jim Van Verthf4c13162018-01-11 16:40:24 -0500749 scalarsPerPosition, offset, SK_Scalar1);
Brian Salomon52db9402017-11-07 14:58:55 -0500750 }
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500751
752 if (bigFallbackTxt.count()) {
753 // Set up paint and matrix to scale glyphs
754 blob->initOverride(runIndex);
755 SkPaint largePaint(paint);
756 largePaint.setTextSize(bigFallbackTextSize);
757 // remove maxScale from viewMatrix and move it into textRatio
758 // this keeps the base glyph size consistent regardless of matrix scale
759 SkMatrix modMatrix(viewMatrix);
760 SkScalar invScale = SkScalarInvert(maxScale);
761 modMatrix.preScale(invScale, invScale);
762 SkScalar bigFallbackTextRatio = textSize*maxScale/bigFallbackTextSize;
763 SkPoint modOffset(offset);
764 modOffset *= maxScale;
765 GrTextUtils::Paint textPaint(&largePaint, paint.dstColorSpaceInfo());
766 GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, textPaint,
767 scalerContextFlags, modMatrix, bigFallbackTxt.begin(),
768 bigFallbackTxt.count(), bigFallbackPos.begin(),
769 scalarsPerPosition, modOffset, bigFallbackTextRatio);
770 }
Brian Salomon52db9402017-11-07 14:58:55 -0500771}
772
Jim Van Verthf4c13162018-01-11 16:40:24 -0500773// TODO: merge with BmpAppendGlyph
774void GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
Brian Salomon52db9402017-11-07 14:58:55 -0500775 GrAtlasGlyphCache* cache, GrAtlasTextStrike** strike,
776 const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500777 GrColor color, SkGlyphCache* glyphCache,
778 SkScalar textRatio) {
Brian Salomon52db9402017-11-07 14:58:55 -0500779 if (!*strike) {
780 *strike = cache->getStrike(glyphCache);
781 }
782
783 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
784 skGlyph.getSubXFixed(),
785 skGlyph.getSubYFixed(),
786 GrGlyph::kDistance_MaskStyle);
787 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
788 if (!glyph) {
Jim Van Verthf4c13162018-01-11 16:40:24 -0500789 return;
Brian Salomon52db9402017-11-07 14:58:55 -0500790 }
791
792 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
793 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
794 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
795 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
796
Jim Van Verthf4c13162018-01-11 16:40:24 -0500797 dx *= textRatio;
798 dy *= textRatio;
799 width *= textRatio;
800 height *= textRatio;
801 SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
Brian Salomon52db9402017-11-07 14:58:55 -0500802
Jim Van Verthf4c13162018-01-11 16:40:24 -0500803 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx, sy,
804 textRatio, false);
Brian Salomon52db9402017-11-07 14:58:55 -0500805}
806
joshualitt79dfb2b2015-05-11 08:58:08 -0700807///////////////////////////////////////////////////////////////////////////////////////////////////
808
Hal Canary6f6961e2017-01-31 13:50:44 -0500809#if GR_TEST_UTILS
joshualitt79dfb2b2015-05-11 08:58:08 -0700810
Brian Salomonf18b1d82017-10-27 11:30:49 -0400811#include "GrRenderTargetContext.h"
812
Brian Salomon44acb5b2017-07-18 19:59:24 -0400813GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
joshualitt79dfb2b2015-05-11 08:58:08 -0700814 static uint32_t gContextID = SK_InvalidGenID;
Brian Salomonaf597482017-11-07 16:23:34 -0500815 static std::unique_ptr<GrAtlasTextContext> gTextContext;
robertphillipsfcf78292015-06-19 11:49:52 -0700816 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -0700817
818 if (context->uniqueID() != gContextID) {
819 gContextID = context->uniqueID();
Brian Salomonaf597482017-11-07 16:23:34 -0500820 gTextContext = GrAtlasTextContext::Make(GrAtlasTextContext::Options());
joshualitt79dfb2b2015-05-11 08:58:08 -0700821 }
822
Brian Osman11052242016-10-27 14:47:55 -0400823 // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
Brian Osmanec8f8b02017-05-11 10:57:37 -0400824 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
Brian Osman11052242016-10-27 14:47:55 -0400825 SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
brianosman8fe485b2016-07-25 12:31:51 -0700826
joshualitt6c891102015-05-13 08:51:49 -0700827 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
Brian Salomon44acb5b2017-07-18 19:59:24 -0400828
829 // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
830 // param.
joshualitt79dfb2b2015-05-11 08:58:08 -0700831 SkPaint skPaint;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500832 skPaint.setColor(random->nextU());
joshualitt79dfb2b2015-05-11 08:58:08 -0700833 skPaint.setLCDRenderText(random->nextBool());
834 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
835 skPaint.setSubpixelText(random->nextBool());
Brian Salomon4cbb6e62017-10-25 15:12:19 -0400836 GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
joshualitt79dfb2b2015-05-11 08:58:08 -0700837
joshualitt79dfb2b2015-05-11 08:58:08 -0700838 const char* text = "The quick brown fox jumps over the lazy dog.";
839 int textLen = (int)strlen(text);
840
joshualittb8d86492016-02-24 09:23:03 -0800841 // create some random x/y offsets, including negative offsets
842 static const int kMaxTrans = 1024;
843 int xPos = (random->nextU() % 2) * 2 - 1;
844 int yPos = (random->nextU() % 2) * 2 - 1;
845 int xInt = (random->nextU() % kMaxTrans) * xPos;
846 int yInt = (random->nextU() % kMaxTrans) * yPos;
847 SkScalar x = SkIntToScalar(xInt);
848 SkScalar y = SkIntToScalar(yInt);
halcanary9d524f22016-03-29 09:03:52 -0700849
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500850 auto atlasGlyphCache = context->contextPriv().getAtlasGlyphCache();
851
Brian Salomon09d994e2016-12-21 11:14:46 -0500852 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
853 // test the text op with this unit test, that is okay.
Brian Salomonaf597482017-11-07 16:23:34 -0500854 sk_sp<GrAtlasTextBlob> blob(gTextContext->makeDrawTextBlob(
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500855 context->contextPriv().getTextBlobCache(), atlasGlyphCache,
Brian Salomon44acb5b2017-07-18 19:59:24 -0400856 *context->caps()->shaderCaps(), utilsPaint,
Brian Salomon5ec9def2016-12-20 15:34:05 -0500857 GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, gSurfaceProps, text,
858 static_cast<size_t>(textLen), x, y));
joshualitt79dfb2b2015-05-11 08:58:08 -0700859
Brian Salomon44acb5b2017-07-18 19:59:24 -0400860 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, gSurfaceProps,
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500861 gTextContext->dfAdjustTable(), atlasGlyphCache,
Brian Salomonf18b1d82017-10-27 11:30:49 -0400862 rtc->textTarget());
joshualitt79dfb2b2015-05-11 08:58:08 -0700863}
864
865#endif