blob: 11d1f12222d1eac494b14caf1ce41a205023b258 [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 Derbya9047642019-12-06 12:12:11 -050023static void calculate_translation(bool applyVM,
24 const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
25 const SkMatrix& currentViewMatrix, SkScalar currentX,
26 SkScalar currentY, SkScalar* transX, SkScalar* transY) {
27 if (applyVM) {
28 *transX = newViewMatrix.getTranslateX() +
29 newViewMatrix.getScaleX() * (newX - currentX) +
30 newViewMatrix.getSkewX() * (newY - currentY) -
31 currentViewMatrix.getTranslateX();
32
33 *transY = newViewMatrix.getTranslateY() +
34 newViewMatrix.getSkewY() * (newX - currentX) +
35 newViewMatrix.getScaleY() * (newY - currentY) -
36 currentViewMatrix.getTranslateY();
37 } else {
38 *transX = newX - currentX;
39 *transY = newY - currentY;
40 }
41}
42
43static SkMatrix make_inverse(const SkMatrix& matrix) {
44 SkMatrix inverseMatrix;
45 if (!matrix.invert(&inverseMatrix)) {
46 inverseMatrix = SkMatrix::I();
47 }
48 return inverseMatrix;
49}
50
51// -- GrTextBlob::Key ------------------------------------------------------------------------------
52GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); }
53
54bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const {
55 return 0 == memcmp(this, &other, sizeof(Key));
56}
57
58// -- GrTextBlob::PathGlyph ------------------------------------------------------------------------
59GrTextBlob::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
60 : fPath(path)
61 , fOrigin(origin) {}
62
63// -- GrTextBlob::SubRun ---------------------------------------------------------------------------
Herb Derbye3c7ff42019-12-10 17:49:28 -050064// Hold data to draw the different types of sub run. SubRuns are produced knowing all the
65// glyphs that are included in them.
66class GrTextBlob::SubRun {
67public:
68 // SubRun for masks
69 SubRun(SubRunType type,
70 GrTextBlob* textBlob,
71 const SkStrikeSpec& strikeSpec,
72 GrMaskFormat format,
73 const SubRunBufferSpec& bufferSpec,
74 sk_sp<GrTextStrike>&& grStrike);
75
76 // SubRun for paths
77 SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec);
78
79 void appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables);
80
81 // TODO when this object is more internal, drop the privacy
82 void resetBulkUseToken();
83 GrDrawOpAtlas::BulkUseTokenUpdater* bulkUseToken();
84 void setStrike(sk_sp<GrTextStrike> strike);
85 GrTextStrike* strike() const;
86 sk_sp<GrTextStrike> refStrike() const;
87
88 void setAtlasGeneration(uint64_t atlasGeneration);
89 uint64_t atlasGeneration() const;
90
91 size_t vertexStartIndex() const;
92 uint32_t glyphCount() const;
93 uint32_t glyphStartIndex() const;
94
95 void setColor(GrColor color);
96 GrColor color() const;
97
98 GrMaskFormat maskFormat() const;
99
100 const SkRect& vertexBounds() const;
101 void joinGlyphBounds(const SkRect& glyphBounds);
102
103 void init(const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
104
105 // This function assumes the translation will be applied before it is called again
106 void computeTranslation(const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
107 SkScalar* transX, SkScalar* transY);
108
109 bool drawAsDistanceFields() const;
110 bool drawAsPaths() const;
111 bool needsTransform() const;
112 bool hasW() const;
113
114 // df properties
115 void setUseLCDText(bool useLCDText);
116 bool hasUseLCDText() const;
117 void setAntiAliased(bool antiAliased);
118 bool isAntiAliased() const;
119
120 const SkStrikeSpec& strikeSpec() const;
121
122 SubRun* fNextSubRun{nullptr};
123 const SubRunType fType;
124 GrTextBlob* const fBlob;
125 const GrMaskFormat fMaskFormat;
126 const uint32_t fGlyphStartIndex;
127 const uint32_t fGlyphEndIndex;
128 const size_t fVertexStartIndex;
129 const size_t fVertexEndIndex;
130 const SkStrikeSpec fStrikeSpec;
131 sk_sp<GrTextStrike> fStrike;
132 struct {
133 bool useLCDText:1;
134 bool antiAliased:1;
135 } fFlags{false, false};
136 GrColor fColor;
137 GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
138 SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
139 uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
140 SkScalar fX;
141 SkScalar fY;
142 SkMatrix fCurrentViewMatrix;
143 std::vector<PathGlyph> fPaths;
144}; // SubRun
145
Herb Derbya9047642019-12-06 12:12:11 -0500146GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
147 GrMaskFormat format, const GrTextBlob::SubRunBufferSpec& bufferSpec,
148 sk_sp<GrTextStrike>&& grStrike)
149 : fType{type}
150 , fBlob{textBlob}
151 , fMaskFormat{format}
152 , fGlyphStartIndex{std::get<0>(bufferSpec)}
153 , fGlyphEndIndex{std::get<1>(bufferSpec)}
154 , fVertexStartIndex{std::get<2>(bufferSpec)}
155 , fVertexEndIndex{std::get<3>(bufferSpec)}
156 , fStrikeSpec{strikeSpec}
157 , fStrike{grStrike}
158 , fColor{textBlob->fColor}
159 , fX{textBlob->fInitialOrigin.x()}
160 , fY{textBlob->fInitialOrigin.y()}
161 , fCurrentViewMatrix{textBlob->fInitialViewMatrix} {
162 SkASSERT(type != kTransformedPath);
Herb Derbycb718892019-12-07 00:07:42 -0500163 textBlob->insertSubRun(this);
Herb Derbya9047642019-12-06 12:12:11 -0500164}
165
166GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
167 : fType{kTransformedPath}
168 , fBlob{textBlob}
169 , fMaskFormat{kA8_GrMaskFormat}
170 , fGlyphStartIndex{0}
171 , fGlyphEndIndex{0}
172 , fVertexStartIndex{0}
173 , fVertexEndIndex{0}
174 , fStrikeSpec{strikeSpec}
175 , fStrike{nullptr}
176 , fColor{textBlob->fColor}
Herb Derbycb718892019-12-07 00:07:42 -0500177 , fPaths{} {
178 textBlob->insertSubRun(this);
179}
Herb Derbya9047642019-12-06 12:12:11 -0500180
181void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
182 GrTextStrike* grStrike = fStrike.get();
183 SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
184 uint32_t glyphCursor = fGlyphStartIndex;
185 size_t vertexCursor = fVertexStartIndex;
186 bool hasW = this->hasW();
187 GrColor color = this->color();
188 // glyphs drawn in perspective must always have a w coord.
189 SkASSERT(hasW || !fBlob->fInitialViewMatrix.hasPerspective());
190 size_t vertexStride = GetVertexStride(fMaskFormat, hasW);
191 // We always write the third position component used by SDFs. If it is unused it gets
192 // overwritten. Similarly, we always write the color and the blob will later overwrite it
193 // with texture coords if it is unused.
194 size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
195 for (auto [variant, pos] : drawables) {
196 SkGlyph* skGlyph = variant;
197 GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
198 // Only floor the device coordinates.
199 SkRect dstRect;
200 if (!this->needsTransform()) {
201 pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
202 dstRect = grGlyph->destRect(pos);
203 } else {
204 dstRect = grGlyph->destRect(pos, strikeToSource);
205 }
206
207 this->joinGlyphBounds(dstRect);
208
209 intptr_t vertex = reinterpret_cast<intptr_t>(fBlob->fVertices + vertexCursor);
210
211 // V0
212 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
213 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
214 vertex += vertexStride;
215
216 // V1
217 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
218 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
219 vertex += vertexStride;
220
221 // V2
222 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
223 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
224 vertex += vertexStride;
225
226 // V3
227 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
228 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
229
230 vertexCursor += vertexStride * kVerticesPerGlyph;
231 fBlob->fGlyphs[glyphCursor++] = grGlyph;
232 }
233 SkASSERT(glyphCursor == fGlyphEndIndex);
234 SkASSERT(vertexCursor == fVertexEndIndex);
235}
236
237void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
238
239GrDrawOpAtlas::BulkUseTokenUpdater* GrTextBlob::SubRun::bulkUseToken() { return &fBulkUseToken; }
240void GrTextBlob::SubRun::setStrike(sk_sp<GrTextStrike> strike) { fStrike = std::move(strike); }
241GrTextStrike* GrTextBlob::SubRun::strike() const { return fStrike.get(); }
242sk_sp<GrTextStrike> GrTextBlob::SubRun::refStrike() const { return fStrike; }
243void GrTextBlob::SubRun::setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
244uint64_t GrTextBlob::SubRun::atlasGeneration() const { return fAtlasGeneration; }
245size_t GrTextBlob::SubRun::vertexStartIndex() const { return fVertexStartIndex; }
246uint32_t GrTextBlob::SubRun::glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
247uint32_t GrTextBlob::SubRun::glyphStartIndex() const { return fGlyphStartIndex; }
248void GrTextBlob::SubRun::setColor(GrColor color) { fColor = color; }
249GrColor GrTextBlob::SubRun::color() const { return fColor; }
250GrMaskFormat GrTextBlob::SubRun::maskFormat() const { return fMaskFormat; }
251const SkRect& GrTextBlob::SubRun::vertexBounds() const { return fVertexBounds; }
252void GrTextBlob::SubRun::joinGlyphBounds(const SkRect& glyphBounds) {
253 fVertexBounds.joinNonEmptyArg(glyphBounds);
254}
255
256void GrTextBlob::SubRun::init(const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
257 fCurrentViewMatrix = viewMatrix;
258 fX = x;
259 fY = y;
260}
261
262void GrTextBlob::SubRun::computeTranslation(const SkMatrix& viewMatrix,
263 SkScalar x, SkScalar y, SkScalar* transX,
264 SkScalar* transY) {
265 // Don't use the matrix to translate on distance field for fallback subruns.
266 calculate_translation(!this->drawAsDistanceFields() && !this->needsTransform(), viewMatrix,
267 x, y, fCurrentViewMatrix, fX, fY, transX, transY);
268 fCurrentViewMatrix = viewMatrix;
269 fX = x;
270 fY = y;
271}
272
273bool GrTextBlob::SubRun::drawAsDistanceFields() const { return fType == kTransformedSDFT; }
274
275bool GrTextBlob::SubRun::drawAsPaths() const { return fType == kTransformedPath; }
276
277bool GrTextBlob::SubRun::needsTransform() const {
278 return fType == kTransformedPath
279 || fType == kTransformedMask
280 || fType == kTransformedSDFT;
281}
282
283bool GrTextBlob::SubRun::hasW() const {
284 return fBlob->hasW(fType);
285}
286
287void GrTextBlob::SubRun::setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
288bool GrTextBlob::SubRun::hasUseLCDText() const { return fFlags.useLCDText; }
289void GrTextBlob::SubRun::setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
290bool GrTextBlob::SubRun::isAntiAliased() const { return fFlags.antiAliased; }
291const SkStrikeSpec& GrTextBlob::SubRun::strikeSpec() const { return fStrikeSpec; }
292
293// -- GrTextBlob -----------------------------------------------------------------------------------
294void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
295void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
296void* GrTextBlob::operator new(size_t, void* p) { return p; }
297
298GrTextBlob::~GrTextBlob() = default;
299
Herb Derby659e4092019-12-06 15:38:10 -0500300sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
Herb Derbyeba195f2019-12-03 16:44:47 -0500301 GrStrikeCache* strikeCache,
Herb Derbye9f691d2019-12-04 12:11:13 -0500302 const SkMatrix& viewMatrix,
Herb Derbya00da612019-03-04 17:10:01 -0500303 GrColor color,
Herb Derbyeba195f2019-12-03 16:44:47 -0500304 bool forceWForDistanceFields) {
Herb Derbycd498e12019-12-06 14:17:31 -0500305
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500306 static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
Herb Derbycb718892019-12-07 00:07:42 -0500307 static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex));
308 size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph;
Herb Derbye74c7762019-12-04 14:15:41 -0500309 if (viewMatrix.hasPerspective() || forceWForDistanceFields) {
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500310 static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex));
Herb Derbycb718892019-12-07 00:07:42 -0500311 static_assert(alignof(ARGB3DVertex) <= alignof(SDFT3DVertex));
312 quadSize = sizeof(SDFT3DVertex) * kVerticesPerGlyph;
Herb Derbye74c7762019-12-04 14:15:41 -0500313 }
314
Herb Derbycb718892019-12-07 00:07:42 -0500315 // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments.
316 static_assert(alignof(SDFT3DVertex) >= alignof(Mask2DVertex));
Herb Derbycd498e12019-12-06 14:17:31 -0500317
Herb Derbycb718892019-12-07 00:07:42 -0500318 size_t subRunsOffset = sizeof(GrTextBlob);
319 size_t subRunsSize = glyphRunList.runCount() * sizeof(SubRun);
320 static_assert(alignof(SubRun) >= alignof(GrGlyph*));
321 size_t glyphsOffset = subRunsOffset + subRunsSize;
322 static_assert(alignof(GrGlyph*) >= alignof(SDFT3DVertex));
323 size_t vertexOffset = glyphsOffset + sizeof(GrGlyph*) * glyphRunList.totalGlyphCount();
324 size_t allocationSize = vertexOffset + quadSize * glyphRunList.totalGlyphCount();
Ben Wagner75d6db72018-09-20 14:39:39 -0400325
Herb Derbycb718892019-12-07 00:07:42 -0500326 void* allocation = ::operator new (allocationSize);
Herb Derbyb12175f2018-05-23 16:38:09 -0400327
Herb Derbyaebc5f82019-12-10 14:07:10 -0500328 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(glyphRunList.paint());
Herb Derby00ae9592019-12-03 15:55:56 -0500329 sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
Herb Derbycb718892019-12-07 00:07:42 -0500330 subRunsSize, strikeCache, viewMatrix, glyphRunList.origin(),
Herb Derbyaebc5f82019-12-10 14:07:10 -0500331 color, initialLuminance, forceWForDistanceFields}};
joshualitt92303772016-02-10 11:55:52 -0800332
333 // setup offsets for vertices / glyphs
Herb Derbycb718892019-12-07 00:07:42 -0500334 blob->fVertices = SkTAddOffset<char>(blob.get(), vertexOffset);
335 blob->fGlyphs = SkTAddOffset<GrGlyph*>(blob.get(), glyphsOffset);
joshualitt92303772016-02-10 11:55:52 -0800336
Herb Derbyf7d5d742018-11-16 13:24:32 -0500337 return blob;
joshualitt92303772016-02-10 11:55:52 -0800338}
339
Herb Derbya9047642019-12-06 12:12:11 -0500340void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::BlurRec& blurRec,
341 const SkPaint& paint) {
342 fKey = key;
343 if (key.fHasBlur) {
344 fBlurRec = blurRec;
345 }
346 if (key.fStyle != SkPaint::kFill_Style) {
347 fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
348 fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
349 fStrokeInfo.fJoin = paint.getStrokeJoin();
350 }
351}
352const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
353uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
354
Herb Derbycb718892019-12-07 00:07:42 -0500355bool GrTextBlob::hasDistanceField() const {
356 return SkToBool(fTextType & kHasDistanceField_TextType);
357}
Herb Derbya9047642019-12-06 12:12:11 -0500358bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
359bool GrTextBlob::hasPerspective() const { return fInitialViewMatrix.hasPerspective(); }
360
361void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
362void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; }
363void GrTextBlob::setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
364 // we init fMaxMinScale and fMinMaxScale in the constructor
365 fMaxMinScale = SkMaxScalar(scaledMin, fMaxMinScale);
366 fMinMaxScale = SkMinScalar(scaledMax, fMinMaxScale);
367}
368
369size_t GrTextBlob::GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
370 switch (maskFormat) {
371 case kA8_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500372 return hasWCoord ? sizeof(SDFT3DVertex) : sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500373 case kARGB_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500374 return hasWCoord ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500375 default:
376 SkASSERT(!hasWCoord);
Herb Derbycd498e12019-12-06 14:17:31 -0500377 return sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500378 }
379}
380
Herb Derby0edb2142018-10-16 17:04:11 -0400381bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
382 const SkMaskFilterBase::BlurRec& blurRec,
383 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualittfd5f6c12015-12-10 07:44:50 -0800384 // If we have LCD text then our canonical color will be set to transparent, in this case we have
385 // to regenerate the blob on any color change
386 // We use the grPaint to get any color filter effects
387 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
Herb Derbyaebc5f82019-12-10 14:07:10 -0500388 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800389 return true;
390 }
391
joshualitt8e0ef292016-02-19 14:13:03 -0800392 if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800393 return true;
394 }
395
Brian Salomon5c6ac642017-12-19 11:09:32 -0500396 /** This could be relaxed for blobs with only distance field glyphs. */
joshualitt8e0ef292016-02-19 14:13:03 -0800397 if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800398 return true;
399 }
400
401 // We only cache one masked version
402 if (fKey.fHasBlur &&
Mike Reed1be1f8d2018-03-14 13:01:17 -0400403 (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800404 return true;
405 }
406
407 // Similarly, we only cache one version for each style
408 if (fKey.fStyle != SkPaint::kFill_Style &&
Herb Derbybc6f9c92018-08-08 13:58:45 -0400409 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
410 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
411 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
joshualittfd5f6c12015-12-10 07:44:50 -0800412 return true;
413 }
414
415 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
416 // for mixed blobs if this becomes an issue.
417 if (this->hasBitmap() && this->hasDistanceField()) {
Herb Derbyeba195f2019-12-03 16:44:47 -0500418 // Identical view matrices and we can reuse in all cases
419 return !(fInitialViewMatrix.cheapEqualTo(viewMatrix) && SkPoint{x, y} == fInitialOrigin);
joshualittfd5f6c12015-12-10 07:44:50 -0800420 }
421
422 if (this->hasBitmap()) {
joshualitt8e0ef292016-02-19 14:13:03 -0800423 if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
424 fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
425 fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
426 fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800427 return true;
428 }
429
Herb Derby9830fc42019-09-12 10:58:26 -0400430 // TODO(herb): this is not needed for full pixel glyph choice, but is needed to adjust
431 // the quads properly. Devise a system that regenerates the quads from original data
432 // using the transform to allow this to be used in general.
433
434 // We can update the positions in the text blob without regenerating the whole
435 // blob, but only for integer translations.
436 // This cool bit of math will determine the necessary translation to apply to the
437 // already generated vertex coordinates to move them to the correct position.
438 // Figure out the translation in view space given a translation in source space.
439 SkScalar transX = viewMatrix.getTranslateX() +
Herb Derbyeba195f2019-12-03 16:44:47 -0500440 viewMatrix.getScaleX() * (x - fInitialOrigin.x()) +
441 viewMatrix.getSkewX() * (y - fInitialOrigin.y()) -
Herb Derby9830fc42019-09-12 10:58:26 -0400442 fInitialViewMatrix.getTranslateX();
443 SkScalar transY = viewMatrix.getTranslateY() +
Herb Derbyeba195f2019-12-03 16:44:47 -0500444 viewMatrix.getSkewY() * (x - fInitialOrigin.x()) +
445 viewMatrix.getScaleY() * (y - fInitialOrigin.y()) -
Herb Derby9830fc42019-09-12 10:58:26 -0400446 fInitialViewMatrix.getTranslateY();
447 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
448 return true;
joshualittfd5f6c12015-12-10 07:44:50 -0800449 }
joshualittfd5f6c12015-12-10 07:44:50 -0800450 } else if (this->hasDistanceField()) {
451 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
452 // distance field being generated, so we have to regenerate in those cases
453 SkScalar newMaxScale = viewMatrix.getMaxScale();
joshualitt8e0ef292016-02-19 14:13:03 -0800454 SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale();
joshualittfd5f6c12015-12-10 07:44:50 -0800455 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
456 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
457 return true;
458 }
joshualittfd5f6c12015-12-10 07:44:50 -0800459 }
460
joshualittfd5f6c12015-12-10 07:44:50 -0800461 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
462 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
463 // the blob anyways at flush time, so no need to regenerate explicitly
464 return false;
465}
466
Herb Derbyc1b482c2018-08-09 15:02:27 -0400467void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
468 const GrDistanceFieldAdjustTable* distanceAdjustTable,
Brian Osmancf860852018-10-31 14:04:39 -0400469 const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
Robert Phillipse4643cc2018-08-14 13:01:29 -0400470 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500471
Herb Derbycb718892019-12-07 00:07:42 -0500472 for (SubRun* subRun = fFirstSubRun; subRun != nullptr; subRun = subRun->fNextSubRun) {
473 if (subRun->drawAsPaths()) {
Herb Derbydac1ed52018-09-12 17:04:21 -0400474 SkPaint runPaint{paint};
Herb Derbycb718892019-12-07 00:07:42 -0500475 runPaint.setAntiAlias(subRun->isAntiAliased());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500476 // If there are shaders, blurs or styles, the path must be scaled into source
477 // space independently of the CTM. This allows the CTM to be correct for the
478 // different effects.
479 GrStyle style(runPaint);
Herb Derby9f491482018-08-08 11:53:00 -0400480
Herb Derby1b8dcd12019-11-15 15:21:15 -0500481 bool scalePath = runPaint.getShader()
482 || style.applies()
483 || runPaint.getMaskFilter();
Robert Phillips137ca522018-08-15 10:14:33 -0400484
Herb Derby1b8dcd12019-11-15 15:21:15 -0500485 // The origin for the blob may have changed, so figure out the delta.
Herb Derbyeba195f2019-12-03 16:44:47 -0500486 SkVector originShift = SkPoint{x, y} - fInitialOrigin;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500487
Herb Derbycb718892019-12-07 00:07:42 -0500488 for (const auto& pathGlyph : subRun->fPaths) {
Herb Derby1b8dcd12019-11-15 15:21:15 -0500489 SkMatrix ctm{viewMatrix};
Herb Derbycb718892019-12-07 00:07:42 -0500490 SkMatrix pathMatrix = SkMatrix::MakeScale(
491 subRun->fStrikeSpec.strikeToSourceRatio());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500492 // Shift the original glyph location in source space to the position of the new
493 // blob.
494 pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
495 originShift.y() + pathGlyph.fOrigin.y());
Herb Derbydac1ed52018-09-12 17:04:21 -0400496
497 // TmpPath must be in the same scope as GrShape shape below.
Robert Phillips137ca522018-08-15 10:14:33 -0400498 SkTLazy<SkPath> tmpPath;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500499 const SkPath* path = &pathGlyph.fPath;
Herb Derby2984d262019-11-20 14:40:39 -0500500 if (!scalePath) {
501 // Scale can be applied to CTM -- no effects.
Herb Derby2984d262019-11-20 14:40:39 -0500502 ctm.preConcat(pathMatrix);
Robert Phillipsd20d2612018-08-28 10:09:01 -0400503 } else {
Herb Derby2984d262019-11-20 14:40:39 -0500504 // Scale the outline into source space.
Herb Derbydac1ed52018-09-12 17:04:21 -0400505
Herb Derby2984d262019-11-20 14:40:39 -0500506 // Transform the path form the normalized outline to source space. This
507 // way the CTM will remain the same so it can be used by the effects.
508 SkPath* sourceOutline = tmpPath.init();
509 path->transform(pathMatrix, sourceOutline);
510 sourceOutline->setIsVolatile(true);
511 path = sourceOutline;
Robert Phillips137ca522018-08-15 10:14:33 -0400512 }
513
Robert Phillips46a13382018-08-23 13:53:01 -0400514 // TODO: we are losing the mutability of the path here
515 GrShape shape(*path, paint);
Herb Derbydac1ed52018-09-12 17:04:21 -0400516 target->drawShape(clip, runPaint, ctm, shape);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500517 }
Herb Derby1b8dcd12019-11-15 15:21:15 -0500518 } else {
Herb Derbycb718892019-12-07 00:07:42 -0500519 int glyphCount = subRun->glyphCount();
Jim Van Verth54d9c882018-02-08 16:14:48 -0500520 if (0 == glyphCount) {
521 continue;
522 }
523
524 bool skipClip = false;
525 bool submitOp = true;
526 SkIRect clipRect = SkIRect::MakeEmpty();
527 SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
528 SkRRect clipRRect;
529 GrAA aa;
Jim Van Verthb515ae72018-05-23 16:44:55 -0400530 // We can clip geometrically if we're not using SDFs or transformed glyphs,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500531 // and we have an axis-aligned rectangular non-AA clip
Herb Derbycb718892019-12-07 00:07:42 -0500532 if (!subRun->drawAsDistanceFields() && !subRun->needsTransform() &&
Jim Van Verthcf838c72018-03-05 14:40:36 -0500533 clip.isRRect(rtBounds, &clipRRect, &aa) &&
Jim Van Verth54d9c882018-02-08 16:14:48 -0500534 clipRRect.isRect() && GrAA::kNo == aa) {
535 skipClip = true;
536 // We only need to do clipping work if the subrun isn't contained by the clip
537 SkRect subRunBounds;
Herb Derbycb718892019-12-07 00:07:42 -0500538 this->computeSubRunBounds(&subRunBounds, *subRun, viewMatrix, x, y, false);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500539 if (!clipRRect.getBounds().contains(subRunBounds)) {
540 // If the subrun is completely outside, don't add an op for it
541 if (!clipRRect.getBounds().intersects(subRunBounds)) {
542 submitOp = false;
543 }
544 else {
545 clipRRect.getBounds().round(&clipRect);
546 }
547 }
548 }
549
550 if (submitOp) {
Herb Derbycb718892019-12-07 00:07:42 -0500551 auto op = this->makeOp(*subRun, glyphCount, viewMatrix, x, y,
Herb Derbybc6f9c92018-08-08 13:58:45 -0400552 clipRect, paint, filteredColor, props, distanceAdjustTable,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500553 target);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500554 if (op) {
555 if (skipClip) {
556 target->addDrawOp(GrNoClip(), std::move(op));
557 }
558 else {
559 target->addDrawOp(clip, std::move(op));
560 }
561 }
562 }
563 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000564 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000565}
566
Herb Derbya9047642019-12-06 12:12:11 -0500567void GrTextBlob::computeSubRunBounds(SkRect* outBounds, const GrTextBlob::SubRun& subRun,
568 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
569 bool needsGlyphTransform) {
570 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
571 // into device space.
572 // We handle vertex bounds differently for distance field text and bitmap text because
573 // the vertex bounds of bitmap text are in device space. If we are flushing multiple runs
574 // from one blob then we are going to pay the price here of mapping the rect for each run.
575 *outBounds = subRun.vertexBounds();
576 if (needsGlyphTransform) {
577 // Distance field text is positioned with the (X,Y) as part of the glyph position,
578 // and currently the view matrix is applied on the GPU
579 outBounds->offset(SkPoint{x, y} - fInitialOrigin);
580 viewMatrix.mapRect(outBounds);
581 } else {
582 // Bitmap text is fully positioned on the CPU, and offset by an (X,Y) translate in
583 // device space.
584 SkMatrix boundsMatrix = fInitialViewMatrixInverse;
585
586 boundsMatrix.postTranslate(-fInitialOrigin.x(), -fInitialOrigin.y());
587
588 boundsMatrix.postTranslate(x, y);
589
590 boundsMatrix.postConcat(viewMatrix);
591 boundsMatrix.mapRect(outBounds);
592
593 // Due to floating point numerical inaccuracies, we have to round out here
594 outBounds->roundOut(outBounds);
595 }
joshualitt323c2eb2016-01-20 06:48:47 -0800596}
joshualitt2e2202e2015-12-10 11:22:08 -0800597
Herb Derbya9047642019-12-06 12:12:11 -0500598const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
599size_t GrTextBlob::size() const { return fSize; }
600
601std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
602 int glyphCount, const SkMatrix& viewMatrix,
603 SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor,
604 const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
605 GrTextTarget* target) {
Herb Derbycb718892019-12-07 00:07:42 -0500606 SubRun* info = fFirstSubRun;
Herb Derbya9047642019-12-06 12:12:11 -0500607 SkIRect emptyRect = SkIRect::MakeEmpty();
Herb Derbycb718892019-12-07 00:07:42 -0500608 return this->makeOp(*info, glyphCount, viewMatrix, x, y, emptyRect,
Herb Derbya9047642019-12-06 12:12:11 -0500609 paint, filteredColor, props, distanceAdjustTable, target);
610}
611
612bool GrTextBlob::hasW(GrTextBlob::SubRunType type) const {
613 if (type == kTransformedSDFT) {
614 return this->hasPerspective() || fForceWForDistanceFields;
615 } else if (type == kTransformedMask || type == kTransformedPath) {
616 return this->hasPerspective();
Herb Derbye9f691d2019-12-04 12:11:13 -0500617 }
Herb Derbya9047642019-12-06 12:12:11 -0500618
619 // The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
620 // used.
621 return false;
622}
623
624GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
625 const SkZip<SkGlyphVariant, SkPoint>& drawables,
626 const SkStrikeSpec& strikeSpec,
627 GrMaskFormat format) {
628 bool hasW = this->hasW(type);
629 uint32_t glyphsStart = fGlyphsCursor;
630 fGlyphsCursor += drawables.size();
631 uint32_t glyphsEnd = fGlyphsCursor;
632 size_t verticesStart = fVerticesCursor;
633 fVerticesCursor += drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
634 size_t verticesEnd = fVerticesCursor;
635
636 SubRunBufferSpec bufferSpec = std::make_tuple(
637 glyphsStart, glyphsEnd, verticesStart, verticesEnd);
638
639 sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
640
Herb Derbycb718892019-12-07 00:07:42 -0500641 SubRun* subRun = fAlloc.make<SubRun>(
Herb Derbya9047642019-12-06 12:12:11 -0500642 type, this, strikeSpec, format, bufferSpec, std::move(grStrike));
643
Herb Derbycb718892019-12-07 00:07:42 -0500644 subRun->appendGlyphs(drawables);
Herb Derbya9047642019-12-06 12:12:11 -0500645
Herb Derbycb718892019-12-07 00:07:42 -0500646 return subRun;
Herb Derbya9047642019-12-06 12:12:11 -0500647}
648
649void GrTextBlob::addSingleMaskFormat(
650 SubRunType type,
651 const SkZip<SkGlyphVariant, SkPoint>& drawables,
652 const SkStrikeSpec& strikeSpec,
653 GrMaskFormat format) {
654 this->makeSubRun(type, drawables, strikeSpec, format);
655}
656
657void GrTextBlob::addMultiMaskFormat(
658 SubRunType type,
659 const SkZip<SkGlyphVariant, SkPoint>& drawables,
660 const SkStrikeSpec& strikeSpec) {
661 this->setHasBitmap();
662 if (drawables.empty()) { return; }
663
664 auto glyphSpan = drawables.get<0>();
665 SkGlyph* glyph = glyphSpan[0];
666 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
667 size_t startIndex = 0;
668 for (size_t i = 1; i < drawables.size(); i++) {
669 glyph = glyphSpan[i];
670 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
671 if (format != nextFormat) {
672 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
673 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
674 format = nextFormat;
675 startIndex = i;
676 }
677 }
678 auto sameFormat = drawables.last(drawables.size() - startIndex);
679 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
680}
681
682void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
683 const SkStrikeSpec& strikeSpec,
684 const SkFont& runFont,
685 SkScalar minScale,
686 SkScalar maxScale) {
687 this->setHasDistanceField();
688 this->setMinAndMaxScale(minScale, maxScale);
689
690 SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
691 subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
692 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbye9f691d2019-12-04 12:11:13 -0500693}
694
Herb Derbycb718892019-12-07 00:07:42 -0500695GrTextBlob::GrTextBlob(size_t allocSize,
Herb Derbyeba195f2019-12-03 16:44:47 -0500696 GrStrikeCache* strikeCache,
Herb Derbye9f691d2019-12-04 12:11:13 -0500697 const SkMatrix& viewMatrix,
Herb Derbyeba195f2019-12-03 16:44:47 -0500698 SkPoint origin,
699 GrColor color,
Herb Derbyaebc5f82019-12-10 14:07:10 -0500700 SkColor initialLuminance,
Herb Derby00ae9592019-12-03 15:55:56 -0500701 bool forceWForDistanceFields)
Herb Derbycb718892019-12-07 00:07:42 -0500702 : fSize{allocSize}
Herb Derby00ae9592019-12-03 15:55:56 -0500703 , fStrikeCache{strikeCache}
Herb Derbye9f691d2019-12-04 12:11:13 -0500704 , fInitialViewMatrix{viewMatrix}
705 , fInitialViewMatrixInverse{make_inverse(viewMatrix)}
Herb Derbyeba195f2019-12-03 16:44:47 -0500706 , fInitialOrigin{origin}
Herb Derby00ae9592019-12-03 15:55:56 -0500707 , fForceWForDistanceFields{forceWForDistanceFields}
Herb Derbycb718892019-12-07 00:07:42 -0500708 , fColor{color}
Herb Derbyaebc5f82019-12-10 14:07:10 -0500709 , fInitialLuminance{initialLuminance}
Herb Derbycb718892019-12-07 00:07:42 -0500710 , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
Herb Derby00ae9592019-12-03 15:55:56 -0500711
Herb Derbycb718892019-12-07 00:07:42 -0500712void GrTextBlob::insertSubRun(SubRun* subRun) {
713 if (fFirstSubRun == nullptr) {
714 fFirstSubRun = subRun;
715 fLastSubRun = subRun;
716 } else {
717 fLastSubRun->fNextSubRun = subRun;
718 fLastSubRun = subRun;
719 }
720}
721
722std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
Herb Derbya9047642019-12-06 12:12:11 -0500723 SubRun& info, int glyphCount,
724 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
725 const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
726 const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
727 GrMaskFormat format = info.maskFormat();
728
729 GrPaint grPaint;
730 target->makeGrPaint(info.maskFormat(), paint, viewMatrix, &grPaint);
731 std::unique_ptr<GrAtlasTextOp> op;
732 if (info.drawAsDistanceFields()) {
733 // TODO: Can we be even smarter based on the dest transfer function?
734 op = GrAtlasTextOp::MakeDistanceField(
735 target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable,
736 target->colorInfo().isLinearlyBlended(), SkPaintPriv::ComputeLuminanceColor(paint),
737 props, info.isAntiAliased(), info.hasUseLCDText());
Herb Derbyeba195f2019-12-03 16:44:47 -0500738 } else {
Herb Derbya9047642019-12-06 12:12:11 -0500739 op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
740 info.needsTransform());
741 }
742 GrAtlasTextOp::Geometry& geometry = op->geometry();
743 geometry.fViewMatrix = viewMatrix;
744 geometry.fClipRect = clipRect;
745 geometry.fBlob = SkRef(this);
746 geometry.fSubRunPtr = &info;
747 geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
748 geometry.fX = x;
749 geometry.fY = y;
750 op->init();
751 return op;
752}
Herb Derbyeba195f2019-12-03 16:44:47 -0500753
Herb Derbya9047642019-12-06 12:12:11 -0500754void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
755 const SkStrikeSpec& strikeSpec) {
756 this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
757}
Herb Derbyeba195f2019-12-03 16:44:47 -0500758
Herb Derbya9047642019-12-06 12:12:11 -0500759void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
760 const SkFont& runFont,
761 const SkStrikeSpec& strikeSpec) {
762 this->setHasBitmap();
Herb Derbycb718892019-12-07 00:07:42 -0500763 SubRun* subRun = fAlloc.make<SubRun>(this, strikeSpec);
764 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbya9047642019-12-06 12:12:11 -0500765 for (auto [variant, pos] : drawables) {
Herb Derbycb718892019-12-07 00:07:42 -0500766 subRun->fPaths.emplace_back(*variant.path(), pos);
Herb Derbyeba195f2019-12-03 16:44:47 -0500767 }
768}
769
Herb Derbya9047642019-12-06 12:12:11 -0500770void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
771 const SkStrikeSpec& strikeSpec,
772 const SkFont& runFont,
773 SkScalar minScale,
774 SkScalar maxScale) {
775 this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
joshualitt8e0ef292016-02-19 14:13:03 -0800776}
Herb Derbya9047642019-12-06 12:12:11 -0500777
778void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
779 const SkStrikeSpec& strikeSpec) {
780 this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
781}
782
783// -- GrTextBlob::VertexRegenerator ----------------------------------------------------------------
784enum RegenMask {
785 kNoRegen = 0x0,
786 kRegenPos = 0x1,
787 kRegenCol = 0x2,
788 kRegenTex = 0x4,
789 kRegenGlyph = 0x8,
790};
791
792static void regen_positions(char* vertex, size_t vertexStride, SkScalar transX, SkScalar transY) {
793 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
794 for (int i = 0; i < 4; ++i) {
795 point->fX += transX;
796 point->fY += transY;
797 point = SkTAddOffset<SkPoint>(point, vertexStride);
798 }
799}
800
801static void regen_colors(char* vertex, size_t vertexStride, GrColor color) {
802 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
803 // vertices, hence vertexStride - sizeof(SkIPoint16)
804 size_t colorOffset = vertexStride - sizeof(SkIPoint16) - sizeof(GrColor);
805 GrColor* vcolor = reinterpret_cast<GrColor*>(vertex + colorOffset);
806 for (int i = 0; i < 4; ++i) {
807 *vcolor = color;
808 vcolor = SkTAddOffset<GrColor>(vcolor, vertexStride);
809 }
810}
811
812static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
813 bool useDistanceFields) {
814 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
815 // vertices, hence vertexStride - sizeof(SkIPoint16)
816 size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
817
818 uint16_t u0, v0, u1, v1;
819 SkASSERT(glyph);
820 int width = glyph->fBounds.width();
821 int height = glyph->fBounds.height();
822
823 if (useDistanceFields) {
824 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
825 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
826 u1 = u0 + width - 2 * SK_DistanceFieldInset;
827 v1 = v0 + height - 2 * SK_DistanceFieldInset;
828 } else {
829 u0 = glyph->fAtlasLocation.fX;
830 v0 = glyph->fAtlasLocation.fY;
831 u1 = u0 + width;
832 v1 = v0 + height;
833 }
834 // We pack the 2bit page index in the low bit of the u and v texture coords
835 uint32_t pageIndex = glyph->pageIndex();
836 SkASSERT(pageIndex < 4);
837 uint16_t uBit = (pageIndex >> 1) & 0x1;
838 uint16_t vBit = pageIndex & 0x1;
839 u0 <<= 1;
840 u0 |= uBit;
841 v0 <<= 1;
842 v0 |= vBit;
843 u1 <<= 1;
844 u1 |= uBit;
845 v1 <<= 1;
846 v1 |= vBit;
847
848 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
849 textureCoords[0] = u0;
850 textureCoords[1] = v0;
851 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
852 textureCoords[0] = u0;
853 textureCoords[1] = v1;
854 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
855 textureCoords[0] = u1;
856 textureCoords[1] = v0;
857 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
858 textureCoords[0] = u1;
859 textureCoords[1] = v1;
860
861#ifdef DISPLAY_PAGE_INDEX
862 // Enable this to visualize the page from which each glyph is being drawn.
863 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
864 GrColor hackColor;
865 switch (pageIndex) {
866 case 0:
867 hackColor = GrColorPackRGBA(0, 255, 0, 255);
868 break;
869 case 1:
870 hackColor = GrColorPackRGBA(255, 0, 0, 255);;
871 break;
872 case 2:
873 hackColor = GrColorPackRGBA(255, 0, 255, 255);
874 break;
875 case 3:
876 hackColor = GrColorPackRGBA(0, 255, 255, 255);
877 break;
878 default:
879 hackColor = GrColorPackRGBA(0, 0, 0, 255);
880 break;
881 }
882 regen_colors(vertex, vertexStride, hackColor);
883#endif
884}
885
886GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
887 GrTextBlob* blob,
888 GrTextBlob::SubRun* subRun,
889 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
890 GrColor color,
891 GrDeferredUploadTarget* uploadTarget,
892 GrStrikeCache* grStrikeCache,
893 GrAtlasManager* fullAtlasManager)
894 : fResourceProvider(resourceProvider)
895 , fViewMatrix(viewMatrix)
896 , fBlob(blob)
897 , fUploadTarget(uploadTarget)
898 , fGrStrikeCache(grStrikeCache)
899 , fFullAtlasManager(fullAtlasManager)
900 , fSubRun(subRun)
901 , fColor(color) {
902 // Compute translation if any
903 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
904
905 // Because the GrStrikeCache may evict the strike a blob depends on using for
906 // generating its texture coords, we have to track whether or not the strike has
907 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
908 // otherwise we have to get the new strike, and use that to get the correct glyphs.
909 // Because we do not have the packed ids, and thus can't look up our glyphs in the
910 // new strike, we instead keep our ref to the old strike and use the packed ids from
911 // it. These ids will still be valid as long as we hold the ref. When we are done
912 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
913 if (fSubRun->strike()->isAbandoned()) {
914 fRegenFlags |= kRegenGlyph;
915 fRegenFlags |= kRegenTex;
916 }
917 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
918 fRegenFlags |= kRegenCol;
919 }
920 if (0.f != fTransX || 0.f != fTransY) {
921 fRegenFlags |= kRegenPos;
922 }
923}
924
925bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
926 bool regenPos, bool regenCol, bool regenTexCoords,
927 bool regenGlyphs) {
928 SkASSERT(!regenGlyphs || regenTexCoords);
929 if (regenTexCoords) {
930 fSubRun->resetBulkUseToken();
931
932 const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
933
934 if (!fMetricsAndImages.isValid()
935 || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
936 fMetricsAndImages.init(strikeSpec);
937 }
938
939 if (regenGlyphs) {
940 // Take the glyphs from the old strike, and translate them a new strike.
941 sk_sp<GrTextStrike> newStrike = strikeSpec.findOrCreateGrStrike(fGrStrikeCache);
942
943 // Start this batch at the start of the subRun plus any glyphs that were previously
944 // processed.
945 size_t glyphStart = fSubRun->glyphStartIndex() + fCurrGlyph;
946 SkSpan<GrGlyph*> glyphs{&(fBlob->fGlyphs[glyphStart]),
947 fSubRun->glyphCount() - fCurrGlyph};
948
949 // Convert old glyphs to newStrike.
950 for (auto& glyph : glyphs) {
951 SkPackedGlyphID id = glyph->fPackedID;
952 glyph = newStrike->getGlyph(id, fMetricsAndImages.get());
953 SkASSERT(id == glyph->fPackedID);
954 }
955
956 fSubRun->setStrike(newStrike);
957 }
958 }
959
960 sk_sp<GrTextStrike> grStrike = fSubRun->refStrike();
961 bool hasW = fSubRun->hasW();
962 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
963 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
964 fCurrGlyph * kVerticesPerGlyph * vertexStride;
965 result->fFirstVertex = currVertex;
966
967 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
968 GrGlyph* glyph = nullptr;
969 if (regenTexCoords) {
970 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
971 glyph = fBlob->fGlyphs[glyphOffset];
972 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
973
974 if (!fFullAtlasManager->hasGlyph(glyph)) {
975 GrDrawOpAtlas::ErrorCode code;
976 code = grStrike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGrStrikeCache,
977 fFullAtlasManager, glyph,
978 fMetricsAndImages.get(), fSubRun->maskFormat(),
979 fSubRun->needsTransform());
980 if (GrDrawOpAtlas::ErrorCode::kError == code) {
981 // Something horrible has happened - drop the op
982 return false;
983 }
984 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
985 fBrokenRun = glyphIdx > 0;
986 result->fFinished = false;
987 return true;
988 }
989 }
990 auto tokenTracker = fUploadTarget->tokenTracker();
991 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
992 tokenTracker->nextDrawToken());
993 }
994
995 if (regenPos) {
996 regen_positions(currVertex, vertexStride, fTransX, fTransY);
997 }
998 if (regenCol) {
999 regen_colors(currVertex, vertexStride, fColor);
1000 }
1001 if (regenTexCoords) {
1002 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
1003 }
1004
1005 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
1006 ++result->fGlyphsRegenerated;
1007 ++fCurrGlyph;
1008 }
1009
1010 // We may have changed the color so update it here
1011 fSubRun->setColor(fColor);
1012 if (regenTexCoords) {
1013 fSubRun->setAtlasGeneration(fBrokenRun
1014 ? GrDrawOpAtlas::kInvalidAtlasGeneration
1015 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
1016 } else {
1017 // For the non-texCoords case we need to ensure that we update the associated use tokens
1018 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
1019 fUploadTarget->tokenTracker()->nextDrawToken(),
1020 fSubRun->maskFormat());
1021 }
1022 return true;
1023}
1024
1025bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
1026 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
1027 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
1028 // this each time.
1029 if (fSubRun->atlasGeneration() != currentAtlasGen) {
1030 fRegenFlags |= kRegenTex;
1031 }
1032
1033 if (fRegenFlags) {
1034 return this->doRegen(result,
1035 fRegenFlags & kRegenPos,
1036 fRegenFlags & kRegenCol,
1037 fRegenFlags & kRegenTex,
1038 fRegenFlags & kRegenGlyph);
1039 } else {
1040 bool hasW = fSubRun->hasW();
1041 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
1042 result->fFinished = true;
1043 result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
1044 result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
1045 fCurrGlyph * kVerticesPerGlyph * vertexStride;
1046 fCurrGlyph = fSubRun->glyphCount();
1047
1048 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1049 // have a valid atlas generation
1050 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
1051 fUploadTarget->tokenTracker()->nextDrawToken(),
1052 fSubRun->maskFormat());
1053 return true;
1054 }
1055 SK_ABORT("Should not get here");
1056}
1057
1058
1059
1060