blob: 793c848c08e3e6f47d3b8584a263b74d9831c0ad [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
Ben Wagner75d6db72018-09-20 14:39:39 -040023template <size_t N> static size_t sk_align(size_t s) {
24 return ((s + (N-1)) / N) * N;
25}
26
Herb Derbya9047642019-12-06 12:12:11 -050027static void calculate_translation(bool applyVM,
28 const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
29 const SkMatrix& currentViewMatrix, SkScalar currentX,
30 SkScalar currentY, SkScalar* transX, SkScalar* transY) {
31 if (applyVM) {
32 *transX = newViewMatrix.getTranslateX() +
33 newViewMatrix.getScaleX() * (newX - currentX) +
34 newViewMatrix.getSkewX() * (newY - currentY) -
35 currentViewMatrix.getTranslateX();
36
37 *transY = newViewMatrix.getTranslateY() +
38 newViewMatrix.getSkewY() * (newX - currentX) +
39 newViewMatrix.getScaleY() * (newY - currentY) -
40 currentViewMatrix.getTranslateY();
41 } else {
42 *transX = newX - currentX;
43 *transY = newY - currentY;
44 }
45}
46
47static SkMatrix make_inverse(const SkMatrix& matrix) {
48 SkMatrix inverseMatrix;
49 if (!matrix.invert(&inverseMatrix)) {
50 inverseMatrix = SkMatrix::I();
51 }
52 return inverseMatrix;
53}
54
55// -- GrTextBlob::Key ------------------------------------------------------------------------------
56GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); }
57
58bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const {
59 return 0 == memcmp(this, &other, sizeof(Key));
60}
61
62// -- GrTextBlob::PathGlyph ------------------------------------------------------------------------
63GrTextBlob::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
64 : fPath(path)
65 , fOrigin(origin) {}
66
67// -- GrTextBlob::SubRun ---------------------------------------------------------------------------
68GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
69 GrMaskFormat format, const GrTextBlob::SubRunBufferSpec& bufferSpec,
70 sk_sp<GrTextStrike>&& grStrike)
71 : fType{type}
72 , fBlob{textBlob}
73 , fMaskFormat{format}
74 , fGlyphStartIndex{std::get<0>(bufferSpec)}
75 , fGlyphEndIndex{std::get<1>(bufferSpec)}
76 , fVertexStartIndex{std::get<2>(bufferSpec)}
77 , fVertexEndIndex{std::get<3>(bufferSpec)}
78 , fStrikeSpec{strikeSpec}
79 , fStrike{grStrike}
80 , fColor{textBlob->fColor}
81 , fX{textBlob->fInitialOrigin.x()}
82 , fY{textBlob->fInitialOrigin.y()}
83 , fCurrentViewMatrix{textBlob->fInitialViewMatrix} {
84 SkASSERT(type != kTransformedPath);
85}
86
87GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
88 : fType{kTransformedPath}
89 , fBlob{textBlob}
90 , fMaskFormat{kA8_GrMaskFormat}
91 , fGlyphStartIndex{0}
92 , fGlyphEndIndex{0}
93 , fVertexStartIndex{0}
94 , fVertexEndIndex{0}
95 , fStrikeSpec{strikeSpec}
96 , fStrike{nullptr}
97 , fColor{textBlob->fColor}
98 , fPaths{} { }
99
100void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
101 GrTextStrike* grStrike = fStrike.get();
102 SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
103 uint32_t glyphCursor = fGlyphStartIndex;
104 size_t vertexCursor = fVertexStartIndex;
105 bool hasW = this->hasW();
106 GrColor color = this->color();
107 // glyphs drawn in perspective must always have a w coord.
108 SkASSERT(hasW || !fBlob->fInitialViewMatrix.hasPerspective());
109 size_t vertexStride = GetVertexStride(fMaskFormat, hasW);
110 // We always write the third position component used by SDFs. If it is unused it gets
111 // overwritten. Similarly, we always write the color and the blob will later overwrite it
112 // with texture coords if it is unused.
113 size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
114 for (auto [variant, pos] : drawables) {
115 SkGlyph* skGlyph = variant;
116 GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
117 // Only floor the device coordinates.
118 SkRect dstRect;
119 if (!this->needsTransform()) {
120 pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
121 dstRect = grGlyph->destRect(pos);
122 } else {
123 dstRect = grGlyph->destRect(pos, strikeToSource);
124 }
125
126 this->joinGlyphBounds(dstRect);
127
128 intptr_t vertex = reinterpret_cast<intptr_t>(fBlob->fVertices + vertexCursor);
129
130 // V0
131 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
132 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
133 vertex += vertexStride;
134
135 // V1
136 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
137 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
138 vertex += vertexStride;
139
140 // V2
141 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
142 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
143 vertex += vertexStride;
144
145 // V3
146 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
147 *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
148
149 vertexCursor += vertexStride * kVerticesPerGlyph;
150 fBlob->fGlyphs[glyphCursor++] = grGlyph;
151 }
152 SkASSERT(glyphCursor == fGlyphEndIndex);
153 SkASSERT(vertexCursor == fVertexEndIndex);
154}
155
156void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
157
158GrDrawOpAtlas::BulkUseTokenUpdater* GrTextBlob::SubRun::bulkUseToken() { return &fBulkUseToken; }
159void GrTextBlob::SubRun::setStrike(sk_sp<GrTextStrike> strike) { fStrike = std::move(strike); }
160GrTextStrike* GrTextBlob::SubRun::strike() const { return fStrike.get(); }
161sk_sp<GrTextStrike> GrTextBlob::SubRun::refStrike() const { return fStrike; }
162void GrTextBlob::SubRun::setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
163uint64_t GrTextBlob::SubRun::atlasGeneration() const { return fAtlasGeneration; }
164size_t GrTextBlob::SubRun::vertexStartIndex() const { return fVertexStartIndex; }
165uint32_t GrTextBlob::SubRun::glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
166uint32_t GrTextBlob::SubRun::glyphStartIndex() const { return fGlyphStartIndex; }
167void GrTextBlob::SubRun::setColor(GrColor color) { fColor = color; }
168GrColor GrTextBlob::SubRun::color() const { return fColor; }
169GrMaskFormat GrTextBlob::SubRun::maskFormat() const { return fMaskFormat; }
170const SkRect& GrTextBlob::SubRun::vertexBounds() const { return fVertexBounds; }
171void GrTextBlob::SubRun::joinGlyphBounds(const SkRect& glyphBounds) {
172 fVertexBounds.joinNonEmptyArg(glyphBounds);
173}
174
175void GrTextBlob::SubRun::init(const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
176 fCurrentViewMatrix = viewMatrix;
177 fX = x;
178 fY = y;
179}
180
181void GrTextBlob::SubRun::computeTranslation(const SkMatrix& viewMatrix,
182 SkScalar x, SkScalar y, SkScalar* transX,
183 SkScalar* transY) {
184 // Don't use the matrix to translate on distance field for fallback subruns.
185 calculate_translation(!this->drawAsDistanceFields() && !this->needsTransform(), viewMatrix,
186 x, y, fCurrentViewMatrix, fX, fY, transX, transY);
187 fCurrentViewMatrix = viewMatrix;
188 fX = x;
189 fY = y;
190}
191
192bool GrTextBlob::SubRun::drawAsDistanceFields() const { return fType == kTransformedSDFT; }
193
194bool GrTextBlob::SubRun::drawAsPaths() const { return fType == kTransformedPath; }
195
196bool GrTextBlob::SubRun::needsTransform() const {
197 return fType == kTransformedPath
198 || fType == kTransformedMask
199 || fType == kTransformedSDFT;
200}
201
202bool GrTextBlob::SubRun::hasW() const {
203 return fBlob->hasW(fType);
204}
205
206void GrTextBlob::SubRun::setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
207bool GrTextBlob::SubRun::hasUseLCDText() const { return fFlags.useLCDText; }
208void GrTextBlob::SubRun::setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
209bool GrTextBlob::SubRun::isAntiAliased() const { return fFlags.antiAliased; }
210const SkStrikeSpec& GrTextBlob::SubRun::strikeSpec() const { return fStrikeSpec; }
211
212// -- GrTextBlob -----------------------------------------------------------------------------------
213void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
214void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
215void* GrTextBlob::operator new(size_t, void* p) { return p; }
216
217GrTextBlob::~GrTextBlob() = default;
218
Herb Derbya00da612019-03-04 17:10:01 -0500219sk_sp<GrTextBlob> GrTextBlob::Make(int glyphCount,
Herb Derbyeba195f2019-12-03 16:44:47 -0500220 GrStrikeCache* strikeCache,
Herb Derbye9f691d2019-12-04 12:11:13 -0500221 const SkMatrix& viewMatrix,
Herb Derbyeba195f2019-12-03 16:44:47 -0500222 SkPoint origin,
Herb Derbya00da612019-03-04 17:10:01 -0500223 GrColor color,
Herb Derbyeba195f2019-12-03 16:44:47 -0500224 bool forceWForDistanceFields) {
Herb Derbycd498e12019-12-06 14:17:31 -0500225
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500226 static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
227 size_t vertexSize = sizeof(Mask2DVertex);
Herb Derbye74c7762019-12-04 14:15:41 -0500228 if (viewMatrix.hasPerspective() || forceWForDistanceFields) {
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500229 static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex));
230 vertexSize = sizeof(SDFT3DVertex);
Herb Derbye74c7762019-12-04 14:15:41 -0500231 }
232
Herb Derbycd498e12019-12-06 14:17:31 -0500233 size_t quadSize = kVerticesPerGlyph * vertexSize;
234
Herb Derby86240592018-05-24 16:12:31 -0400235 // We allocate size for the GrTextBlob itself, plus size for the vertices array,
joshualitt92303772016-02-10 11:55:52 -0800236 // and size for the glyphIds array.
Herb Derbye74c7762019-12-04 14:15:41 -0500237 size_t verticesCount = glyphCount * quadSize;
Ben Wagner75d6db72018-09-20 14:39:39 -0400238
Herb Derbyf7d5d742018-11-16 13:24:32 -0500239 size_t blobStart = 0;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500240 size_t vertex = sk_align<alignof(char)> (blobStart + sizeof(GrTextBlob) * 1);
241 size_t glyphs = sk_align<alignof(GrGlyph*)> (vertex + sizeof(char) * verticesCount);
242 size_t size = (glyphs + sizeof(GrGlyph*) * glyphCount);
joshualitt92303772016-02-10 11:55:52 -0800243
Herb Derbyb12175f2018-05-23 16:38:09 -0400244 void* allocation = ::operator new (size);
245
joshualitt92303772016-02-10 11:55:52 -0800246 if (CACHE_SANITY_CHECK) {
247 sk_bzero(allocation, size);
248 }
249
Herb Derby00ae9592019-12-03 15:55:56 -0500250 sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
Herb Derbye9f691d2019-12-04 12:11:13 -0500251 size, strikeCache, viewMatrix, origin, color, forceWForDistanceFields}};
joshualitt92303772016-02-10 11:55:52 -0800252
253 // setup offsets for vertices / glyphs
Herb Derbyf7d5d742018-11-16 13:24:32 -0500254 blob->fVertices = SkTAddOffset<char>(blob.get(), vertex);
Herb Derby1b8dcd12019-11-15 15:21:15 -0500255 blob->fGlyphs = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
joshualitt92303772016-02-10 11:55:52 -0800256
Herb Derbyf7d5d742018-11-16 13:24:32 -0500257 return blob;
joshualitt92303772016-02-10 11:55:52 -0800258}
259
Herb Derbya9047642019-12-06 12:12:11 -0500260void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
261 const GrTextContext::Options& options,
262 const SkPaint& paint,
263 const SkMatrix& viewMatrix,
264 const SkSurfaceProps& props,
265 const SkGlyphRunList& glyphRunList,
266 SkGlyphRunListPainter* glyphPainter) {
267 const SkPaint& runPaint = glyphRunList.paint();
268 this->initReusableBlob(SkPaintPriv::ComputeLuminanceColor(runPaint));
269
270 glyphPainter->processGlyphRunList(glyphRunList,
271 viewMatrix,
272 props,
273 shaderCaps.supportsDistanceFieldText(),
274 options,
275 this);
276}
277
278void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::BlurRec& blurRec,
279 const SkPaint& paint) {
280 fKey = key;
281 if (key.fHasBlur) {
282 fBlurRec = blurRec;
283 }
284 if (key.fStyle != SkPaint::kFill_Style) {
285 fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
286 fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
287 fStrokeInfo.fJoin = paint.getStrokeJoin();
288 }
289}
290const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
291uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
292
293bool GrTextBlob::hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); }
294bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
295bool GrTextBlob::hasPerspective() const { return fInitialViewMatrix.hasPerspective(); }
296
297void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
298void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; }
299void GrTextBlob::setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
300 // we init fMaxMinScale and fMinMaxScale in the constructor
301 fMaxMinScale = SkMaxScalar(scaledMin, fMaxMinScale);
302 fMinMaxScale = SkMinScalar(scaledMax, fMinMaxScale);
303}
304
305size_t GrTextBlob::GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
306 switch (maskFormat) {
307 case kA8_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500308 return hasWCoord ? sizeof(SDFT3DVertex) : sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500309 case kARGB_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500310 return hasWCoord ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500311 default:
312 SkASSERT(!hasWCoord);
Herb Derbycd498e12019-12-06 14:17:31 -0500313 return sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500314 }
315}
316
Herb Derby0edb2142018-10-16 17:04:11 -0400317bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
318 const SkMaskFilterBase::BlurRec& blurRec,
319 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualittfd5f6c12015-12-10 07:44:50 -0800320 // If we have LCD text then our canonical color will be set to transparent, in this case we have
321 // to regenerate the blob on any color change
322 // We use the grPaint to get any color filter effects
323 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
Mike Reedec7278e2019-02-01 14:00:34 -0500324 fLuminanceColor != SkPaintPriv::ComputeLuminanceColor(paint)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800325 return true;
326 }
327
joshualitt8e0ef292016-02-19 14:13:03 -0800328 if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800329 return true;
330 }
331
Brian Salomon5c6ac642017-12-19 11:09:32 -0500332 /** This could be relaxed for blobs with only distance field glyphs. */
joshualitt8e0ef292016-02-19 14:13:03 -0800333 if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800334 return true;
335 }
336
337 // We only cache one masked version
338 if (fKey.fHasBlur &&
Mike Reed1be1f8d2018-03-14 13:01:17 -0400339 (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800340 return true;
341 }
342
343 // Similarly, we only cache one version for each style
344 if (fKey.fStyle != SkPaint::kFill_Style &&
Herb Derbybc6f9c92018-08-08 13:58:45 -0400345 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
346 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
347 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
joshualittfd5f6c12015-12-10 07:44:50 -0800348 return true;
349 }
350
351 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
352 // for mixed blobs if this becomes an issue.
353 if (this->hasBitmap() && this->hasDistanceField()) {
Herb Derbyeba195f2019-12-03 16:44:47 -0500354 // Identical view matrices and we can reuse in all cases
355 return !(fInitialViewMatrix.cheapEqualTo(viewMatrix) && SkPoint{x, y} == fInitialOrigin);
joshualittfd5f6c12015-12-10 07:44:50 -0800356 }
357
358 if (this->hasBitmap()) {
joshualitt8e0ef292016-02-19 14:13:03 -0800359 if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
360 fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
361 fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
362 fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800363 return true;
364 }
365
Herb Derby9830fc42019-09-12 10:58:26 -0400366 // TODO(herb): this is not needed for full pixel glyph choice, but is needed to adjust
367 // the quads properly. Devise a system that regenerates the quads from original data
368 // using the transform to allow this to be used in general.
369
370 // We can update the positions in the text blob without regenerating the whole
371 // blob, but only for integer translations.
372 // This cool bit of math will determine the necessary translation to apply to the
373 // already generated vertex coordinates to move them to the correct position.
374 // Figure out the translation in view space given a translation in source space.
375 SkScalar transX = viewMatrix.getTranslateX() +
Herb Derbyeba195f2019-12-03 16:44:47 -0500376 viewMatrix.getScaleX() * (x - fInitialOrigin.x()) +
377 viewMatrix.getSkewX() * (y - fInitialOrigin.y()) -
Herb Derby9830fc42019-09-12 10:58:26 -0400378 fInitialViewMatrix.getTranslateX();
379 SkScalar transY = viewMatrix.getTranslateY() +
Herb Derbyeba195f2019-12-03 16:44:47 -0500380 viewMatrix.getSkewY() * (x - fInitialOrigin.x()) +
381 viewMatrix.getScaleY() * (y - fInitialOrigin.y()) -
Herb Derby9830fc42019-09-12 10:58:26 -0400382 fInitialViewMatrix.getTranslateY();
383 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
384 return true;
joshualittfd5f6c12015-12-10 07:44:50 -0800385 }
joshualittfd5f6c12015-12-10 07:44:50 -0800386 } else if (this->hasDistanceField()) {
387 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
388 // distance field being generated, so we have to regenerate in those cases
389 SkScalar newMaxScale = viewMatrix.getMaxScale();
joshualitt8e0ef292016-02-19 14:13:03 -0800390 SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale();
joshualittfd5f6c12015-12-10 07:44:50 -0800391 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
392 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
393 return true;
394 }
joshualittfd5f6c12015-12-10 07:44:50 -0800395 }
396
joshualittfd5f6c12015-12-10 07:44:50 -0800397 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
398 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
399 // the blob anyways at flush time, so no need to regenerate explicitly
400 return false;
401}
402
Herb Derbyc1b482c2018-08-09 15:02:27 -0400403void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
404 const GrDistanceFieldAdjustTable* distanceAdjustTable,
Brian Osmancf860852018-10-31 14:04:39 -0400405 const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
Robert Phillipse4643cc2018-08-14 13:01:29 -0400406 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500407
Herb Derby1b8dcd12019-11-15 15:21:15 -0500408 for (auto& subRun : fSubRuns) {
409 if (subRun.drawAsPaths()) {
Herb Derbydac1ed52018-09-12 17:04:21 -0400410 SkPaint runPaint{paint};
Herb Derby1b8dcd12019-11-15 15:21:15 -0500411 runPaint.setAntiAlias(subRun.isAntiAliased());
412 // If there are shaders, blurs or styles, the path must be scaled into source
413 // space independently of the CTM. This allows the CTM to be correct for the
414 // different effects.
415 GrStyle style(runPaint);
Herb Derby9f491482018-08-08 11:53:00 -0400416
Herb Derby1b8dcd12019-11-15 15:21:15 -0500417 bool scalePath = runPaint.getShader()
418 || style.applies()
419 || runPaint.getMaskFilter();
Robert Phillips137ca522018-08-15 10:14:33 -0400420
Herb Derby1b8dcd12019-11-15 15:21:15 -0500421 // The origin for the blob may have changed, so figure out the delta.
Herb Derbyeba195f2019-12-03 16:44:47 -0500422 SkVector originShift = SkPoint{x, y} - fInitialOrigin;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500423
424 for (const auto& pathGlyph : subRun.fPaths) {
425 SkMatrix ctm{viewMatrix};
426 SkMatrix pathMatrix = SkMatrix::MakeScale(subRun.fStrikeSpec.strikeToSourceRatio());
427 // Shift the original glyph location in source space to the position of the new
428 // blob.
429 pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
430 originShift.y() + pathGlyph.fOrigin.y());
Herb Derbydac1ed52018-09-12 17:04:21 -0400431
432 // TmpPath must be in the same scope as GrShape shape below.
Robert Phillips137ca522018-08-15 10:14:33 -0400433 SkTLazy<SkPath> tmpPath;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500434 const SkPath* path = &pathGlyph.fPath;
Herb Derby2984d262019-11-20 14:40:39 -0500435 if (!scalePath) {
436 // Scale can be applied to CTM -- no effects.
Herb Derby2984d262019-11-20 14:40:39 -0500437 ctm.preConcat(pathMatrix);
Robert Phillipsd20d2612018-08-28 10:09:01 -0400438 } else {
Herb Derby2984d262019-11-20 14:40:39 -0500439 // Scale the outline into source space.
Herb Derbydac1ed52018-09-12 17:04:21 -0400440
Herb Derby2984d262019-11-20 14:40:39 -0500441 // Transform the path form the normalized outline to source space. This
442 // way the CTM will remain the same so it can be used by the effects.
443 SkPath* sourceOutline = tmpPath.init();
444 path->transform(pathMatrix, sourceOutline);
445 sourceOutline->setIsVolatile(true);
446 path = sourceOutline;
Robert Phillips137ca522018-08-15 10:14:33 -0400447 }
448
Robert Phillips46a13382018-08-23 13:53:01 -0400449 // TODO: we are losing the mutability of the path here
450 GrShape shape(*path, paint);
Herb Derbydac1ed52018-09-12 17:04:21 -0400451 target->drawShape(clip, runPaint, ctm, shape);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500452 }
Herb Derby1b8dcd12019-11-15 15:21:15 -0500453 } else {
454 int glyphCount = subRun.glyphCount();
Jim Van Verth54d9c882018-02-08 16:14:48 -0500455 if (0 == glyphCount) {
456 continue;
457 }
458
459 bool skipClip = false;
460 bool submitOp = true;
461 SkIRect clipRect = SkIRect::MakeEmpty();
462 SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
463 SkRRect clipRRect;
464 GrAA aa;
Jim Van Verthb515ae72018-05-23 16:44:55 -0400465 // We can clip geometrically if we're not using SDFs or transformed glyphs,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500466 // and we have an axis-aligned rectangular non-AA clip
Herb Derby1b8dcd12019-11-15 15:21:15 -0500467 if (!subRun.drawAsDistanceFields() && !subRun.needsTransform() &&
Jim Van Verthcf838c72018-03-05 14:40:36 -0500468 clip.isRRect(rtBounds, &clipRRect, &aa) &&
Jim Van Verth54d9c882018-02-08 16:14:48 -0500469 clipRRect.isRect() && GrAA::kNo == aa) {
470 skipClip = true;
471 // We only need to do clipping work if the subrun isn't contained by the clip
472 SkRect subRunBounds;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500473 this->computeSubRunBounds(&subRunBounds, subRun, viewMatrix, x, y, false);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500474 if (!clipRRect.getBounds().contains(subRunBounds)) {
475 // If the subrun is completely outside, don't add an op for it
476 if (!clipRRect.getBounds().intersects(subRunBounds)) {
477 submitOp = false;
478 }
479 else {
480 clipRRect.getBounds().round(&clipRect);
481 }
482 }
483 }
484
485 if (submitOp) {
Herb Derby1b8dcd12019-11-15 15:21:15 -0500486 auto op = this->makeOp(subRun, glyphCount, viewMatrix, x, y,
Herb Derbybc6f9c92018-08-08 13:58:45 -0400487 clipRect, paint, filteredColor, props, distanceAdjustTable,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500488 target);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500489 if (op) {
490 if (skipClip) {
491 target->addDrawOp(GrNoClip(), std::move(op));
492 }
493 else {
494 target->addDrawOp(clip, std::move(op));
495 }
496 }
497 }
498 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000499 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000500}
501
Herb Derbya9047642019-12-06 12:12:11 -0500502void GrTextBlob::computeSubRunBounds(SkRect* outBounds, const GrTextBlob::SubRun& subRun,
503 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
504 bool needsGlyphTransform) {
505 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
506 // into device space.
507 // We handle vertex bounds differently for distance field text and bitmap text because
508 // the vertex bounds of bitmap text are in device space. If we are flushing multiple runs
509 // from one blob then we are going to pay the price here of mapping the rect for each run.
510 *outBounds = subRun.vertexBounds();
511 if (needsGlyphTransform) {
512 // Distance field text is positioned with the (X,Y) as part of the glyph position,
513 // and currently the view matrix is applied on the GPU
514 outBounds->offset(SkPoint{x, y} - fInitialOrigin);
515 viewMatrix.mapRect(outBounds);
516 } else {
517 // Bitmap text is fully positioned on the CPU, and offset by an (X,Y) translate in
518 // device space.
519 SkMatrix boundsMatrix = fInitialViewMatrixInverse;
520
521 boundsMatrix.postTranslate(-fInitialOrigin.x(), -fInitialOrigin.y());
522
523 boundsMatrix.postTranslate(x, y);
524
525 boundsMatrix.postConcat(viewMatrix);
526 boundsMatrix.mapRect(outBounds);
527
528 // Due to floating point numerical inaccuracies, we have to round out here
529 outBounds->roundOut(outBounds);
530 }
joshualitt323c2eb2016-01-20 06:48:47 -0800531}
joshualitt2e2202e2015-12-10 11:22:08 -0800532
Herb Derby86240592018-05-24 16:12:31 -0400533void GrTextBlob::AssertEqual(const GrTextBlob& l, const GrTextBlob& r) {
joshualitt2f2ee832016-02-10 08:52:24 -0800534 SkASSERT_RELEASE(l.fSize == r.fSize);
joshualitt259fbf12015-07-21 11:39:34 -0700535
joshualitt2f2ee832016-02-10 08:52:24 -0800536 SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
537 SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
joshualitt259fbf12015-07-21 11:39:34 -0700538
joshualitt2f2ee832016-02-10 08:52:24 -0800539 SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
540 SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
541 SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
joshualitt259fbf12015-07-21 11:39:34 -0700542
joshualitt2f2ee832016-02-10 08:52:24 -0800543 SkASSERT_RELEASE(l.fKey == r.fKey);
joshualitt2f2ee832016-02-10 08:52:24 -0800544 //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical
545 SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale);
546 SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
547 SkASSERT_RELEASE(l.fTextType == r.fTextType);
joshualitt259fbf12015-07-21 11:39:34 -0700548
Herb Derby1b8dcd12019-11-15 15:21:15 -0500549 for(auto t : SkMakeZip(l.fSubRuns, r.fSubRuns)) {
550 const SubRun& lSubRun = std::get<0>(t);
551 const SubRun& rSubRun = std::get<1>(t);
552 SkASSERT(lSubRun.drawAsPaths() == rSubRun.drawAsPaths());
553 if (!lSubRun.drawAsPaths()) {
joshualitt259fbf12015-07-21 11:39:34 -0700554
joshualitt2f2ee832016-02-10 08:52:24 -0800555 // TODO we can do this check, but we have to apply the VM to the old vertex bounds
556 //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds());
joshualitt259fbf12015-07-21 11:39:34 -0700557
joshualitt2f2ee832016-02-10 08:52:24 -0800558 if (lSubRun.strike()) {
559 SkASSERT_RELEASE(rSubRun.strike());
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500560 SkASSERT_RELEASE(GrTextStrike::GetKey(*lSubRun.strike()) ==
561 GrTextStrike::GetKey(*rSubRun.strike()));
joshualitt2f2ee832016-02-10 08:52:24 -0800562
563 } else {
564 SkASSERT_RELEASE(!rSubRun.strike());
565 }
566
567 SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
joshualitt2f2ee832016-02-10 08:52:24 -0800568 SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
joshualitt2f2ee832016-02-10 08:52:24 -0800569 SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
570 SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
571 SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500572 } else {
573 SkASSERT_RELEASE(lSubRun.fPaths.size() == rSubRun.fPaths.size());
574 for(auto p : SkMakeZip(lSubRun.fPaths, rSubRun.fPaths)) {
575 const PathGlyph& lPath = std::get<0>(p);
576 const PathGlyph& rPath = std::get<1>(p);
577 SkASSERT_RELEASE(lPath.fPath == rPath.fPath);
578 // We can't assert that these have the same translations
579 }
Jim Van Verth54d9c882018-02-08 16:14:48 -0500580 }
joshualitt259fbf12015-07-21 11:39:34 -0700581 }
582}
joshualitt8e0ef292016-02-19 14:13:03 -0800583
Herb Derbya9047642019-12-06 12:12:11 -0500584void GrTextBlob::initReusableBlob(SkColor luminanceColor) {
585 fLuminanceColor = luminanceColor;
586}
587
588const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
589size_t GrTextBlob::size() const { return fSize; }
590
591std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
592 int glyphCount, const SkMatrix& viewMatrix,
593 SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor,
594 const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
595 GrTextTarget* target) {
596 GrTextBlob::SubRun& info = fSubRuns[0];
597 SkIRect emptyRect = SkIRect::MakeEmpty();
598 return this->makeOp(info, glyphCount, viewMatrix, x, y, emptyRect,
599 paint, filteredColor, props, distanceAdjustTable, target);
600}
601
602bool GrTextBlob::hasW(GrTextBlob::SubRunType type) const {
603 if (type == kTransformedSDFT) {
604 return this->hasPerspective() || fForceWForDistanceFields;
605 } else if (type == kTransformedMask || type == kTransformedPath) {
606 return this->hasPerspective();
Herb Derbye9f691d2019-12-04 12:11:13 -0500607 }
Herb Derbya9047642019-12-06 12:12:11 -0500608
609 // The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
610 // used.
611 return false;
612}
613
614GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
615 const SkZip<SkGlyphVariant, SkPoint>& drawables,
616 const SkStrikeSpec& strikeSpec,
617 GrMaskFormat format) {
618 bool hasW = this->hasW(type);
619 uint32_t glyphsStart = fGlyphsCursor;
620 fGlyphsCursor += drawables.size();
621 uint32_t glyphsEnd = fGlyphsCursor;
622 size_t verticesStart = fVerticesCursor;
623 fVerticesCursor += drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
624 size_t verticesEnd = fVerticesCursor;
625
626 SubRunBufferSpec bufferSpec = std::make_tuple(
627 glyphsStart, glyphsEnd, verticesStart, verticesEnd);
628
629 sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
630
631 SubRun& subRun = fSubRuns.emplace_back(
632 type, this, strikeSpec, format, bufferSpec, std::move(grStrike));
633
634 subRun.appendGlyphs(drawables);
635
636 return &subRun;
637}
638
639void GrTextBlob::addSingleMaskFormat(
640 SubRunType type,
641 const SkZip<SkGlyphVariant, SkPoint>& drawables,
642 const SkStrikeSpec& strikeSpec,
643 GrMaskFormat format) {
644 this->makeSubRun(type, drawables, strikeSpec, format);
645}
646
647void GrTextBlob::addMultiMaskFormat(
648 SubRunType type,
649 const SkZip<SkGlyphVariant, SkPoint>& drawables,
650 const SkStrikeSpec& strikeSpec) {
651 this->setHasBitmap();
652 if (drawables.empty()) { return; }
653
654 auto glyphSpan = drawables.get<0>();
655 SkGlyph* glyph = glyphSpan[0];
656 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
657 size_t startIndex = 0;
658 for (size_t i = 1; i < drawables.size(); i++) {
659 glyph = glyphSpan[i];
660 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
661 if (format != nextFormat) {
662 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
663 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
664 format = nextFormat;
665 startIndex = i;
666 }
667 }
668 auto sameFormat = drawables.last(drawables.size() - startIndex);
669 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
670}
671
672void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
673 const SkStrikeSpec& strikeSpec,
674 const SkFont& runFont,
675 SkScalar minScale,
676 SkScalar maxScale) {
677 this->setHasDistanceField();
678 this->setMinAndMaxScale(minScale, maxScale);
679
680 SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
681 subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
682 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbye9f691d2019-12-04 12:11:13 -0500683}
684
Herb Derbyeba195f2019-12-03 16:44:47 -0500685GrTextBlob::GrTextBlob(size_t size,
686 GrStrikeCache* strikeCache,
Herb Derbye9f691d2019-12-04 12:11:13 -0500687 const SkMatrix& viewMatrix,
Herb Derbyeba195f2019-12-03 16:44:47 -0500688 SkPoint origin,
689 GrColor color,
Herb Derby00ae9592019-12-03 15:55:56 -0500690 bool forceWForDistanceFields)
691 : fSize{size}
692 , fStrikeCache{strikeCache}
Herb Derbye9f691d2019-12-04 12:11:13 -0500693 , fInitialViewMatrix{viewMatrix}
694 , fInitialViewMatrixInverse{make_inverse(viewMatrix)}
Herb Derbyeba195f2019-12-03 16:44:47 -0500695 , fInitialOrigin{origin}
Herb Derby00ae9592019-12-03 15:55:56 -0500696 , fForceWForDistanceFields{forceWForDistanceFields}
697 , fColor{color} { }
698
Herb Derbya9047642019-12-06 12:12:11 -0500699inline std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
700 SubRun& info, int glyphCount,
701 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
702 const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
703 const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
704 GrMaskFormat format = info.maskFormat();
705
706 GrPaint grPaint;
707 target->makeGrPaint(info.maskFormat(), paint, viewMatrix, &grPaint);
708 std::unique_ptr<GrAtlasTextOp> op;
709 if (info.drawAsDistanceFields()) {
710 // TODO: Can we be even smarter based on the dest transfer function?
711 op = GrAtlasTextOp::MakeDistanceField(
712 target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable,
713 target->colorInfo().isLinearlyBlended(), SkPaintPriv::ComputeLuminanceColor(paint),
714 props, info.isAntiAliased(), info.hasUseLCDText());
Herb Derbyeba195f2019-12-03 16:44:47 -0500715 } else {
Herb Derbya9047642019-12-06 12:12:11 -0500716 op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
717 info.needsTransform());
718 }
719 GrAtlasTextOp::Geometry& geometry = op->geometry();
720 geometry.fViewMatrix = viewMatrix;
721 geometry.fClipRect = clipRect;
722 geometry.fBlob = SkRef(this);
723 geometry.fSubRunPtr = &info;
724 geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
725 geometry.fX = x;
726 geometry.fY = y;
727 op->init();
728 return op;
729}
Herb Derbyeba195f2019-12-03 16:44:47 -0500730
Herb Derbya9047642019-12-06 12:12:11 -0500731void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
732 const SkStrikeSpec& strikeSpec) {
733 this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
734}
Herb Derbyeba195f2019-12-03 16:44:47 -0500735
Herb Derbya9047642019-12-06 12:12:11 -0500736void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
737 const SkFont& runFont,
738 const SkStrikeSpec& strikeSpec) {
739 this->setHasBitmap();
740 SubRun& subRun = fSubRuns.emplace_back(this, strikeSpec);
741 subRun.setAntiAliased(runFont.hasSomeAntiAliasing());
742 for (auto [variant, pos] : drawables) {
743 subRun.fPaths.emplace_back(*variant.path(), pos);
Herb Derbyeba195f2019-12-03 16:44:47 -0500744 }
745}
746
Herb Derbya9047642019-12-06 12:12:11 -0500747void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
748 const SkStrikeSpec& strikeSpec,
749 const SkFont& runFont,
750 SkScalar minScale,
751 SkScalar maxScale) {
752 this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
joshualitt8e0ef292016-02-19 14:13:03 -0800753}
Herb Derbya9047642019-12-06 12:12:11 -0500754
755void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
756 const SkStrikeSpec& strikeSpec) {
757 this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
758}
759
760// -- GrTextBlob::VertexRegenerator ----------------------------------------------------------------
761enum RegenMask {
762 kNoRegen = 0x0,
763 kRegenPos = 0x1,
764 kRegenCol = 0x2,
765 kRegenTex = 0x4,
766 kRegenGlyph = 0x8,
767};
768
769static void regen_positions(char* vertex, size_t vertexStride, SkScalar transX, SkScalar transY) {
770 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
771 for (int i = 0; i < 4; ++i) {
772 point->fX += transX;
773 point->fY += transY;
774 point = SkTAddOffset<SkPoint>(point, vertexStride);
775 }
776}
777
778static void regen_colors(char* vertex, size_t vertexStride, GrColor color) {
779 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
780 // vertices, hence vertexStride - sizeof(SkIPoint16)
781 size_t colorOffset = vertexStride - sizeof(SkIPoint16) - sizeof(GrColor);
782 GrColor* vcolor = reinterpret_cast<GrColor*>(vertex + colorOffset);
783 for (int i = 0; i < 4; ++i) {
784 *vcolor = color;
785 vcolor = SkTAddOffset<GrColor>(vcolor, vertexStride);
786 }
787}
788
789static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
790 bool useDistanceFields) {
791 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
792 // vertices, hence vertexStride - sizeof(SkIPoint16)
793 size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
794
795 uint16_t u0, v0, u1, v1;
796 SkASSERT(glyph);
797 int width = glyph->fBounds.width();
798 int height = glyph->fBounds.height();
799
800 if (useDistanceFields) {
801 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
802 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
803 u1 = u0 + width - 2 * SK_DistanceFieldInset;
804 v1 = v0 + height - 2 * SK_DistanceFieldInset;
805 } else {
806 u0 = glyph->fAtlasLocation.fX;
807 v0 = glyph->fAtlasLocation.fY;
808 u1 = u0 + width;
809 v1 = v0 + height;
810 }
811 // We pack the 2bit page index in the low bit of the u and v texture coords
812 uint32_t pageIndex = glyph->pageIndex();
813 SkASSERT(pageIndex < 4);
814 uint16_t uBit = (pageIndex >> 1) & 0x1;
815 uint16_t vBit = pageIndex & 0x1;
816 u0 <<= 1;
817 u0 |= uBit;
818 v0 <<= 1;
819 v0 |= vBit;
820 u1 <<= 1;
821 u1 |= uBit;
822 v1 <<= 1;
823 v1 |= vBit;
824
825 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
826 textureCoords[0] = u0;
827 textureCoords[1] = v0;
828 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
829 textureCoords[0] = u0;
830 textureCoords[1] = v1;
831 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
832 textureCoords[0] = u1;
833 textureCoords[1] = v0;
834 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
835 textureCoords[0] = u1;
836 textureCoords[1] = v1;
837
838#ifdef DISPLAY_PAGE_INDEX
839 // Enable this to visualize the page from which each glyph is being drawn.
840 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
841 GrColor hackColor;
842 switch (pageIndex) {
843 case 0:
844 hackColor = GrColorPackRGBA(0, 255, 0, 255);
845 break;
846 case 1:
847 hackColor = GrColorPackRGBA(255, 0, 0, 255);;
848 break;
849 case 2:
850 hackColor = GrColorPackRGBA(255, 0, 255, 255);
851 break;
852 case 3:
853 hackColor = GrColorPackRGBA(0, 255, 255, 255);
854 break;
855 default:
856 hackColor = GrColorPackRGBA(0, 0, 0, 255);
857 break;
858 }
859 regen_colors(vertex, vertexStride, hackColor);
860#endif
861}
862
863GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
864 GrTextBlob* blob,
865 GrTextBlob::SubRun* subRun,
866 const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
867 GrColor color,
868 GrDeferredUploadTarget* uploadTarget,
869 GrStrikeCache* grStrikeCache,
870 GrAtlasManager* fullAtlasManager)
871 : fResourceProvider(resourceProvider)
872 , fViewMatrix(viewMatrix)
873 , fBlob(blob)
874 , fUploadTarget(uploadTarget)
875 , fGrStrikeCache(grStrikeCache)
876 , fFullAtlasManager(fullAtlasManager)
877 , fSubRun(subRun)
878 , fColor(color) {
879 // Compute translation if any
880 fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
881
882 // Because the GrStrikeCache may evict the strike a blob depends on using for
883 // generating its texture coords, we have to track whether or not the strike has
884 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
885 // otherwise we have to get the new strike, and use that to get the correct glyphs.
886 // Because we do not have the packed ids, and thus can't look up our glyphs in the
887 // new strike, we instead keep our ref to the old strike and use the packed ids from
888 // it. These ids will still be valid as long as we hold the ref. When we are done
889 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
890 if (fSubRun->strike()->isAbandoned()) {
891 fRegenFlags |= kRegenGlyph;
892 fRegenFlags |= kRegenTex;
893 }
894 if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
895 fRegenFlags |= kRegenCol;
896 }
897 if (0.f != fTransX || 0.f != fTransY) {
898 fRegenFlags |= kRegenPos;
899 }
900}
901
902bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
903 bool regenPos, bool regenCol, bool regenTexCoords,
904 bool regenGlyphs) {
905 SkASSERT(!regenGlyphs || regenTexCoords);
906 if (regenTexCoords) {
907 fSubRun->resetBulkUseToken();
908
909 const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
910
911 if (!fMetricsAndImages.isValid()
912 || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
913 fMetricsAndImages.init(strikeSpec);
914 }
915
916 if (regenGlyphs) {
917 // Take the glyphs from the old strike, and translate them a new strike.
918 sk_sp<GrTextStrike> newStrike = strikeSpec.findOrCreateGrStrike(fGrStrikeCache);
919
920 // Start this batch at the start of the subRun plus any glyphs that were previously
921 // processed.
922 size_t glyphStart = fSubRun->glyphStartIndex() + fCurrGlyph;
923 SkSpan<GrGlyph*> glyphs{&(fBlob->fGlyphs[glyphStart]),
924 fSubRun->glyphCount() - fCurrGlyph};
925
926 // Convert old glyphs to newStrike.
927 for (auto& glyph : glyphs) {
928 SkPackedGlyphID id = glyph->fPackedID;
929 glyph = newStrike->getGlyph(id, fMetricsAndImages.get());
930 SkASSERT(id == glyph->fPackedID);
931 }
932
933 fSubRun->setStrike(newStrike);
934 }
935 }
936
937 sk_sp<GrTextStrike> grStrike = fSubRun->refStrike();
938 bool hasW = fSubRun->hasW();
939 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
940 char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
941 fCurrGlyph * kVerticesPerGlyph * vertexStride;
942 result->fFirstVertex = currVertex;
943
944 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
945 GrGlyph* glyph = nullptr;
946 if (regenTexCoords) {
947 size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
948 glyph = fBlob->fGlyphs[glyphOffset];
949 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
950
951 if (!fFullAtlasManager->hasGlyph(glyph)) {
952 GrDrawOpAtlas::ErrorCode code;
953 code = grStrike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGrStrikeCache,
954 fFullAtlasManager, glyph,
955 fMetricsAndImages.get(), fSubRun->maskFormat(),
956 fSubRun->needsTransform());
957 if (GrDrawOpAtlas::ErrorCode::kError == code) {
958 // Something horrible has happened - drop the op
959 return false;
960 }
961 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
962 fBrokenRun = glyphIdx > 0;
963 result->fFinished = false;
964 return true;
965 }
966 }
967 auto tokenTracker = fUploadTarget->tokenTracker();
968 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
969 tokenTracker->nextDrawToken());
970 }
971
972 if (regenPos) {
973 regen_positions(currVertex, vertexStride, fTransX, fTransY);
974 }
975 if (regenCol) {
976 regen_colors(currVertex, vertexStride, fColor);
977 }
978 if (regenTexCoords) {
979 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
980 }
981
982 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
983 ++result->fGlyphsRegenerated;
984 ++fCurrGlyph;
985 }
986
987 // We may have changed the color so update it here
988 fSubRun->setColor(fColor);
989 if (regenTexCoords) {
990 fSubRun->setAtlasGeneration(fBrokenRun
991 ? GrDrawOpAtlas::kInvalidAtlasGeneration
992 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
993 } else {
994 // For the non-texCoords case we need to ensure that we update the associated use tokens
995 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
996 fUploadTarget->tokenTracker()->nextDrawToken(),
997 fSubRun->maskFormat());
998 }
999 return true;
1000}
1001
1002bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
1003 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
1004 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
1005 // this each time.
1006 if (fSubRun->atlasGeneration() != currentAtlasGen) {
1007 fRegenFlags |= kRegenTex;
1008 }
1009
1010 if (fRegenFlags) {
1011 return this->doRegen(result,
1012 fRegenFlags & kRegenPos,
1013 fRegenFlags & kRegenCol,
1014 fRegenFlags & kRegenTex,
1015 fRegenFlags & kRegenGlyph);
1016 } else {
1017 bool hasW = fSubRun->hasW();
1018 auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
1019 result->fFinished = true;
1020 result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
1021 result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
1022 fCurrGlyph * kVerticesPerGlyph * vertexStride;
1023 fCurrGlyph = fSubRun->glyphCount();
1024
1025 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1026 // have a valid atlas generation
1027 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
1028 fUploadTarget->tokenTracker()->nextDrawToken(),
1029 fSubRun->maskFormat());
1030 return true;
1031 }
1032 SK_ABORT("Should not get here");
1033}
1034
1035
1036
1037