blob: f0e7dfe1ad0dcbb347fa0ce050fcb4ad170880d2 [file] [log] [blame]
joshualitt259fbf12015-07-21 11:39:34 -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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkColorFilter.h"
9#include "include/gpu/GrContext.h"
10#include "src/core/SkMaskFilterBase.h"
11#include "src/core/SkPaintPriv.h"
12#include "src/gpu/GrBlurUtils.h"
13#include "src/gpu/GrClip.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/gpu/GrStyle.h"
Michael Ludwig663afe52019-06-03 16:46:19 -040015#include "src/gpu/geometry/GrShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/gpu/ops/GrAtlasTextOp.h"
Herb Derbya9047642019-12-06 12:12:11 -050017#include "src/gpu/text/GrAtlasManager.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/gpu/text/GrTextBlob.h"
19#include "src/gpu/text/GrTextTarget.h"
Ben Wagner75d6db72018-09-20 14:39:39 -040020
Mike Klein79aea6a2018-06-11 10:45:26 -040021#include <new>
joshualitt2e2202e2015-12-10 11:22:08 -080022
Herb Derby5bf5b042019-12-12 16:37:03 -050023static SkVector calculate_translation(bool applyVM,
24 const SkMatrix& drawMatrix, SkPoint drawOrigin,
25 const SkMatrix& currentViewMatrix, SkPoint currentOrigin) {
26 SkVector translate;
Herb Derbya9047642019-12-06 12:12:11 -050027 if (applyVM) {
Herb Derby5bf5b042019-12-12 16:37:03 -050028 translate.fX = drawMatrix.getTranslateX() +
29 drawMatrix.getScaleX() * (drawOrigin.x() - currentOrigin.x()) +
30 drawMatrix.getSkewX() * (drawOrigin.y() - currentOrigin.y()) -
31 currentViewMatrix.getTranslateX();
Herb Derbya9047642019-12-06 12:12:11 -050032
Herb Derby5bf5b042019-12-12 16:37:03 -050033 translate.fY = drawMatrix.getTranslateY() +
34 drawMatrix.getSkewY() * (drawOrigin.x() - currentOrigin.x()) +
35 drawMatrix.getScaleY() * (drawOrigin.y() - currentOrigin.y()) -
36 currentViewMatrix.getTranslateY();
Herb Derbya9047642019-12-06 12:12:11 -050037 } else {
Herb Derby5bf5b042019-12-12 16:37:03 -050038 translate = drawOrigin - currentOrigin;
Herb Derbya9047642019-12-06 12:12:11 -050039 }
Herb Derby5bf5b042019-12-12 16:37:03 -050040
41 return translate;
Herb Derbya9047642019-12-06 12:12:11 -050042}
43
44static SkMatrix make_inverse(const SkMatrix& matrix) {
45 SkMatrix inverseMatrix;
46 if (!matrix.invert(&inverseMatrix)) {
47 inverseMatrix = SkMatrix::I();
48 }
49 return inverseMatrix;
50}
51
52// -- GrTextBlob::Key ------------------------------------------------------------------------------
53GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); }
54
55bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const {
56 return 0 == memcmp(this, &other, sizeof(Key));
57}
58
59// -- GrTextBlob::PathGlyph ------------------------------------------------------------------------
60GrTextBlob::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
61 : fPath(path)
62 , fOrigin(origin) {}
63
64// -- GrTextBlob::SubRun ---------------------------------------------------------------------------
Herb Derbye3c7ff42019-12-10 17:49:28 -050065// Hold data to draw the different types of sub run. SubRuns are produced knowing all the
66// glyphs that are included in them.
67class GrTextBlob::SubRun {
68public:
69 // SubRun for masks
70 SubRun(SubRunType type,
71 GrTextBlob* textBlob,
72 const SkStrikeSpec& strikeSpec,
73 GrMaskFormat format,
Herb Derbyc514e7d2019-12-11 17:00:31 -050074 const SkSpan<GrGlyph*>& glyphs, const SkSpan<char>& vertexData,
Herb Derbye3c7ff42019-12-10 17:49:28 -050075 sk_sp<GrTextStrike>&& grStrike);
76
77 // SubRun for paths
78 SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec);
79
80 void appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables);
81
82 // TODO when this object is more internal, drop the privacy
83 void resetBulkUseToken();
84 GrDrawOpAtlas::BulkUseTokenUpdater* bulkUseToken();
85 void setStrike(sk_sp<GrTextStrike> strike);
86 GrTextStrike* strike() const;
Herb Derbye3c7ff42019-12-10 17:49:28 -050087
88 void setAtlasGeneration(uint64_t atlasGeneration);
89 uint64_t atlasGeneration() const;
90
Herb Derbye3c7ff42019-12-10 17:49:28 -050091 void setColor(GrColor color);
92 GrColor color() const;
93
94 GrMaskFormat maskFormat() const;
95
96 const SkRect& vertexBounds() const;
97 void joinGlyphBounds(const SkRect& glyphBounds);
98
Herb Derbye3c7ff42019-12-10 17:49:28 -050099 // This function assumes the translation will be applied before it is called again
Herb Derby5bf5b042019-12-12 16:37:03 -0500100 SkVector computeTranslation(const SkMatrix& drawMatrix, SkPoint drawOrigin);
Herb Derbye3c7ff42019-12-10 17:49:28 -0500101
102 bool drawAsDistanceFields() const;
103 bool drawAsPaths() const;
104 bool needsTransform() const;
105 bool hasW() const;
106
107 // df properties
108 void setUseLCDText(bool useLCDText);
109 bool hasUseLCDText() const;
110 void setAntiAliased(bool antiAliased);
111 bool isAntiAliased() const;
112
113 const SkStrikeSpec& strikeSpec() const;
114
115 SubRun* fNextSubRun{nullptr};
116 const SubRunType fType;
117 GrTextBlob* const fBlob;
118 const GrMaskFormat fMaskFormat;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500119 const SkSpan<GrGlyph*> fGlyphs;
120 const SkSpan<char> fVertexData;
Herb Derbye3c7ff42019-12-10 17:49:28 -0500121 const SkStrikeSpec fStrikeSpec;
122 sk_sp<GrTextStrike> fStrike;
123 struct {
124 bool useLCDText:1;
125 bool antiAliased:1;
126 } fFlags{false, false};
127 GrColor fColor;
128 GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
129 SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
130 uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
Herb Derby5bf5b042019-12-12 16:37:03 -0500131 SkPoint fCurrentOrigin;
Herb Derby1c5be7b2019-12-13 12:03:06 -0500132 SkMatrix fCurrentMatrix;
Herb Derbye3c7ff42019-12-10 17:49:28 -0500133 std::vector<PathGlyph> fPaths;
134}; // SubRun
135
Herb Derbya9047642019-12-06 12:12:11 -0500136GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
Herb Derbyc514e7d2019-12-11 17:00:31 -0500137 GrMaskFormat format,
138 const SkSpan<GrGlyph*>& glyphs, const SkSpan<char>& vertexData,
Herb Derbya9047642019-12-06 12:12:11 -0500139 sk_sp<GrTextStrike>&& grStrike)
140 : fType{type}
141 , fBlob{textBlob}
142 , fMaskFormat{format}
Herb Derbyc514e7d2019-12-11 17:00:31 -0500143 , fGlyphs{glyphs}
144 , fVertexData{vertexData}
Herb Derbya9047642019-12-06 12:12:11 -0500145 , fStrikeSpec{strikeSpec}
146 , fStrike{grStrike}
147 , fColor{textBlob->fColor}
Herb Derby5bf5b042019-12-12 16:37:03 -0500148 , fCurrentOrigin{textBlob->fInitialOrigin}
Herb Derby1c5be7b2019-12-13 12:03:06 -0500149 , fCurrentMatrix{textBlob->fInitialMatrix} {
Herb Derbya9047642019-12-06 12:12:11 -0500150 SkASSERT(type != kTransformedPath);
Herb Derbycb718892019-12-07 00:07:42 -0500151 textBlob->insertSubRun(this);
Herb Derbya9047642019-12-06 12:12:11 -0500152}
153
154GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
155 : fType{kTransformedPath}
156 , fBlob{textBlob}
157 , fMaskFormat{kA8_GrMaskFormat}
Herb Derbyc514e7d2019-12-11 17:00:31 -0500158 , fGlyphs{SkSpan<GrGlyph*>{}}
159 , fVertexData{SkSpan<char>{}}
Herb Derbya9047642019-12-06 12:12:11 -0500160 , fStrikeSpec{strikeSpec}
161 , fStrike{nullptr}
162 , fColor{textBlob->fColor}
Herb Derbycb718892019-12-07 00:07:42 -0500163 , fPaths{} {
164 textBlob->insertSubRun(this);
165}
Herb Derbya9047642019-12-06 12:12:11 -0500166
167void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
168 GrTextStrike* grStrike = fStrike.get();
169 SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
Herb Derbyc514e7d2019-12-11 17:00:31 -0500170 GrGlyph** glyphCursor = fGlyphs.data();
171 char* vertexCursor = fVertexData.data();
Herb Derbya9047642019-12-06 12:12:11 -0500172 bool hasW = this->hasW();
173 GrColor color = this->color();
174 // glyphs drawn in perspective must always have a w coord.
Herb Derby1c5be7b2019-12-13 12:03:06 -0500175 SkASSERT(hasW || !fBlob->fInitialMatrix.hasPerspective());
Herb Derbya9047642019-12-06 12:12:11 -0500176 size_t vertexStride = GetVertexStride(fMaskFormat, hasW);
177 // We always write the third position component used by SDFs. If it is unused it gets
178 // overwritten. Similarly, we always write the color and the blob will later overwrite it
179 // with texture coords if it is unused.
180 size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
181 for (auto [variant, pos] : drawables) {
182 SkGlyph* skGlyph = variant;
183 GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
184 // Only floor the device coordinates.
185 SkRect dstRect;
186 if (!this->needsTransform()) {
187 pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
188 dstRect = grGlyph->destRect(pos);
189 } else {
190 dstRect = grGlyph->destRect(pos, strikeToSource);
191 }
192
193 this->joinGlyphBounds(dstRect);
194
Herb Derbya9047642019-12-06 12:12:11 -0500195 // V0
Herb Derbyc514e7d2019-12-11 17:00:31 -0500196 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fTop, 1.f};
197 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = color;
198 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500199
200 // V1
Herb Derbyc514e7d2019-12-11 17:00:31 -0500201 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fBottom, 1.f};
202 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = color;
203 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500204
205 // V2
Herb Derbyc514e7d2019-12-11 17:00:31 -0500206 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fTop, 1.f};
207 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = color;
208 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500209
210 // V3
Herb Derbyc514e7d2019-12-11 17:00:31 -0500211 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fBottom, 1.f};
212 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = color;
213 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500214
Herb Derbyc514e7d2019-12-11 17:00:31 -0500215 *glyphCursor++ = grGlyph;
Herb Derbya9047642019-12-06 12:12:11 -0500216 }
Herb Derbya9047642019-12-06 12:12:11 -0500217}
218
219void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
220
221GrDrawOpAtlas::BulkUseTokenUpdater* GrTextBlob::SubRun::bulkUseToken() { return &fBulkUseToken; }
222void GrTextBlob::SubRun::setStrike(sk_sp<GrTextStrike> strike) { fStrike = std::move(strike); }
223GrTextStrike* GrTextBlob::SubRun::strike() const { return fStrike.get(); }
Herb Derbya9047642019-12-06 12:12:11 -0500224void GrTextBlob::SubRun::setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
225uint64_t GrTextBlob::SubRun::atlasGeneration() const { return fAtlasGeneration; }
Herb Derbya9047642019-12-06 12:12:11 -0500226void GrTextBlob::SubRun::setColor(GrColor color) { fColor = color; }
227GrColor GrTextBlob::SubRun::color() const { return fColor; }
228GrMaskFormat GrTextBlob::SubRun::maskFormat() const { return fMaskFormat; }
229const SkRect& GrTextBlob::SubRun::vertexBounds() const { return fVertexBounds; }
230void GrTextBlob::SubRun::joinGlyphBounds(const SkRect& glyphBounds) {
231 fVertexBounds.joinNonEmptyArg(glyphBounds);
232}
233
Herb Derby5bf5b042019-12-12 16:37:03 -0500234SkVector GrTextBlob::SubRun::computeTranslation(
235 const SkMatrix& drawMatrix, SkPoint drawOrigin){
Herb Derbya9047642019-12-06 12:12:11 -0500236 // Don't use the matrix to translate on distance field for fallback subruns.
Herb Derby5bf5b042019-12-12 16:37:03 -0500237
238 SkVector translate = calculate_translation(
239 !this->drawAsDistanceFields() && !this->needsTransform(),
240 drawMatrix, drawOrigin, fCurrentMatrix, fCurrentOrigin);
241
242 // Update SubRun indicating that the vertices now correspond to the origin and matrix used in
243 // the draw.
Herb Derby1c5be7b2019-12-13 12:03:06 -0500244 fCurrentMatrix = drawMatrix;
Herb Derby5bf5b042019-12-12 16:37:03 -0500245 fCurrentOrigin = drawOrigin;
246 return translate;
Herb Derbya9047642019-12-06 12:12:11 -0500247}
248
249bool GrTextBlob::SubRun::drawAsDistanceFields() const { return fType == kTransformedSDFT; }
250
251bool GrTextBlob::SubRun::drawAsPaths() const { return fType == kTransformedPath; }
252
253bool GrTextBlob::SubRun::needsTransform() const {
254 return fType == kTransformedPath
255 || fType == kTransformedMask
256 || fType == kTransformedSDFT;
257}
258
259bool GrTextBlob::SubRun::hasW() const {
260 return fBlob->hasW(fType);
261}
262
263void GrTextBlob::SubRun::setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
264bool GrTextBlob::SubRun::hasUseLCDText() const { return fFlags.useLCDText; }
265void GrTextBlob::SubRun::setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
266bool GrTextBlob::SubRun::isAntiAliased() const { return fFlags.antiAliased; }
267const SkStrikeSpec& GrTextBlob::SubRun::strikeSpec() const { return fStrikeSpec; }
268
269// -- GrTextBlob -----------------------------------------------------------------------------------
270void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
271void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
272void* GrTextBlob::operator new(size_t, void* p) { return p; }
273
274GrTextBlob::~GrTextBlob() = default;
275
Herb Derby659e4092019-12-06 15:38:10 -0500276sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
Herb Derbyeba195f2019-12-03 16:44:47 -0500277 GrStrikeCache* strikeCache,
Herb Derby1c5be7b2019-12-13 12:03:06 -0500278 const SkMatrix& drawMatrix,
Herb Derbya00da612019-03-04 17:10:01 -0500279 GrColor color,
Herb Derbyeba195f2019-12-03 16:44:47 -0500280 bool forceWForDistanceFields) {
Herb Derbycd498e12019-12-06 14:17:31 -0500281
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500282 static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
Herb Derbycb718892019-12-07 00:07:42 -0500283 static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex));
284 size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph;
Herb Derby1c5be7b2019-12-13 12:03:06 -0500285 if (drawMatrix.hasPerspective() || forceWForDistanceFields) {
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500286 static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex));
Herb Derbycb718892019-12-07 00:07:42 -0500287 static_assert(alignof(ARGB3DVertex) <= alignof(SDFT3DVertex));
288 quadSize = sizeof(SDFT3DVertex) * kVerticesPerGlyph;
Herb Derbye74c7762019-12-04 14:15:41 -0500289 }
290
Herb Derbycb718892019-12-07 00:07:42 -0500291 // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments.
292 static_assert(alignof(SDFT3DVertex) >= alignof(Mask2DVertex));
Herb Derbyc514e7d2019-12-11 17:00:31 -0500293 // Assume there is no padding needed between glyph pointers and vertices.
Herb Derbycb718892019-12-07 00:07:42 -0500294 static_assert(alignof(GrGlyph*) >= alignof(SDFT3DVertex));
Herb Derbyc514e7d2019-12-11 17:00:31 -0500295
296 // In the arena, the layout is GrGlyph*... | SDFT3DVertex... | SubRun, so there is no padding
297 // between GrGlyph* and SDFT3DVertex, but padding is needed between the Mask2DVertex array
298 // and the SubRun.
299 size_t vertexToSubRunPadding = alignof(SDFT3DVertex) - alignof(SubRun);
300 size_t arenaSize =
301 sizeof(GrGlyph*) * glyphRunList.totalGlyphCount()
302 + quadSize * glyphRunList.totalGlyphCount()
303 + glyphRunList.runCount() * (sizeof(SubRun) + vertexToSubRunPadding);
304
305 size_t allocationSize = sizeof(GrTextBlob) + arenaSize;
Ben Wagner75d6db72018-09-20 14:39:39 -0400306
Herb Derbycb718892019-12-07 00:07:42 -0500307 void* allocation = ::operator new (allocationSize);
Herb Derbyb12175f2018-05-23 16:38:09 -0400308
Herb Derbyaebc5f82019-12-10 14:07:10 -0500309 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(glyphRunList.paint());
Herb Derby00ae9592019-12-03 15:55:56 -0500310 sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
Herb Derby1c5be7b2019-12-13 12:03:06 -0500311 arenaSize, strikeCache, drawMatrix, glyphRunList.origin(),
Herb Derbyaebc5f82019-12-10 14:07:10 -0500312 color, initialLuminance, forceWForDistanceFields}};
joshualitt92303772016-02-10 11:55:52 -0800313
Herb Derbyf7d5d742018-11-16 13:24:32 -0500314 return blob;
joshualitt92303772016-02-10 11:55:52 -0800315}
316
Herb Derbya9047642019-12-06 12:12:11 -0500317void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::BlurRec& blurRec,
318 const SkPaint& paint) {
319 fKey = key;
320 if (key.fHasBlur) {
321 fBlurRec = blurRec;
322 }
323 if (key.fStyle != SkPaint::kFill_Style) {
324 fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
325 fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
326 fStrokeInfo.fJoin = paint.getStrokeJoin();
327 }
328}
329const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
330uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
331
Herb Derbycb718892019-12-07 00:07:42 -0500332bool GrTextBlob::hasDistanceField() const {
333 return SkToBool(fTextType & kHasDistanceField_TextType);
334}
Herb Derbya9047642019-12-06 12:12:11 -0500335bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
Herb Derby1c5be7b2019-12-13 12:03:06 -0500336bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
Herb Derbya9047642019-12-06 12:12:11 -0500337
338void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
339void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; }
340void GrTextBlob::setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
341 // we init fMaxMinScale and fMinMaxScale in the constructor
342 fMaxMinScale = SkMaxScalar(scaledMin, fMaxMinScale);
343 fMinMaxScale = SkMinScalar(scaledMax, fMinMaxScale);
344}
345
346size_t GrTextBlob::GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
347 switch (maskFormat) {
348 case kA8_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500349 return hasWCoord ? sizeof(SDFT3DVertex) : sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500350 case kARGB_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500351 return hasWCoord ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500352 default:
353 SkASSERT(!hasWCoord);
Herb Derbycd498e12019-12-06 14:17:31 -0500354 return sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500355 }
356}
357
Herb Derby0edb2142018-10-16 17:04:11 -0400358bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
359 const SkMaskFilterBase::BlurRec& blurRec,
Herb Derby5bf5b042019-12-12 16:37:03 -0500360 const SkMatrix& drawMatrix, SkPoint drawOrigin) {
joshualittfd5f6c12015-12-10 07:44:50 -0800361 // If we have LCD text then our canonical color will be set to transparent, in this case we have
362 // to regenerate the blob on any color change
363 // We use the grPaint to get any color filter effects
364 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
Herb Derbyaebc5f82019-12-10 14:07:10 -0500365 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800366 return true;
367 }
368
Herb Derby1c5be7b2019-12-13 12:03:06 -0500369 if (fInitialMatrix.hasPerspective() != drawMatrix.hasPerspective()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800370 return true;
371 }
372
Brian Salomon5c6ac642017-12-19 11:09:32 -0500373 /** This could be relaxed for blobs with only distance field glyphs. */
Mike Reed2c383152019-12-18 16:47:47 -0500374 if (fInitialMatrix.hasPerspective() && !SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800375 return true;
376 }
377
378 // We only cache one masked version
379 if (fKey.fHasBlur &&
Mike Reed1be1f8d2018-03-14 13:01:17 -0400380 (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800381 return true;
382 }
383
384 // Similarly, we only cache one version for each style
385 if (fKey.fStyle != SkPaint::kFill_Style &&
Herb Derbybc6f9c92018-08-08 13:58:45 -0400386 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
387 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
388 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
joshualittfd5f6c12015-12-10 07:44:50 -0800389 return true;
390 }
391
392 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
393 // for mixed blobs if this becomes an issue.
394 if (this->hasBitmap() && this->hasDistanceField()) {
Herb Derbyeba195f2019-12-03 16:44:47 -0500395 // Identical view matrices and we can reuse in all cases
Mike Reed2c383152019-12-18 16:47:47 -0500396 return !(SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix) &&
397 drawOrigin == fInitialOrigin);
joshualittfd5f6c12015-12-10 07:44:50 -0800398 }
399
400 if (this->hasBitmap()) {
Herb Derby1c5be7b2019-12-13 12:03:06 -0500401 if (fInitialMatrix.getScaleX() != drawMatrix.getScaleX() ||
402 fInitialMatrix.getScaleY() != drawMatrix.getScaleY() ||
403 fInitialMatrix.getSkewX() != drawMatrix.getSkewX() ||
404 fInitialMatrix.getSkewY() != drawMatrix.getSkewY()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800405 return true;
406 }
407
Herb Derby9830fc42019-09-12 10:58:26 -0400408 // TODO(herb): this is not needed for full pixel glyph choice, but is needed to adjust
409 // the quads properly. Devise a system that regenerates the quads from original data
410 // using the transform to allow this to be used in general.
411
412 // We can update the positions in the text blob without regenerating the whole
413 // blob, but only for integer translations.
414 // This cool bit of math will determine the necessary translation to apply to the
415 // already generated vertex coordinates to move them to the correct position.
416 // Figure out the translation in view space given a translation in source space.
Herb Derby1c5be7b2019-12-13 12:03:06 -0500417 SkScalar transX = drawMatrix.getTranslateX() +
Herb Derby5bf5b042019-12-12 16:37:03 -0500418 drawMatrix.getScaleX() * (drawOrigin.x() - fInitialOrigin.x()) +
419 drawMatrix.getSkewX() * (drawOrigin.y() - fInitialOrigin.y()) -
Herb Derby1c5be7b2019-12-13 12:03:06 -0500420 fInitialMatrix.getTranslateX();
421 SkScalar transY = drawMatrix.getTranslateY() +
Herb Derby5bf5b042019-12-12 16:37:03 -0500422 drawMatrix.getSkewY() * (drawOrigin.x() - fInitialOrigin.x()) +
423 drawMatrix.getScaleY() * (drawOrigin.y() - fInitialOrigin.y()) -
Herb Derby1c5be7b2019-12-13 12:03:06 -0500424 fInitialMatrix.getTranslateY();
Herb Derby9830fc42019-09-12 10:58:26 -0400425 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
426 return true;
joshualittfd5f6c12015-12-10 07:44:50 -0800427 }
joshualittfd5f6c12015-12-10 07:44:50 -0800428 } else if (this->hasDistanceField()) {
429 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
430 // distance field being generated, so we have to regenerate in those cases
Herb Derby1c5be7b2019-12-13 12:03:06 -0500431 SkScalar newMaxScale = drawMatrix.getMaxScale();
432 SkScalar oldMaxScale = fInitialMatrix.getMaxScale();
joshualittfd5f6c12015-12-10 07:44:50 -0800433 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
434 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
435 return true;
436 }
joshualittfd5f6c12015-12-10 07:44:50 -0800437 }
438
joshualittfd5f6c12015-12-10 07:44:50 -0800439 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
440 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
441 // the blob anyways at flush time, so no need to regenerate explicitly
442 return false;
443}
444
Herb Derbyc1b482c2018-08-09 15:02:27 -0400445void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
446 const GrDistanceFieldAdjustTable* distanceAdjustTable,
Brian Osmancf860852018-10-31 14:04:39 -0400447 const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
Herb Derby5bf5b042019-12-12 16:37:03 -0500448 const SkMatrix& drawMatrix, SkPoint drawOrigin) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500449
Herb Derbycb718892019-12-07 00:07:42 -0500450 for (SubRun* subRun = fFirstSubRun; subRun != nullptr; subRun = subRun->fNextSubRun) {
451 if (subRun->drawAsPaths()) {
Herb Derbydac1ed52018-09-12 17:04:21 -0400452 SkPaint runPaint{paint};
Herb Derbycb718892019-12-07 00:07:42 -0500453 runPaint.setAntiAlias(subRun->isAntiAliased());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500454 // If there are shaders, blurs or styles, the path must be scaled into source
455 // space independently of the CTM. This allows the CTM to be correct for the
456 // different effects.
457 GrStyle style(runPaint);
Herb Derby9f491482018-08-08 11:53:00 -0400458
Herb Derby1b8dcd12019-11-15 15:21:15 -0500459 bool scalePath = runPaint.getShader()
460 || style.applies()
461 || runPaint.getMaskFilter();
Robert Phillips137ca522018-08-15 10:14:33 -0400462
Herb Derby1b8dcd12019-11-15 15:21:15 -0500463 // The origin for the blob may have changed, so figure out the delta.
Herb Derby5bf5b042019-12-12 16:37:03 -0500464 SkVector originShift = drawOrigin - fInitialOrigin;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500465
Herb Derbycb718892019-12-07 00:07:42 -0500466 for (const auto& pathGlyph : subRun->fPaths) {
Herb Derby1c5be7b2019-12-13 12:03:06 -0500467 SkMatrix ctm{drawMatrix};
Herb Derbycb718892019-12-07 00:07:42 -0500468 SkMatrix pathMatrix = SkMatrix::MakeScale(
469 subRun->fStrikeSpec.strikeToSourceRatio());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500470 // Shift the original glyph location in source space to the position of the new
471 // blob.
472 pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
473 originShift.y() + pathGlyph.fOrigin.y());
Herb Derbydac1ed52018-09-12 17:04:21 -0400474
475 // TmpPath must be in the same scope as GrShape shape below.
Robert Phillips137ca522018-08-15 10:14:33 -0400476 SkTLazy<SkPath> tmpPath;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500477 const SkPath* path = &pathGlyph.fPath;
Herb Derby2984d262019-11-20 14:40:39 -0500478 if (!scalePath) {
479 // Scale can be applied to CTM -- no effects.
Herb Derby2984d262019-11-20 14:40:39 -0500480 ctm.preConcat(pathMatrix);
Robert Phillipsd20d2612018-08-28 10:09:01 -0400481 } else {
Herb Derby2984d262019-11-20 14:40:39 -0500482 // Scale the outline into source space.
Herb Derbydac1ed52018-09-12 17:04:21 -0400483
Herb Derby2984d262019-11-20 14:40:39 -0500484 // Transform the path form the normalized outline to source space. This
485 // way the CTM will remain the same so it can be used by the effects.
486 SkPath* sourceOutline = tmpPath.init();
487 path->transform(pathMatrix, sourceOutline);
488 sourceOutline->setIsVolatile(true);
489 path = sourceOutline;
Robert Phillips137ca522018-08-15 10:14:33 -0400490 }
491
Robert Phillips46a13382018-08-23 13:53:01 -0400492 // TODO: we are losing the mutability of the path here
493 GrShape shape(*path, paint);
Herb Derbydac1ed52018-09-12 17:04:21 -0400494 target->drawShape(clip, runPaint, ctm, shape);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500495 }
Herb Derby1b8dcd12019-11-15 15:21:15 -0500496 } else {
Herb Derbyc514e7d2019-12-11 17:00:31 -0500497 int glyphCount = subRun->fGlyphs.size();
Jim Van Verth54d9c882018-02-08 16:14:48 -0500498 if (0 == glyphCount) {
499 continue;
500 }
501
502 bool skipClip = false;
503 bool submitOp = true;
504 SkIRect clipRect = SkIRect::MakeEmpty();
505 SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
506 SkRRect clipRRect;
507 GrAA aa;
Jim Van Verthb515ae72018-05-23 16:44:55 -0400508 // We can clip geometrically if we're not using SDFs or transformed glyphs,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500509 // and we have an axis-aligned rectangular non-AA clip
Herb Derbycb718892019-12-07 00:07:42 -0500510 if (!subRun->drawAsDistanceFields() && !subRun->needsTransform() &&
Jim Van Verthcf838c72018-03-05 14:40:36 -0500511 clip.isRRect(rtBounds, &clipRRect, &aa) &&
Jim Van Verth54d9c882018-02-08 16:14:48 -0500512 clipRRect.isRect() && GrAA::kNo == aa) {
513 skipClip = true;
514 // We only need to do clipping work if the subrun isn't contained by the clip
515 SkRect subRunBounds;
Herb Derby5bf5b042019-12-12 16:37:03 -0500516 this->computeSubRunBounds(
517 &subRunBounds, *subRun, drawMatrix, drawOrigin, false);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500518 if (!clipRRect.getBounds().contains(subRunBounds)) {
519 // If the subrun is completely outside, don't add an op for it
520 if (!clipRRect.getBounds().intersects(subRunBounds)) {
521 submitOp = false;
522 }
523 else {
524 clipRRect.getBounds().round(&clipRect);
525 }
526 }
527 }
528
529 if (submitOp) {
Herb Derby5bf5b042019-12-12 16:37:03 -0500530 auto op = this->makeOp(*subRun, glyphCount, drawMatrix, drawOrigin,
Herb Derbybc6f9c92018-08-08 13:58:45 -0400531 clipRect, paint, filteredColor, props, distanceAdjustTable,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500532 target);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500533 if (op) {
534 if (skipClip) {
535 target->addDrawOp(GrNoClip(), std::move(op));
536 }
537 else {
538 target->addDrawOp(clip, std::move(op));
539 }
540 }
541 }
542 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000543 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000544}
545
Herb Derby1c5be7b2019-12-13 12:03:06 -0500546void GrTextBlob::computeSubRunBounds(SkRect* outBounds, const SubRun& subRun,
Herb Derby5bf5b042019-12-12 16:37:03 -0500547 const SkMatrix& drawMatrix, SkPoint drawOrigin,
Herb Derbya9047642019-12-06 12:12:11 -0500548 bool needsGlyphTransform) {
549 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
550 // into device space.
551 // We handle vertex bounds differently for distance field text and bitmap text because
552 // the vertex bounds of bitmap text are in device space. If we are flushing multiple runs
553 // from one blob then we are going to pay the price here of mapping the rect for each run.
554 *outBounds = subRun.vertexBounds();
555 if (needsGlyphTransform) {
556 // Distance field text is positioned with the (X,Y) as part of the glyph position,
557 // and currently the view matrix is applied on the GPU
Herb Derby5bf5b042019-12-12 16:37:03 -0500558 outBounds->offset(drawOrigin - fInitialOrigin);
Herb Derby1c5be7b2019-12-13 12:03:06 -0500559 drawMatrix.mapRect(outBounds);
Herb Derbya9047642019-12-06 12:12:11 -0500560 } else {
561 // Bitmap text is fully positioned on the CPU, and offset by an (X,Y) translate in
562 // device space.
Herb Derby1c5be7b2019-12-13 12:03:06 -0500563 SkMatrix boundsMatrix = fInitialMatrixInverse;
Herb Derbya9047642019-12-06 12:12:11 -0500564
565 boundsMatrix.postTranslate(-fInitialOrigin.x(), -fInitialOrigin.y());
566
Herb Derby5bf5b042019-12-12 16:37:03 -0500567 boundsMatrix.postTranslate(drawOrigin.x(), drawOrigin.y());
Herb Derbya9047642019-12-06 12:12:11 -0500568
Herb Derby1c5be7b2019-12-13 12:03:06 -0500569 boundsMatrix.postConcat(drawMatrix);
Herb Derbya9047642019-12-06 12:12:11 -0500570 boundsMatrix.mapRect(outBounds);
571
572 // Due to floating point numerical inaccuracies, we have to round out here
573 outBounds->roundOut(outBounds);
574 }
joshualitt323c2eb2016-01-20 06:48:47 -0800575}
joshualitt2e2202e2015-12-10 11:22:08 -0800576
Herb Derbya9047642019-12-06 12:12:11 -0500577const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
578size_t GrTextBlob::size() const { return fSize; }
579
580std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
Herb Derby1c5be7b2019-12-13 12:03:06 -0500581 int glyphCount, const SkMatrix& drawMatrix,
Herb Derby5bf5b042019-12-12 16:37:03 -0500582 SkPoint drawOrigin, const SkPaint& paint, const SkPMColor4f& filteredColor,
Herb Derbya9047642019-12-06 12:12:11 -0500583 const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
584 GrTextTarget* target) {
Herb Derbycb718892019-12-07 00:07:42 -0500585 SubRun* info = fFirstSubRun;
Herb Derbya9047642019-12-06 12:12:11 -0500586 SkIRect emptyRect = SkIRect::MakeEmpty();
Herb Derby5bf5b042019-12-12 16:37:03 -0500587 return this->makeOp(*info, glyphCount, drawMatrix, drawOrigin, emptyRect,
Herb Derbya9047642019-12-06 12:12:11 -0500588 paint, filteredColor, props, distanceAdjustTable, target);
589}
590
591bool GrTextBlob::hasW(GrTextBlob::SubRunType type) const {
592 if (type == kTransformedSDFT) {
593 return this->hasPerspective() || fForceWForDistanceFields;
594 } else if (type == kTransformedMask || type == kTransformedPath) {
595 return this->hasPerspective();
Herb Derbye9f691d2019-12-04 12:11:13 -0500596 }
Herb Derbya9047642019-12-06 12:12:11 -0500597
598 // The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
599 // used.
600 return false;
601}
602
603GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
604 const SkZip<SkGlyphVariant, SkPoint>& drawables,
605 const SkStrikeSpec& strikeSpec,
606 GrMaskFormat format) {
Herb Derbyc514e7d2019-12-11 17:00:31 -0500607 SkSpan<GrGlyph*> glyphs{fAlloc.makeArrayDefault<GrGlyph*>(drawables.size()), drawables.size()};
Herb Derbya9047642019-12-06 12:12:11 -0500608 bool hasW = this->hasW(type);
Herb Derbyc514e7d2019-12-11 17:00:31 -0500609 size_t vertexDataSize = drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
610 SkSpan<char> vertexData{fAlloc.makeArrayDefault<char>(vertexDataSize), vertexDataSize};
Herb Derbya9047642019-12-06 12:12:11 -0500611
612 sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
613
Herb Derbycb718892019-12-07 00:07:42 -0500614 SubRun* subRun = fAlloc.make<SubRun>(
Herb Derbyc514e7d2019-12-11 17:00:31 -0500615 type, this, strikeSpec, format, glyphs, vertexData, std::move(grStrike));
Herb Derbya9047642019-12-06 12:12:11 -0500616
Herb Derbycb718892019-12-07 00:07:42 -0500617 subRun->appendGlyphs(drawables);
Herb Derbya9047642019-12-06 12:12:11 -0500618
Herb Derbycb718892019-12-07 00:07:42 -0500619 return subRun;
Herb Derbya9047642019-12-06 12:12:11 -0500620}
621
622void GrTextBlob::addSingleMaskFormat(
623 SubRunType type,
624 const SkZip<SkGlyphVariant, SkPoint>& drawables,
625 const SkStrikeSpec& strikeSpec,
626 GrMaskFormat format) {
627 this->makeSubRun(type, drawables, strikeSpec, format);
628}
629
630void GrTextBlob::addMultiMaskFormat(
631 SubRunType type,
632 const SkZip<SkGlyphVariant, SkPoint>& drawables,
633 const SkStrikeSpec& strikeSpec) {
634 this->setHasBitmap();
635 if (drawables.empty()) { return; }
636
637 auto glyphSpan = drawables.get<0>();
638 SkGlyph* glyph = glyphSpan[0];
639 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
640 size_t startIndex = 0;
641 for (size_t i = 1; i < drawables.size(); i++) {
642 glyph = glyphSpan[i];
643 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
644 if (format != nextFormat) {
645 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
646 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
647 format = nextFormat;
648 startIndex = i;
649 }
650 }
651 auto sameFormat = drawables.last(drawables.size() - startIndex);
652 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
653}
654
655void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
656 const SkStrikeSpec& strikeSpec,
657 const SkFont& runFont,
658 SkScalar minScale,
659 SkScalar maxScale) {
660 this->setHasDistanceField();
661 this->setMinAndMaxScale(minScale, maxScale);
662
663 SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
664 subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
665 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbye9f691d2019-12-04 12:11:13 -0500666}
667
Herb Derbycb718892019-12-07 00:07:42 -0500668GrTextBlob::GrTextBlob(size_t allocSize,
Herb Derbyeba195f2019-12-03 16:44:47 -0500669 GrStrikeCache* strikeCache,
Herb Derby1c5be7b2019-12-13 12:03:06 -0500670 const SkMatrix& drawMatrix,
Herb Derbyeba195f2019-12-03 16:44:47 -0500671 SkPoint origin,
672 GrColor color,
Herb Derbyaebc5f82019-12-10 14:07:10 -0500673 SkColor initialLuminance,
Herb Derby00ae9592019-12-03 15:55:56 -0500674 bool forceWForDistanceFields)
Herb Derbycb718892019-12-07 00:07:42 -0500675 : fSize{allocSize}
Herb Derby00ae9592019-12-03 15:55:56 -0500676 , fStrikeCache{strikeCache}
Herb Derby1c5be7b2019-12-13 12:03:06 -0500677 , fInitialMatrix{drawMatrix}
678 , fInitialMatrixInverse{make_inverse(drawMatrix)}
Herb Derbyeba195f2019-12-03 16:44:47 -0500679 , fInitialOrigin{origin}
Herb Derby00ae9592019-12-03 15:55:56 -0500680 , fForceWForDistanceFields{forceWForDistanceFields}
Herb Derbycb718892019-12-07 00:07:42 -0500681 , fColor{color}
Herb Derbyaebc5f82019-12-10 14:07:10 -0500682 , fInitialLuminance{initialLuminance}
Herb Derbycb718892019-12-07 00:07:42 -0500683 , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
Herb Derby00ae9592019-12-03 15:55:56 -0500684
Herb Derbycb718892019-12-07 00:07:42 -0500685void GrTextBlob::insertSubRun(SubRun* subRun) {
686 if (fFirstSubRun == nullptr) {
687 fFirstSubRun = subRun;
688 fLastSubRun = subRun;
689 } else {
690 fLastSubRun->fNextSubRun = subRun;
691 fLastSubRun = subRun;
692 }
693}
694
695std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
Herb Derbya9047642019-12-06 12:12:11 -0500696 SubRun& info, int glyphCount,
Herb Derby5bf5b042019-12-12 16:37:03 -0500697 const SkMatrix& drawMatrix, SkPoint drawOrigin, const SkIRect& clipRect,
Herb Derbya9047642019-12-06 12:12:11 -0500698 const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
699 const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
700 GrMaskFormat format = info.maskFormat();
701
702 GrPaint grPaint;
Herb Derby1c5be7b2019-12-13 12:03:06 -0500703 target->makeGrPaint(info.maskFormat(), paint, drawMatrix, &grPaint);
Herb Derbya9047642019-12-06 12:12:11 -0500704 std::unique_ptr<GrAtlasTextOp> op;
705 if (info.drawAsDistanceFields()) {
706 // TODO: Can we be even smarter based on the dest transfer function?
707 op = GrAtlasTextOp::MakeDistanceField(
708 target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable,
709 target->colorInfo().isLinearlyBlended(), SkPaintPriv::ComputeLuminanceColor(paint),
710 props, info.isAntiAliased(), info.hasUseLCDText());
Herb Derbyeba195f2019-12-03 16:44:47 -0500711 } else {
Herb Derbya9047642019-12-06 12:12:11 -0500712 op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
713 info.needsTransform());
714 }
715 GrAtlasTextOp::Geometry& geometry = op->geometry();
Herb Derby1c5be7b2019-12-13 12:03:06 -0500716 geometry.fDrawMatrix = drawMatrix;
Herb Derbya9047642019-12-06 12:12:11 -0500717 geometry.fClipRect = clipRect;
718 geometry.fBlob = SkRef(this);
719 geometry.fSubRunPtr = &info;
720 geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
Herb Derby5bf5b042019-12-12 16:37:03 -0500721 geometry.fDrawOrigin = drawOrigin;
Herb Derbya9047642019-12-06 12:12:11 -0500722 op->init();
723 return op;
724}
Herb Derbyeba195f2019-12-03 16:44:47 -0500725
Herb Derbya9047642019-12-06 12:12:11 -0500726void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
727 const SkStrikeSpec& strikeSpec) {
728 this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
729}
Herb Derbyeba195f2019-12-03 16:44:47 -0500730
Herb Derbya9047642019-12-06 12:12:11 -0500731void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
732 const SkFont& runFont,
733 const SkStrikeSpec& strikeSpec) {
734 this->setHasBitmap();
Herb Derbycb718892019-12-07 00:07:42 -0500735 SubRun* subRun = fAlloc.make<SubRun>(this, strikeSpec);
736 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbya9047642019-12-06 12:12:11 -0500737 for (auto [variant, pos] : drawables) {
Herb Derbycb718892019-12-07 00:07:42 -0500738 subRun->fPaths.emplace_back(*variant.path(), pos);
Herb Derbyeba195f2019-12-03 16:44:47 -0500739 }
740}
741
Herb Derbya9047642019-12-06 12:12:11 -0500742void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
743 const SkStrikeSpec& strikeSpec,
744 const SkFont& runFont,
745 SkScalar minScale,
746 SkScalar maxScale) {
747 this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
joshualitt8e0ef292016-02-19 14:13:03 -0800748}
Herb Derbya9047642019-12-06 12:12:11 -0500749
750void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
751 const SkStrikeSpec& strikeSpec) {
752 this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
753}
754
755// -- GrTextBlob::VertexRegenerator ----------------------------------------------------------------
Herb Derby5bf5b042019-12-12 16:37:03 -0500756static void regen_positions(char* vertex, size_t vertexStride, SkVector translation) {
Herb Derbya9047642019-12-06 12:12:11 -0500757 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
758 for (int i = 0; i < 4; ++i) {
Herb Derby5bf5b042019-12-12 16:37:03 -0500759 *point += translation;
Herb Derbya9047642019-12-06 12:12:11 -0500760 point = SkTAddOffset<SkPoint>(point, vertexStride);
761 }
762}
763
764static void regen_colors(char* vertex, size_t vertexStride, GrColor color) {
765 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
766 // vertices, hence vertexStride - sizeof(SkIPoint16)
767 size_t colorOffset = vertexStride - sizeof(SkIPoint16) - sizeof(GrColor);
768 GrColor* vcolor = reinterpret_cast<GrColor*>(vertex + colorOffset);
769 for (int i = 0; i < 4; ++i) {
770 *vcolor = color;
771 vcolor = SkTAddOffset<GrColor>(vcolor, vertexStride);
772 }
773}
774
775static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
776 bool useDistanceFields) {
777 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
778 // vertices, hence vertexStride - sizeof(SkIPoint16)
779 size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
780
781 uint16_t u0, v0, u1, v1;
782 SkASSERT(glyph);
783 int width = glyph->fBounds.width();
784 int height = glyph->fBounds.height();
785
786 if (useDistanceFields) {
787 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
788 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
789 u1 = u0 + width - 2 * SK_DistanceFieldInset;
790 v1 = v0 + height - 2 * SK_DistanceFieldInset;
791 } else {
792 u0 = glyph->fAtlasLocation.fX;
793 v0 = glyph->fAtlasLocation.fY;
794 u1 = u0 + width;
795 v1 = v0 + height;
796 }
797 // We pack the 2bit page index in the low bit of the u and v texture coords
798 uint32_t pageIndex = glyph->pageIndex();
799 SkASSERT(pageIndex < 4);
800 uint16_t uBit = (pageIndex >> 1) & 0x1;
801 uint16_t vBit = pageIndex & 0x1;
802 u0 <<= 1;
803 u0 |= uBit;
804 v0 <<= 1;
805 v0 |= vBit;
806 u1 <<= 1;
807 u1 |= uBit;
808 v1 <<= 1;
809 v1 |= vBit;
810
811 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
812 textureCoords[0] = u0;
813 textureCoords[1] = v0;
814 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
815 textureCoords[0] = u0;
816 textureCoords[1] = v1;
817 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
818 textureCoords[0] = u1;
819 textureCoords[1] = v0;
820 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
821 textureCoords[0] = u1;
822 textureCoords[1] = v1;
823
824#ifdef DISPLAY_PAGE_INDEX
825 // Enable this to visualize the page from which each glyph is being drawn.
826 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
827 GrColor hackColor;
828 switch (pageIndex) {
829 case 0:
830 hackColor = GrColorPackRGBA(0, 255, 0, 255);
831 break;
832 case 1:
833 hackColor = GrColorPackRGBA(255, 0, 0, 255);;
834 break;
835 case 2:
836 hackColor = GrColorPackRGBA(255, 0, 255, 255);
837 break;
838 case 3:
839 hackColor = GrColorPackRGBA(0, 255, 255, 255);
840 break;
841 default:
842 hackColor = GrColorPackRGBA(0, 0, 0, 255);
843 break;
844 }
845 regen_colors(vertex, vertexStride, hackColor);
846#endif
847}
848
849GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
Herb Derbya9047642019-12-06 12:12:11 -0500850 GrTextBlob::SubRun* subRun,
Herb Derby5bf5b042019-12-12 16:37:03 -0500851 const SkMatrix& drawMatrix,
852 SkPoint drawOrigin,
Herb Derbya9047642019-12-06 12:12:11 -0500853 GrColor color,
854 GrDeferredUploadTarget* uploadTarget,
855 GrStrikeCache* grStrikeCache,
856 GrAtlasManager* fullAtlasManager)
857 : fResourceProvider(resourceProvider)
Herb Derby1c5be7b2019-12-13 12:03:06 -0500858 , fDrawMatrix(drawMatrix)
Herb Derbya9047642019-12-06 12:12:11 -0500859 , fUploadTarget(uploadTarget)
860 , fGrStrikeCache(grStrikeCache)
861 , fFullAtlasManager(fullAtlasManager)
862 , fSubRun(subRun)
863 , fColor(color) {
864 // Compute translation if any
Herb Derby5bf5b042019-12-12 16:37:03 -0500865 fDrawTranslation = fSubRun->computeTranslation(fDrawMatrix, drawOrigin);
Herb Derbya9047642019-12-06 12:12:11 -0500866
867 // Because the GrStrikeCache may evict the strike a blob depends on using for
868 // generating its texture coords, we have to track whether or not the strike has
869 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
870 // otherwise we have to get the new strike, and use that to get the correct glyphs.
871 // Because we do not have the packed ids, and thus can't look up our glyphs in the
872 // new strike, we instead keep our ref to the old strike and use the packed ids from
873 // it. These ids will still be valid as long as we hold the ref. When we are done
874 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
Herb Derby73630212019-12-13 16:29:14 -0500875 fActions.regenTextureCoordinates = fSubRun->strike()->isAbandoned();
876 fActions.regenStrike = fSubRun->strike()->isAbandoned();
877 fActions.regenColor = kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color;
878 fActions.regenPositions = fDrawTranslation.x() != 0.f || fDrawTranslation.y() != 0.f;
Herb Derbya9047642019-12-06 12:12:11 -0500879}
880
Herb Derby73630212019-12-13 16:29:14 -0500881bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result) {
882 SkASSERT(!fActions.regenStrike || fActions.regenTextureCoordinates);
883 if (fActions.regenTextureCoordinates) {
Herb Derbya9047642019-12-06 12:12:11 -0500884 fSubRun->resetBulkUseToken();
885
886 const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
887
888 if (!fMetricsAndImages.isValid()
889 || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
890 fMetricsAndImages.init(strikeSpec);
891 }
892
Herb Derby73630212019-12-13 16:29:14 -0500893 if (fActions.regenStrike) {
Herb Derbya9047642019-12-06 12:12:11 -0500894 // Take the glyphs from the old strike, and translate them a new strike.
895 sk_sp<GrTextStrike> newStrike = strikeSpec.findOrCreateGrStrike(fGrStrikeCache);
896
897 // Start this batch at the start of the subRun plus any glyphs that were previously
898 // processed.
Herb Derbyc514e7d2019-12-11 17:00:31 -0500899 SkSpan<GrGlyph*> glyphs = fSubRun->fGlyphs.last(fSubRun->fGlyphs.size() - fCurrGlyph);
Herb Derbya9047642019-12-06 12:12:11 -0500900
901 // Convert old glyphs to newStrike.
902 for (auto& glyph : glyphs) {
903 SkPackedGlyphID id = glyph->fPackedID;
904 glyph = newStrike->getGlyph(id, fMetricsAndImages.get());
905 SkASSERT(id == glyph->fPackedID);
906 }
907
908 fSubRun->setStrike(newStrike);
909 }
910 }
911
Herb Derby7cf4a2e2019-12-23 14:51:55 -0500912 GrTextStrike* grStrike = fSubRun->strike();
Herb Derbya9047642019-12-06 12:12:11 -0500913 bool hasW = fSubRun->hasW();
914 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
Herb Derbyc514e7d2019-12-11 17:00:31 -0500915 char* currVertex = fSubRun->fVertexData.data() + fCurrGlyph * kVerticesPerGlyph * vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500916 result->fFirstVertex = currVertex;
917
Herb Derbyc514e7d2019-12-11 17:00:31 -0500918 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->fGlyphs.size(); glyphIdx++) {
Herb Derbya9047642019-12-06 12:12:11 -0500919 GrGlyph* glyph = nullptr;
Herb Derby73630212019-12-13 16:29:14 -0500920 if (fActions.regenTextureCoordinates) {
Herb Derbyc514e7d2019-12-11 17:00:31 -0500921 glyph = fSubRun->fGlyphs[glyphIdx];
Herb Derbya9047642019-12-06 12:12:11 -0500922 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
923
924 if (!fFullAtlasManager->hasGlyph(glyph)) {
Herb Derby7cf4a2e2019-12-23 14:51:55 -0500925 GrDrawOpAtlas::ErrorCode code = grStrike->addGlyphToAtlas(
926 fResourceProvider, fUploadTarget, fGrStrikeCache, fFullAtlasManager, glyph,
927 fMetricsAndImages.get(), fSubRun->maskFormat(), fSubRun->needsTransform());
Herb Derbya9047642019-12-06 12:12:11 -0500928 if (GrDrawOpAtlas::ErrorCode::kError == code) {
929 // Something horrible has happened - drop the op
930 return false;
931 }
932 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
933 fBrokenRun = glyphIdx > 0;
934 result->fFinished = false;
935 return true;
936 }
937 }
938 auto tokenTracker = fUploadTarget->tokenTracker();
939 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
940 tokenTracker->nextDrawToken());
941 }
942
Herb Derby73630212019-12-13 16:29:14 -0500943 if (fActions.regenPositions) {
Herb Derby5bf5b042019-12-12 16:37:03 -0500944 regen_positions(currVertex, vertexStride, fDrawTranslation);
Herb Derbya9047642019-12-06 12:12:11 -0500945 }
Herb Derby73630212019-12-13 16:29:14 -0500946 if (fActions.regenColor) {
Herb Derbya9047642019-12-06 12:12:11 -0500947 regen_colors(currVertex, vertexStride, fColor);
948 }
Herb Derby73630212019-12-13 16:29:14 -0500949 if (fActions.regenTextureCoordinates) {
Herb Derbya9047642019-12-06 12:12:11 -0500950 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
951 }
952
953 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
954 ++result->fGlyphsRegenerated;
955 ++fCurrGlyph;
956 }
957
958 // We may have changed the color so update it here
959 fSubRun->setColor(fColor);
Herb Derby73630212019-12-13 16:29:14 -0500960 if (fActions.regenTextureCoordinates) {
Herb Derbya9047642019-12-06 12:12:11 -0500961 fSubRun->setAtlasGeneration(fBrokenRun
962 ? GrDrawOpAtlas::kInvalidAtlasGeneration
963 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
964 } else {
965 // For the non-texCoords case we need to ensure that we update the associated use tokens
966 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
967 fUploadTarget->tokenTracker()->nextDrawToken(),
968 fSubRun->maskFormat());
969 }
970 return true;
971}
972
973bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
974 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
975 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
976 // this each time.
Herb Derby73630212019-12-13 16:29:14 -0500977 fActions.regenTextureCoordinates |= fSubRun->atlasGeneration() != currentAtlasGen;
Herb Derbya9047642019-12-06 12:12:11 -0500978
Herb Derby73630212019-12-13 16:29:14 -0500979 if (fActions.regenStrike
980 |fActions.regenTextureCoordinates
981 |fActions.regenColor
982 |fActions.regenPositions) {
983 return this->doRegen(result);
Herb Derbya9047642019-12-06 12:12:11 -0500984 } else {
985 bool hasW = fSubRun->hasW();
986 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
987 result->fFinished = true;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500988 result->fGlyphsRegenerated = fSubRun->fGlyphs.size() - fCurrGlyph;
989 result->fFirstVertex = fSubRun->fVertexData.data() +
990 fCurrGlyph * kVerticesPerGlyph * vertexStride;
991 fCurrGlyph = fSubRun->fGlyphs.size();
Herb Derbya9047642019-12-06 12:12:11 -0500992
993 // set use tokens for all of the glyphs in our subrun. This is only valid if we
994 // have a valid atlas generation
995 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
996 fUploadTarget->tokenTracker()->nextDrawToken(),
997 fSubRun->maskFormat());
998 return true;
999 }
1000 SK_ABORT("Should not get here");
1001}
1002
1003
1004
1005