blob: 6da4c0d014513df417a478df3b7ec5bd42c7a312 [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"
17#include "SkDrawFilter.h"
Jim Van Verth54d9c882018-02-08 16:14:48 -050018#include "SkDrawProcs.h"
Brian Salomon52db9402017-11-07 14:58:55 -050019#include "SkFindAndPlaceGlyph.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040020#include "SkGlyphRun.h"
Brian Osman3b655982017-03-07 16:58:08 -050021#include "SkGr.h"
Jim Van Verthc65b65d2018-01-16 16:26:35 -050022#include "SkGraphics.h"
Brian Salomonaf597482017-11-07 16:23:34 -050023#include "SkMakeUnique.h"
Mike Reed80747ef2018-01-23 15:29:32 -050024#include "SkMaskFilterBase.h"
Herb Derbyeb3f6742018-03-05 14:36:45 -050025#include "SkPaintPriv.h"
Jim Van Verth54d9c882018-02-08 16:14:48 -050026#include "SkTextMapStateProc.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040027#include "SkTo.h"
Brian Salomon649a3412017-03-09 13:50:43 -050028#include "ops/GrMeshDrawOp.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070029
Brian Salomonaf597482017-11-07 16:23:34 -050030// DF sizes and thresholds for usage of the small and medium sizes. For example, above
31// kSmallDFFontLimit we will use the medium size. The large size is used up until the size at
32// which we switch over to drawing as paths as controlled by Options.
33static const int kSmallDFFontSize = 32;
34static const int kSmallDFFontLimit = 32;
Jim Van Verth825d4d72018-01-30 20:37:03 +000035static const int kMediumDFFontSize = 72;
36static const int kMediumDFFontLimit = 72;
37static const int kLargeDFFontSize = 162;
Brian Salomonaf597482017-11-07 16:23:34 -050038
39static const int kDefaultMinDistanceFieldFontSize = 18;
40#ifdef SK_BUILD_FOR_ANDROID
41static const int kDefaultMaxDistanceFieldFontSize = 384;
42#else
43static const int kDefaultMaxDistanceFieldFontSize = 2 * kLargeDFFontSize;
44#endif
45
Herb Derby26cbe512018-05-24 14:39:01 -040046GrTextContext::GrTextContext(const Options& options)
Khushal3e7548c2018-05-23 15:45:01 -070047 : fDistanceAdjustTable(new GrDistanceFieldAdjustTable), fOptions(options) {
48 SanitizeOptions(&fOptions);
joshualitt9bd2daf2015-04-17 09:30:06 -070049}
50
Herb Derby26cbe512018-05-24 14:39:01 -040051std::unique_ptr<GrTextContext> GrTextContext::Make(const Options& options) {
52 return std::unique_ptr<GrTextContext>(new GrTextContext(options));
joshualitt1d89e8d2015-04-01 12:40:54 -070053}
54
Herb Derby26cbe512018-05-24 14:39:01 -040055SkColor GrTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -050056 SkColor canonicalColor = paint.computeLuminanceColor();
joshualitt9e36c1a2015-04-14 12:17:27 -070057 if (lcd) {
58 // This is the correct computation, but there are tons of cases where LCD can be overridden.
59 // For now we just regenerate if any run in a textblob has LCD.
60 // TODO figure out where all of these overrides are and see if we can incorporate that logic
61 // at a higher level *OR* use sRGB
62 SkASSERT(false);
63 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
64 } else {
65 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
66 // gamma corrected masks anyways, nor color
67 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
68 SkColorGetG(canonicalColor),
69 SkColorGetB(canonicalColor));
70 // reduce to our finite number of bits
71 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
72 }
73 return canonicalColor;
74}
75
Herb Derby26cbe512018-05-24 14:39:01 -040076SkScalerContextFlags GrTextContext::ComputeScalerContextFlags(
Herb Derbyd8327a82018-01-22 14:39:27 -050077 const GrColorSpaceInfo& colorSpaceInfo) {
brianosman5280dcb2016-04-14 06:02:59 -070078 // If we're doing gamma-correct rendering, then we can disable the gamma hacks.
79 // Otherwise, leave them on. In either case, we still want the contrast boost:
Brian Salomonf3569f02017-10-24 12:52:33 -040080 if (colorSpaceInfo.isGammaCorrect()) {
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
joshualitt9e36c1a2015-04-14 12:17:27 -070087// TODO if this function ever shows up in profiling, then we can compute this value when the
88// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
89// run so this is not a big deal to compute here.
Herb Derby26cbe512018-05-24 14:39:01 -040090bool GrTextContext::HasLCD(const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -070091 SkTextBlobRunIterator it(blob);
joshualitt9e36c1a2015-04-14 12:17:27 -070092 for (; !it.done(); it.next()) {
93 if (it.isLCD()) {
94 return true;
95 }
96 }
97 return false;
98}
99
Herb Derby26cbe512018-05-24 14:39:01 -0400100void GrTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target,
101 const GrClip& clip, const SkPaint& skPaint,
102 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
103 const SkTextBlob* blob, SkScalar x, SkScalar y,
104 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700105 // If we have been abandoned, then don't draw
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500106 if (context->contextPriv().abandoned()) {
robertphillipsea461502015-05-26 11:38:03 -0700107 return;
108 }
109
Herb Derby86240592018-05-24 16:12:31 -0400110 sk_sp<GrTextBlob> cacheBlob;
Mike Reed80747ef2018-01-23 15:29:32 -0500111 SkMaskFilterBase::BlurRec blurRec;
Herb Derby86240592018-05-24 16:12:31 -0400112 GrTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700113 // It might be worth caching these things, but its not clear at this time
114 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
115 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700116 bool canCache = !(skPaint.getPathEffect() ||
Mike Reed80747ef2018-01-23 15:29:32 -0500117 (mf && !as_MFB(mf)->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700118 drawFilter);
Herb Derbyd8327a82018-01-22 14:39:27 -0500119 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
joshualitt2a0e9f32015-04-13 06:12:21 -0700120
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500121 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500122 GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
123
joshualitt2a0e9f32015-04-13 06:12:21 -0700124 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700125 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700126
127 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
joshualitt2c89bc12016-02-11 05:42:30 -0800128 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700129 kUnknown_SkPixelGeometry;
130
joshualitt9e36c1a2015-04-14 12:17:27 -0700131 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
132 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
133 // ensure we always match the same key
134 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
135 ComputeCanonicalColor(skPaint, hasLCD);
136
joshualitte4cee1f2015-05-11 13:04:28 -0700137 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700138 key.fUniqueID = blob->uniqueID();
139 key.fStyle = skPaint.getStyle();
140 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700141 key.fCanonicalColor = canonicalColor;
brianosman8d7ffce2016-04-21 08:29:06 -0700142 key.fScalerContextFlags = scalerContextFlags;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500143 cacheBlob = textBlobCache->find(key);
joshualitt2a0e9f32015-04-13 06:12:21 -0700144 }
145
Brian Salomonf18b1d82017-10-27 11:30:49 -0400146 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
joshualittb7133be2015-04-08 09:08:31 -0700147 if (cacheBlob) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500148 if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700149 // We have to remake the blob because changes may invalidate our masks.
150 // TODO we could probably get away reuse most of the time if the pointer is unique,
151 // but we'd have to clear the subrun information
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500152 textBlobCache->remove(cacheBlob.get());
153 cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500154 this->regenerateTextBlob(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400155 *context->contextPriv().caps()->shaderCaps(), paint,
156 scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
joshualittb7133be2015-04-08 09:08:31 -0700157 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500158 textBlobCache->makeMRU(cacheBlob.get());
joshualitt2f2ee832016-02-10 08:52:24 -0800159
160 if (CACHE_SANITY_CHECK) {
joshualitt259fbf12015-07-21 11:39:34 -0700161 int glyphCount = 0;
162 int runCount = 0;
163 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
Herb Derby86240592018-05-24 16:12:31 -0400164 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount));
joshualitt92303772016-02-10 11:55:52 -0800165 sanityBlob->setupKey(key, blurRec, skPaint);
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400166 this->regenerateTextBlob(
167 sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(),
168 paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
Herb Derby86240592018-05-24 16:12:31 -0400169 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700170 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700171 }
172 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700173 if (canCache) {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500174 cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint);
joshualitt2a0e9f32015-04-13 06:12:21 -0700175 } else {
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500176 cacheBlob = textBlobCache->makeBlob(blob);
joshualitt2a0e9f32015-04-13 06:12:21 -0700177 }
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500178 this->regenerateTextBlob(cacheBlob.get(), glyphCache,
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400179 *context->contextPriv().caps()->shaderCaps(), paint,
180 scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter);
joshualitt1d89e8d2015-04-01 12:40:54 -0700181 }
182
Robert Phillips5a66efb2018-03-07 15:13:18 -0500183 cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500184 clip, viewMatrix, clipBounds, x, y);
joshualitt1d89e8d2015-04-01 12:40:54 -0700185}
186
Herb Derby86240592018-05-24 16:12:31 -0400187void GrTextContext::regenerateTextBlob(GrTextBlob* cacheBlob,
Herb Derby26cbe512018-05-24 14:39:01 -0400188 GrGlyphCache* glyphCache,
189 const GrShaderCaps& shaderCaps,
190 const GrTextUtils::Paint& paint,
191 SkScalerContextFlags scalerContextFlags,
192 const SkMatrix& viewMatrix,
193 const SkSurfaceProps& props, const SkTextBlob* blob,
194 SkScalar x, SkScalar y,
195 SkDrawFilter* drawFilter) const {
Jim Van Verthbc2cdd12017-06-08 11:14:35 -0400196 cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y);
joshualitt1d89e8d2015-04-01 12:40:54 -0700197
198 // Regenerate textblob
halcanary33779752015-10-27 14:01:05 -0700199 SkTextBlobRunIterator it(blob);
Ben Wagnerd234afd2018-04-13 15:50:01 -0400200 GrTextUtils::RunPaint runPaint(&paint, drawFilter);
joshualitt1d89e8d2015-04-01 12:40:54 -0700201 for (int run = 0; !it.done(); it.next(), run++) {
202 int glyphCount = it.glyphCount();
203 size_t textLen = glyphCount * sizeof(uint16_t);
204 const SkPoint& offset = it.offset();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500205 cacheBlob->push_back_run(run);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500206 if (!runPaint.modifyForRun([it](SkPaint* p) { it.applyFontToPaint(p); })) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700207 continue;
208 }
Jim Van Verth54d9c882018-02-08 16:14:48 -0500209 cacheBlob->setRunPaintFlags(run, runPaint.skPaint().getFlags());
210
Khushal3e7548c2018-05-23 15:45:01 -0700211 if (CanDrawAsDistanceFields(runPaint, viewMatrix, props,
212 shaderCaps.supportsDistanceFieldText(), fOptions)) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700213 switch (it.positioning()) {
214 case SkTextBlob::kDefault_Positioning: {
Herb Derby41f4f312018-06-06 17:45:53 +0000215 auto origin = SkPoint::Make(x + offset.x(), y + offset.y());
Herb Derby59d997a2018-06-07 12:44:09 -0400216 SkGlyphRunBuilder builder;
217 builder.prepareDrawText(runPaint.skPaint(),
218 (const char*)it.glyphs(), textLen, origin);
Herb Derby41f4f312018-06-06 17:45:53 +0000219
Herb Derby9d85d632018-06-18 16:25:52 -0400220 auto glyphRun = builder.useGlyphRun();
221
222 glyphRun->temporaryShuntToCallback(
Herb Derby59d997a2018-06-07 12:44:09 -0400223 [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
224 this->drawDFPosText(
225 cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
226 viewMatrix, glyphIDs, 2 * runSize, pos, 2,
227 SkPoint::Make(0,0));
228 });
joshualittfcfb9fc2015-04-21 07:35:10 -0700229 break;
230 }
Herb Derby41f4f312018-06-06 17:45:53 +0000231
joshualittfcfb9fc2015-04-21 07:35:10 -0700232 case SkTextBlob::kHorizontal_Positioning: {
joshualitt5425a9a2015-12-11 11:05:43 -0800233 SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500234 this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
Brian Salomonaf597482017-11-07 16:23:34 -0500235 scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
236 textLen, it.pos(), 1, dfOffset);
joshualittfcfb9fc2015-04-21 07:35:10 -0700237 break;
238 }
239 case SkTextBlob::kFull_Positioning: {
joshualitt5425a9a2015-12-11 11:05:43 -0800240 SkPoint dfOffset = SkPoint::Make(x, y);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500241 this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint,
Brian Salomonaf597482017-11-07 16:23:34 -0500242 scalerContextFlags, viewMatrix, (const char*)it.glyphs(),
243 textLen, it.pos(), 2, dfOffset);
joshualittfcfb9fc2015-04-21 07:35:10 -0700244 break;
245 }
246 }
joshualittfcfb9fc2015-04-21 07:35:10 -0700247 } else {
joshualittfcfb9fc2015-04-21 07:35:10 -0700248 switch (it.positioning()) {
Herb Derby41f4f312018-06-06 17:45:53 +0000249 case SkTextBlob::kDefault_Positioning: {
250 auto origin = SkPoint::Make(x + offset.x(), y + offset.y());
Herb Derby59d997a2018-06-07 12:44:09 -0400251 SkGlyphRunBuilder builder;
252 builder.prepareDrawText(runPaint.skPaint(),
253 (const char*)it.glyphs(), textLen, origin);
Herb Derby41f4f312018-06-06 17:45:53 +0000254
Herb Derby9d85d632018-06-18 16:25:52 -0400255 auto glyphRun = builder.useGlyphRun();
256
257 glyphRun->temporaryShuntToCallback(
Herb Derby59d997a2018-06-07 12:44:09 -0400258 [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
259 this->DrawBmpPosText(
260 cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
261 viewMatrix, glyphIDs, 2 * runSize,
262 pos, 2, SkPoint::Make(0, 0));
263 });
joshualittfcfb9fc2015-04-21 07:35:10 -0700264 break;
Herb Derby41f4f312018-06-06 17:45:53 +0000265 }
joshualittfcfb9fc2015-04-21 07:35:10 -0700266 case SkTextBlob::kHorizontal_Positioning:
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500267 DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
Brian Salomon52db9402017-11-07 14:58:55 -0500268 viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500269 SkPoint::Make(x, y + offset.y()));
joshualittfcfb9fc2015-04-21 07:35:10 -0700270 break;
271 case SkTextBlob::kFull_Positioning:
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500272 DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags,
Brian Salomon52db9402017-11-07 14:58:55 -0500273 viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500274 SkPoint::Make(x, y));
joshualittfcfb9fc2015-04-21 07:35:10 -0700275 break;
276 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700277 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700278 }
279}
280
Herb Derby86240592018-05-24 16:12:31 -0400281inline sk_sp<GrTextBlob>
Herb Derby26cbe512018-05-24 14:39:01 -0400282GrTextContext::makeDrawPosTextBlob(GrTextBlobCache* blobCache,
283 GrGlyphCache* glyphCache,
284 const GrShaderCaps& shaderCaps,
285 const GrTextUtils::Paint& paint,
286 SkScalerContextFlags scalerContextFlags,
287 const SkMatrix& viewMatrix,
288 const SkSurfaceProps& props,
289 const char text[], size_t byteLength,
290 const SkScalar pos[], int scalarsPerPosition, const
291 SkPoint& offset) const {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500292 int glyphCount = paint.skPaint().countText(text, byteLength);
Brian Salomonb3f6ac42017-07-31 08:00:25 -0400293 if (!glyphCount) {
294 return nullptr;
295 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700296
Herb Derby86240592018-05-24 16:12:31 -0400297 sk_sp<GrTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
joshualitt7481e752016-01-22 06:08:48 -0800298 blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
Jim Van Verth54d9c882018-02-08 16:14:48 -0500299 blob->setRunPaintFlags(0, paint.skPaint().getFlags());
joshualitt9bd2daf2015-04-17 09:30:06 -0700300
Khushal3e7548c2018-05-23 15:45:01 -0700301 if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps.supportsDistanceFieldText(),
302 fOptions)) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500303 this->drawDFPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
Brian Salomonaf597482017-11-07 16:23:34 -0500304 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700305 } else {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500306 DrawBmpPosText(blob.get(), 0, glyphCache, props, paint, scalerContextFlags, viewMatrix,
307 text, byteLength, pos, scalarsPerPosition, offset);
joshualitt9bd2daf2015-04-17 09:30:06 -0700308 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700309 return blob;
310}
311
Herb Derby26cbe512018-05-24 14:39:01 -0400312void GrTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target,
313 const GrClip& clip, const SkPaint& skPaint,
314 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
315 const char text[], size_t byteLength, const SkScalar pos[],
316 int scalarsPerPosition, const SkPoint& offset,
317 const SkIRect& regionClipBounds) {
Brian Salomonf18b1d82017-10-27 11:30:49 -0400318 GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo());
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500319 if (context->contextPriv().abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800320 return;
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500321 }
322
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500323 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsf35fd8d2018-01-22 10:48:15 -0500324 auto textBlobCache = context->contextPriv().getTextBlobCache();
325
Herb Derby86240592018-05-24 16:12:31 -0400326 sk_sp<GrTextBlob> blob(this->makeDrawPosTextBlob(
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400327 textBlobCache, glyphCache, *context->contextPriv().caps()->shaderCaps(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500328 ComputeScalerContextFlags(target->colorSpaceInfo()), viewMatrix, props, text,
329 byteLength, pos, scalarsPerPosition, offset));
330 if (blob) {
Robert Phillips5a66efb2018-03-07 15:13:18 -0500331 blob->flush(target, props, fDistanceAdjustTable.get(), paint,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500332 clip, viewMatrix, regionClipBounds, offset.fX, offset.fY);
joshualitte55750e2016-02-10 12:52:21 -0800333 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700334}
335
Brian Salomon52db9402017-11-07 14:58:55 -0500336
Herb Derby86240592018-05-24 16:12:31 -0400337void GrTextContext::DrawBmpPosText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400338 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
339 const GrTextUtils::Paint& paint,
340 SkScalerContextFlags scalerContextFlags,
341 const SkMatrix& viewMatrix,
342 const char text[], size_t byteLength, const SkScalar pos[],
343 int scalarsPerPosition, const SkPoint& offset) {
Brian Salomon52db9402017-11-07 14:58:55 -0500344 SkASSERT(byteLength == 0 || text != nullptr);
345 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
346
347 // nothing to draw
348 if (text == nullptr || byteLength == 0) {
349 return;
350 }
351
352 // Ensure the blob is set for bitmaptext
353 blob->setHasBitmap();
354
Jim Van Verth54d9c882018-02-08 16:14:48 -0500355 if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500356 DrawBmpPosTextAsPaths(blob, runIndex, glyphCache, props, paint, scalerContextFlags,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500357 viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500358 return;
359 }
360
Herb Derbyab6fd7e2018-03-07 18:05:39 +0000361 sk_sp<GrTextStrike> currStrike;
Herb Derby526819d2018-03-09 12:51:12 -0500362 auto cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
Brian Salomon52db9402017-11-07 14:58:55 -0500363 SkFindAndPlaceGlyph::ProcessPosText(
364 paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
Herb Derby1e7c6582018-05-21 16:10:17 -0400365 scalarsPerPosition, cache.get(),
Brian Salomon52db9402017-11-07 14:58:55 -0500366 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
367 position += rounding;
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500368 BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500369 SkScalarFloorToScalar(position.fX),
370 SkScalarFloorToScalar(position.fY),
Jim Van Verthb515ae72018-05-23 16:44:55 -0400371 paint.filteredPremulColor(), cache.get(), SK_Scalar1, false);
Brian Salomon52db9402017-11-07 14:58:55 -0500372 });
Brian Salomon52db9402017-11-07 14:58:55 -0500373}
374
Herb Derby86240592018-05-24 16:12:31 -0400375void GrTextContext::DrawBmpPosTextAsPaths(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400376 GrGlyphCache* glyphCache,
377 const SkSurfaceProps& props,
378 const GrTextUtils::Paint& origPaint,
379 SkScalerContextFlags scalerContextFlags,
380 const SkMatrix& viewMatrix,
381 const char text[], size_t byteLength,
382 const SkScalar pos[], int scalarsPerPosition,
383 const SkPoint& offset) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500384 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
385
386 // nothing to draw
387 if (text == nullptr || byteLength == 0) {
388 return;
389 }
390
391 // setup our std paint, in hopes of getting hits in the cache
Jim Van Verthc401bb92018-02-15 14:05:24 -0500392 SkPaint pathPaint(origPaint);
393 SkScalar matrixScale = pathPaint.setupForAsPaths();
Khushalfa8ff092018-06-06 17:46:38 -0700394 FallbackTextHelper fallbackTextHelper(viewMatrix, origPaint, glyphCache->getGlyphSizeLimit(),
395 matrixScale);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500396
397 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
Jim Van Verthc401bb92018-02-15 14:05:24 -0500398 pathPaint.setStyle(SkPaint::kFill_Style);
399 pathPaint.setPathEffect(nullptr);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500400
Jim Van Verthc401bb92018-02-15 14:05:24 -0500401 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(pathPaint.getTextEncoding(),
Jim Van Verthc401bb92018-02-15 14:05:24 -0500402 true);
Herb Derbyfa996902018-04-18 11:36:12 -0400403 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
Herb Derby4e34a012018-03-21 10:47:30 -0400404 pathPaint, &props, SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500405
406 const char* stop = text + byteLength;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500407 const char* lastText = text;
Jim Van Verth54d9c882018-02-08 16:14:48 -0500408 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
409
410 while (text < stop) {
Herb Derby4e34a012018-03-21 10:47:30 -0400411 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500412 if (glyph.fWidth) {
Jim Van Verthc401bb92018-02-15 14:05:24 -0500413 SkPoint loc;
Herb Derby1e7c6582018-05-21 16:10:17 -0400414 tmsProc(pos, &loc);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500415 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
416 fallbackTextHelper.appendText(glyph, text - lastText, lastText, loc);
417 } else {
418 const SkPath* path = cache->findPath(glyph);
419 if (path) {
420 blob->appendPathGlyph(runIndex, *path, loc.fX, loc.fY, matrixScale, false);
421 }
Jim Van Verth54d9c882018-02-08 16:14:48 -0500422 }
423 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500424 lastText = text;
Jim Van Verth54d9c882018-02-08 16:14:48 -0500425 pos += scalarsPerPosition;
426 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500427
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500428 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, origPaint, scalerContextFlags);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500429}
430
Herb Derby86240592018-05-24 16:12:31 -0400431void GrTextContext::BmpAppendGlyph(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400432 GrGlyphCache* grGlyphCache,
433 sk_sp<GrTextStrike>* strike,
434 const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
435 GrColor color, SkGlyphCache* skGlyphCache,
436 SkScalar textRatio, bool needsTransform) {
Brian Salomon52db9402017-11-07 14:58:55 -0500437 if (!*strike) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500438 *strike = grGlyphCache->getStrike(skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500439 }
440
441 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
442 skGlyph.getSubXFixed(),
443 skGlyph.getSubYFixed(),
444 GrGlyph::kCoverage_MaskStyle);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500445 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500446 if (!glyph) {
447 return;
448 }
449
Jim Van Verthc65b65d2018-01-16 16:26:35 -0500450 SkASSERT(skGlyph.fWidth == glyph->width());
451 SkASSERT(skGlyph.fHeight == glyph->height());
452
Jim Van Verthf4c13162018-01-11 16:40:24 -0500453 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
454 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
455 SkScalar width = SkIntToScalar(glyph->fBounds.width());
456 SkScalar height = SkIntToScalar(glyph->fBounds.height());
Brian Salomon52db9402017-11-07 14:58:55 -0500457
Jim Van Verthf4c13162018-01-11 16:40:24 -0500458 dx *= textRatio;
459 dy *= textRatio;
460 width *= textRatio;
461 height *= textRatio;
Brian Salomon52db9402017-11-07 14:58:55 -0500462
Jim Van Verthf4c13162018-01-11 16:40:24 -0500463 SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
Brian Salomon52db9402017-11-07 14:58:55 -0500464
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500465 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
Jim Van Verthb515ae72018-05-23 16:44:55 -0400466 textRatio, !needsTransform);
Brian Salomon52db9402017-11-07 14:58:55 -0500467}
468
Herb Derby26cbe512018-05-24 14:39:01 -0400469void GrTextContext::SanitizeOptions(Options* options) {
Khushal3e7548c2018-05-23 15:45:01 -0700470 if (options->fMaxDistanceFieldFontSize < 0.f) {
471 options->fMaxDistanceFieldFontSize = kDefaultMaxDistanceFieldFontSize;
472 }
473 if (options->fMinDistanceFieldFontSize < 0.f) {
474 options->fMinDistanceFieldFontSize = kDefaultMinDistanceFieldFontSize;
475 }
476}
477
Herb Derby26cbe512018-05-24 14:39:01 -0400478bool GrTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
479 const SkSurfaceProps& props,
480 bool contextSupportsDistanceFieldText,
481 const Options& options) {
Brian Salomon52db9402017-11-07 14:58:55 -0500482 if (!viewMatrix.hasPerspective()) {
483 SkScalar maxScale = viewMatrix.getMaxScale();
484 SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
485 // Hinted text looks far better at small resolutions
486 // Scaling up beyond 2x yields undesireable artifacts
Khushal3e7548c2018-05-23 15:45:01 -0700487 if (scaledTextSize < options.fMinDistanceFieldFontSize ||
488 scaledTextSize > options.fMaxDistanceFieldFontSize) {
Brian Salomon52db9402017-11-07 14:58:55 -0500489 return false;
490 }
491
492 bool useDFT = props.isUseDeviceIndependentFonts();
493#if SK_FORCE_DISTANCE_FIELD_TEXT
494 useDFT = true;
495#endif
496
497 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
498 return false;
499 }
500 }
501
Mike Reed8ad91a92018-01-19 19:09:32 -0500502 // mask filters modify alpha, which doesn't translate well to distance
Khushal3e7548c2018-05-23 15:45:01 -0700503 if (skPaint.getMaskFilter() || !contextSupportsDistanceFieldText) {
Brian Salomon52db9402017-11-07 14:58:55 -0500504 return false;
505 }
506
507 // TODO: add some stroking support
508 if (skPaint.getStyle() != SkPaint::kFill_Style) {
509 return false;
510 }
511
512 return true;
513}
514
Herb Derby86240592018-05-24 16:12:31 -0400515void GrTextContext::InitDistanceFieldPaint(GrTextBlob* blob,
Herb Derby26cbe512018-05-24 14:39:01 -0400516 SkPaint* skPaint,
517 const SkMatrix& viewMatrix,
518 const Options& options,
519 SkScalar* textRatio,
520 SkScalerContextFlags* flags) {
Brian Salomon52db9402017-11-07 14:58:55 -0500521 SkScalar textSize = skPaint->getTextSize();
522 SkScalar scaledTextSize = textSize;
523
524 if (viewMatrix.hasPerspective()) {
525 // for perspective, we simply force to the medium size
526 // TODO: compute a size based on approximate screen area
527 scaledTextSize = kMediumDFFontLimit;
528 } else {
529 SkScalar maxScale = viewMatrix.getMaxScale();
530 // if we have non-unity scale, we need to choose our base text size
531 // based on the SkPaint's text size multiplied by the max scale factor
532 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
533 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
534 scaledTextSize *= maxScale;
535 }
536 }
537
538 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
539 // and ceiling. A scale outside of this range would require regenerating the distance fields
540 SkScalar dfMaskScaleFloor;
541 SkScalar dfMaskScaleCeil;
542 if (scaledTextSize <= kSmallDFFontLimit) {
Khushal3e7548c2018-05-23 15:45:01 -0700543 dfMaskScaleFloor = options.fMinDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500544 dfMaskScaleCeil = kSmallDFFontLimit;
545 *textRatio = textSize / kSmallDFFontSize;
546 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
547 } else if (scaledTextSize <= kMediumDFFontLimit) {
548 dfMaskScaleFloor = kSmallDFFontLimit;
549 dfMaskScaleCeil = kMediumDFFontLimit;
550 *textRatio = textSize / kMediumDFFontSize;
551 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
552 } else {
553 dfMaskScaleFloor = kMediumDFFontLimit;
Khushal3e7548c2018-05-23 15:45:01 -0700554 dfMaskScaleCeil = options.fMaxDistanceFieldFontSize;
Brian Salomon52db9402017-11-07 14:58:55 -0500555 *textRatio = textSize / kLargeDFFontSize;
556 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
557 }
558
559 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
560 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
561 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
562 // tolerate before we'd have to move to a large mip size. When we actually test these values
563 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
564 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
565 // level)
566 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
Khushal3e7548c2018-05-23 15:45:01 -0700567 if (blob) {
568 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize,
569 dfMaskScaleCeil / scaledTextSize);
570 }
Brian Salomon52db9402017-11-07 14:58:55 -0500571
572 skPaint->setAntiAlias(true);
573 skPaint->setLCDRenderText(false);
574 skPaint->setAutohinted(false);
575 skPaint->setHinting(SkPaint::kNormal_Hinting);
576 skPaint->setSubpixelText(true);
Jim Van Verthd401da62018-05-03 10:40:30 -0400577
578 skPaint->setMaskFilter(GrSDFMaskFilter::Make());
Khushal3e7548c2018-05-23 15:45:01 -0700579
580 // We apply the fake-gamma by altering the distance in the shader, so we ignore the
581 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
582 *flags = SkScalerContextFlags::kNone;
Brian Salomon52db9402017-11-07 14:58:55 -0500583}
584
Herb Derby86240592018-05-24 16:12:31 -0400585void GrTextContext::drawDFPosText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400586 GrGlyphCache* glyphCache, const SkSurfaceProps& props,
587 const GrTextUtils::Paint& paint,
588 SkScalerContextFlags scalerContextFlags,
589 const SkMatrix& viewMatrix, const char text[],
590 size_t byteLength, const SkScalar pos[],
591 int scalarsPerPosition, const SkPoint& offset) const {
Brian Salomon52db9402017-11-07 14:58:55 -0500592 SkASSERT(byteLength == 0 || text != nullptr);
593 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
594
595 // nothing to draw
596 if (text == nullptr || byteLength == 0) {
597 return;
598 }
599
Khushal3e7548c2018-05-23 15:45:01 -0700600 bool hasWCoord = viewMatrix.hasPerspective() || fOptions.fDistanceFieldVerticesAlwaysHaveW;
Brian Salomonb5086962017-12-13 10:59:33 -0500601
Brian Salomon52db9402017-11-07 14:58:55 -0500602 // Setup distance field paint and text ratio
603 SkScalar textRatio;
604 SkPaint dfPaint(paint);
Khushal3e7548c2018-05-23 15:45:01 -0700605 SkScalerContextFlags flags;
606 InitDistanceFieldPaint(blob, &dfPaint, viewMatrix, fOptions, &textRatio, &flags);
Brian Salomon52db9402017-11-07 14:58:55 -0500607 blob->setHasDistanceField();
608 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
Brian Salomon5c6ac642017-12-19 11:09:32 -0500609 paint.skPaint().isAntiAlias(), hasWCoord);
Brian Salomon52db9402017-11-07 14:58:55 -0500610
Khushalfa8ff092018-06-06 17:46:38 -0700611 FallbackTextHelper fallbackTextHelper(viewMatrix, paint, glyphCache->getGlyphSizeLimit(),
612 textRatio);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500613
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500614 sk_sp<GrTextStrike> currStrike;
Brian Salomon52db9402017-11-07 14:58:55 -0500615
Herb Derby526819d2018-03-09 12:51:12 -0500616 {
Khushal3e7548c2018-05-23 15:45:01 -0700617 auto cache = blob->setupCache(runIndex, props, flags, dfPaint, nullptr);
Herb Derby526819d2018-03-09 12:51:12 -0500618 SkPaint::GlyphCacheProc glyphCacheProc =
Herb Derbyfcac00f2018-05-01 11:57:56 -0400619 SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), true);
Brian Salomon52db9402017-11-07 14:58:55 -0500620
Herb Derby526819d2018-03-09 12:51:12 -0500621 const char* stop = text + byteLength;
Brian Salomon52db9402017-11-07 14:58:55 -0500622
Herb Derby526819d2018-03-09 12:51:12 -0500623 while (text < stop) {
624 const char* lastText = text;
625 // the last 2 parameters are ignored
626 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
Brian Salomon52db9402017-11-07 14:58:55 -0500627
Herb Derby526819d2018-03-09 12:51:12 -0500628 if (glyph.fWidth) {
629 SkPoint glyphPos(offset);
Herb Derby1e7c6582018-05-21 16:10:17 -0400630 glyphPos.fX += pos[0];
631 glyphPos.fY += (2 == scalarsPerPosition ? pos[1] : 0);
Jim Van Verthf4c13162018-01-11 16:40:24 -0500632
Jim Van Verthd401da62018-05-03 10:40:30 -0400633 if (glyph.fMaskFormat == SkMask::kSDF_Format) {
Herb Derby526819d2018-03-09 12:51:12 -0500634 DfAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos.fX,
635 glyphPos.fY, paint.filteredPremulColor(), cache.get(), textRatio);
636 } else {
Jim Van Verthd401da62018-05-03 10:40:30 -0400637 // can't append non-SDF glyph to SDF batch, send to fallback
Herb Derby526819d2018-03-09 12:51:12 -0500638 fallbackTextHelper.appendText(glyph, SkToInt(text - lastText), lastText,
639 glyphPos);
640 }
Brian Salomon52db9402017-11-07 14:58:55 -0500641 }
Herb Derby526819d2018-03-09 12:51:12 -0500642 pos += scalarsPerPosition;
Brian Salomon52db9402017-11-07 14:58:55 -0500643 }
Brian Salomon52db9402017-11-07 14:58:55 -0500644 }
Herb Derbyab6fd7e2018-03-07 18:05:39 +0000645
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500646 fallbackTextHelper.drawText(blob, runIndex, glyphCache, props, paint, scalerContextFlags);
Brian Salomon52db9402017-11-07 14:58:55 -0500647}
648
Jim Van Verthf4c13162018-01-11 16:40:24 -0500649// TODO: merge with BmpAppendGlyph
Herb Derby86240592018-05-24 16:12:31 -0400650void GrTextContext::DfAppendGlyph(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400651 GrGlyphCache* grGlyphCache, sk_sp<GrTextStrike>* strike,
652 const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
653 GrColor color, SkGlyphCache* skGlyphCache,
654 SkScalar textRatio) {
Brian Salomon52db9402017-11-07 14:58:55 -0500655 if (!*strike) {
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500656 *strike = grGlyphCache->getStrike(skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500657 }
658
659 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
660 skGlyph.getSubXFixed(),
661 skGlyph.getSubYFixed(),
662 GrGlyph::kDistance_MaskStyle);
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500663 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, skGlyphCache);
Brian Salomon52db9402017-11-07 14:58:55 -0500664 if (!glyph) {
Jim Van Verthf4c13162018-01-11 16:40:24 -0500665 return;
Brian Salomon52db9402017-11-07 14:58:55 -0500666 }
667
668 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
669 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
670 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
671 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
672
Jim Van Verthf4c13162018-01-11 16:40:24 -0500673 dx *= textRatio;
674 dy *= textRatio;
675 width *= textRatio;
676 height *= textRatio;
677 SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
Brian Salomon52db9402017-11-07 14:58:55 -0500678
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500679 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
Jim Van Verthf4c13162018-01-11 16:40:24 -0500680 textRatio, false);
Brian Salomon52db9402017-11-07 14:58:55 -0500681}
682
joshualitt79dfb2b2015-05-11 08:58:08 -0700683///////////////////////////////////////////////////////////////////////////////////////////////////
684
Herb Derby26cbe512018-05-24 14:39:01 -0400685void GrTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, int count,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500686 const char* text, SkPoint glyphPos) {
Jim Van Verthb515ae72018-05-23 16:44:55 -0400687 SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
Khushalfa8ff092018-06-06 17:46:38 -0700688 if (SkScalarNearlyZero(maxDim)) return;
689
Jim Van Verthb515ae72018-05-23 16:44:55 -0400690 if (!fUseTransformedFallback) {
691 if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
692 fUseTransformedFallback = true;
Jim Van Verthcf838c72018-03-05 14:40:36 -0500693 fMaxTextSize -= 2; // Subtract 2 to account for the bilerp pad around the glyph
Jim Van Verthc401bb92018-02-15 14:05:24 -0500694 }
695 }
696
697 fFallbackTxt.append(count, text);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400698 if (fUseTransformedFallback) {
Jim Van Verth080a9282018-03-02 10:41:43 -0500699 // If there's a glyph in the font that's particularly large, it's possible
700 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
Jim Van Verth8b7284d2018-05-17 12:33:52 -0400701 // that glyph than make the others blurry, so we set a minimum size of half the
Jim Van Verth080a9282018-03-02 10:41:43 -0500702 // maximum text size to avoid this case.
Jim Van Verthb515ae72018-05-23 16:44:55 -0400703 SkScalar glyphTextSize = SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim),
Jim Van Verth080a9282018-03-02 10:41:43 -0500704 0.5f*fMaxTextSize);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400705 fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500706 }
707 *fFallbackPos.append() = glyphPos;
708}
709
Herb Derby86240592018-05-24 16:12:31 -0400710void GrTextContext::FallbackTextHelper::drawText(GrTextBlob* blob, int runIndex,
Herb Derby26cbe512018-05-24 14:39:01 -0400711 GrGlyphCache* glyphCache,
712 const SkSurfaceProps& props,
713 const GrTextUtils::Paint& paint,
714 SkScalerContextFlags scalerContextFlags) {
Jim Van Verthc401bb92018-02-15 14:05:24 -0500715 if (fFallbackTxt.count()) {
716 blob->initOverride(runIndex);
717 blob->setHasBitmap();
Jim Van Verthb515ae72018-05-23 16:44:55 -0400718 blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
Herb Derby526819d2018-03-09 12:51:12 -0500719 SkExclusiveStrikePtr cache;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500720 const SkPaint& skPaint = paint.skPaint();
721 SkPaint::GlyphCacheProc glyphCacheProc =
Herb Derbyfcac00f2018-05-01 11:57:56 -0400722 SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), true);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500723 SkColor textColor = paint.filteredPremulColor();
Khushalfa8ff092018-06-06 17:46:38 -0700724
Jim Van Verthc401bb92018-02-15 14:05:24 -0500725 SkScalar textRatio = SK_Scalar1;
Khushalfa8ff092018-06-06 17:46:38 -0700726 SkPaint fallbackPaint(skPaint);
727 SkMatrix matrix = fViewMatrix;
728 this->initializeForDraw(&fallbackPaint, &textRatio, &matrix);
729 cache = blob->setupCache(runIndex, props, scalerContextFlags, fallbackPaint, &matrix);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500730
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500731 sk_sp<GrTextStrike> currStrike;
Jim Van Verthc401bb92018-02-15 14:05:24 -0500732 const char* text = fFallbackTxt.begin();
733 const char* stop = text + fFallbackTxt.count();
734 SkPoint* glyphPos = fFallbackPos.begin();
735 while (text < stop) {
Herb Derby526819d2018-03-09 12:51:12 -0500736 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
Jim Van Verthb515ae72018-05-23 16:44:55 -0400737 if (!fUseTransformedFallback) {
738 fViewMatrix.mapPoints(glyphPos, 1);
Jim Van Verth76e85162018-03-29 13:46:56 -0400739 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
740 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
741 }
Herb Derby26cbe512018-05-24 14:39:01 -0400742 GrTextContext::BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
Jim Van Verthc401bb92018-02-15 14:05:24 -0500743 glyphPos->fX, glyphPos->fY, textColor,
Jim Van Verthb515ae72018-05-23 16:44:55 -0400744 cache.get(), textRatio, fUseTransformedFallback);
Jim Van Verthc401bb92018-02-15 14:05:24 -0500745 glyphPos++;
746 }
Jim Van Verthc401bb92018-02-15 14:05:24 -0500747 }
748}
749
Khushalfa8ff092018-06-06 17:46:38 -0700750void GrTextContext::FallbackTextHelper::initializeForDraw(SkPaint* paint, SkScalar* textRatio,
751 SkMatrix* matrix) const {
752 if (!fUseTransformedFallback) return;
753
754 paint->setTextSize(fTransformedFallbackTextSize);
755 *textRatio = fTextSize / fTransformedFallbackTextSize;
756 *matrix = SkMatrix::I();
757}
758
Jim Van Verthc401bb92018-02-15 14:05:24 -0500759///////////////////////////////////////////////////////////////////////////////////////////////////
760
Hal Canary6f6961e2017-01-31 13:50:44 -0500761#if GR_TEST_UTILS
joshualitt79dfb2b2015-05-11 08:58:08 -0700762
Brian Salomonf18b1d82017-10-27 11:30:49 -0400763#include "GrRenderTargetContext.h"
764
Herb Derby26cbe512018-05-24 14:39:01 -0400765std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context,
766 GrTextContext* textContext,
767 GrRenderTargetContext* rtc,
768 const SkPaint& skPaint,
769 const SkMatrix& viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -0400770 const char* text,
771 int x,
772 int y) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500773 auto glyphCache = context->contextPriv().getGlyphCache();
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500774
775 static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
776
777 size_t textLen = (int)strlen(text);
778
779 GrTextUtils::Paint utilsPaint(&skPaint, &rtc->colorSpaceInfo());
780
781 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
782 // test the text op with this unit test, that is okay.
Herb Derby41f4f312018-06-06 17:45:53 +0000783
784 auto origin = SkPoint::Make(x, y);
Herb Derby59d997a2018-06-07 12:44:09 -0400785 SkGlyphRunBuilder builder;
786 builder.prepareDrawText(skPaint, text, textLen, origin);
787 sk_sp<GrTextBlob> blob;
Herb Derby41f4f312018-06-06 17:45:53 +0000788
Herb Derby9d85d632018-06-18 16:25:52 -0400789 auto glyphRun = builder.useGlyphRun();
Herb Derby59d997a2018-06-07 12:44:09 -0400790 // Use the text and textLen below, because we don't want to mess with the paint.
Herb Derby9d85d632018-06-18 16:25:52 -0400791 glyphRun->temporaryShuntToCallback(
Herb Derby59d997a2018-06-07 12:44:09 -0400792 [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) {
793 blob = textContext->makeDrawPosTextBlob(
794 context->contextPriv().getTextBlobCache(), glyphCache,
795 *context->contextPriv().caps()->shaderCaps(), utilsPaint,
796 GrTextContext::kTextBlobOpScalerContextFlags, viewMatrix, surfaceProps, text,
797 textLen, pos, 2, origin);
798 });
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500799
800 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, utilsPaint, surfaceProps,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500801 textContext->dfAdjustTable(), rtc->textTarget());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500802}
803
Brian Salomon44acb5b2017-07-18 19:59:24 -0400804GR_DRAW_OP_TEST_DEFINE(GrAtlasTextOp) {
joshualitt79dfb2b2015-05-11 08:58:08 -0700805 static uint32_t gContextID = SK_InvalidGenID;
Herb Derby26cbe512018-05-24 14:39:01 -0400806 static std::unique_ptr<GrTextContext> gTextContext;
robertphillipsfcf78292015-06-19 11:49:52 -0700807 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -0700808
809 if (context->uniqueID() != gContextID) {
810 gContextID = context->uniqueID();
Herb Derby26cbe512018-05-24 14:39:01 -0400811 gTextContext = GrTextContext::Make(GrTextContext::Options());
joshualitt79dfb2b2015-05-11 08:58:08 -0700812 }
813
Brian Osman11052242016-10-27 14:47:55 -0400814 // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500815 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Brian Osman11052242016-10-27 14:47:55 -0400816 SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
brianosman8fe485b2016-07-25 12:31:51 -0700817
joshualitt6c891102015-05-13 08:51:49 -0700818 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
Brian Salomon44acb5b2017-07-18 19:59:24 -0400819
820 // Because we the GrTextUtils::Paint requires an SkPaint for font info, we ignore the GrPaint
821 // param.
joshualitt79dfb2b2015-05-11 08:58:08 -0700822 SkPaint skPaint;
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500823 skPaint.setColor(random->nextU());
joshualitt79dfb2b2015-05-11 08:58:08 -0700824 skPaint.setLCDRenderText(random->nextBool());
825 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
826 skPaint.setSubpixelText(random->nextBool());
827
joshualitt79dfb2b2015-05-11 08:58:08 -0700828 const char* text = "The quick brown fox jumps over the lazy dog.";
joshualitt79dfb2b2015-05-11 08:58:08 -0700829
joshualittb8d86492016-02-24 09:23:03 -0800830 // create some random x/y offsets, including negative offsets
831 static const int kMaxTrans = 1024;
832 int xPos = (random->nextU() % 2) * 2 - 1;
833 int yPos = (random->nextU() % 2) * 2 - 1;
834 int xInt = (random->nextU() % kMaxTrans) * xPos;
835 int yInt = (random->nextU() % kMaxTrans) * yPos;
halcanary9d524f22016-03-29 09:03:52 -0700836
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500837 return gTextContext->createOp_TestingOnly(context, gTextContext.get(), rtc.get(),
838 skPaint, viewMatrix, text, xInt, yInt);
joshualitt79dfb2b2015-05-11 08:58:08 -0700839}
840
841#endif