blob: fab6cb0481b4c3f530b8ff3412bf4674a9b06067 [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"
Herb Derby91fd46a2019-12-26 11:36:30 -050010#include "include/private/SkTemplates.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkMaskFilterBase.h"
12#include "src/core/SkPaintPriv.h"
13#include "src/gpu/GrBlurUtils.h"
14#include "src/gpu/GrClip.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/gpu/GrStyle.h"
Michael Ludwig663afe52019-06-03 16:46:19 -040016#include "src/gpu/geometry/GrShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/ops/GrAtlasTextOp.h"
Herb Derbya9047642019-12-06 12:12:11 -050018#include "src/gpu/text/GrAtlasManager.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/text/GrTextBlob.h"
20#include "src/gpu/text/GrTextTarget.h"
Ben Wagner75d6db72018-09-20 14:39:39 -040021
Herb Derby3d3150c2019-12-23 15:26:44 -050022#include <cstddef>
Mike Klein79aea6a2018-06-11 10:45:26 -040023#include <new>
joshualitt2e2202e2015-12-10 11:22:08 -080024
Herb Derbya9047642019-12-06 12:12:11 -050025static SkMatrix make_inverse(const SkMatrix& matrix) {
26 SkMatrix inverseMatrix;
27 if (!matrix.invert(&inverseMatrix)) {
28 inverseMatrix = SkMatrix::I();
29 }
30 return inverseMatrix;
31}
32
33// -- GrTextBlob::Key ------------------------------------------------------------------------------
34GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); }
35
36bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const {
37 return 0 == memcmp(this, &other, sizeof(Key));
38}
39
40// -- GrTextBlob::PathGlyph ------------------------------------------------------------------------
41GrTextBlob::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
42 : fPath(path)
43 , fOrigin(origin) {}
44
45// -- GrTextBlob::SubRun ---------------------------------------------------------------------------
Herb Derbye3c7ff42019-12-10 17:49:28 -050046// Hold data to draw the different types of sub run. SubRuns are produced knowing all the
47// glyphs that are included in them.
48class GrTextBlob::SubRun {
49public:
50 // SubRun for masks
51 SubRun(SubRunType type,
52 GrTextBlob* textBlob,
53 const SkStrikeSpec& strikeSpec,
54 GrMaskFormat format,
Herb Derbyc514e7d2019-12-11 17:00:31 -050055 const SkSpan<GrGlyph*>& glyphs, const SkSpan<char>& vertexData,
Herb Derbye3c7ff42019-12-10 17:49:28 -050056 sk_sp<GrTextStrike>&& grStrike);
57
58 // SubRun for paths
59 SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec);
60
61 void appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables);
62
63 // TODO when this object is more internal, drop the privacy
64 void resetBulkUseToken();
65 GrDrawOpAtlas::BulkUseTokenUpdater* bulkUseToken();
66 void setStrike(sk_sp<GrTextStrike> strike);
67 GrTextStrike* strike() const;
Herb Derbye3c7ff42019-12-10 17:49:28 -050068
69 void setAtlasGeneration(uint64_t atlasGeneration);
70 uint64_t atlasGeneration() const;
71
Herb Derbye3c7ff42019-12-10 17:49:28 -050072 GrMaskFormat maskFormat() const;
73
Herb Derbya2d72252019-12-23 15:02:33 -050074 size_t vertexStride() const;
Herb Derby3d3150c2019-12-23 15:26:44 -050075 size_t colorOffset() const;
Herb Derby91fd46a2019-12-26 11:36:30 -050076 char* quadStart(size_t index) const;
Herb Derbya2d72252019-12-23 15:02:33 -050077
Herb Derbye3c7ff42019-12-10 17:49:28 -050078 const SkRect& vertexBounds() const;
79 void joinGlyphBounds(const SkRect& glyphBounds);
80
Herb Derbye3c7ff42019-12-10 17:49:28 -050081 bool drawAsDistanceFields() const;
82 bool drawAsPaths() const;
83 bool needsTransform() const;
Herb Derbye3c7ff42019-12-10 17:49:28 -050084
Herb Derbydf2c1ee2019-12-26 17:54:41 -050085 void translateVerticesIfNeeded(const SkMatrix& drawMatrix, SkPoint drawOrigin);
86 void updateVerticesColorIfNeeded(GrColor newColor);
Herb Derby91fd46a2019-12-26 11:36:30 -050087
Herb Derbye3c7ff42019-12-10 17:49:28 -050088 // df properties
89 void setUseLCDText(bool useLCDText);
90 bool hasUseLCDText() const;
91 void setAntiAliased(bool antiAliased);
92 bool isAntiAliased() const;
93
94 const SkStrikeSpec& strikeSpec() const;
95
96 SubRun* fNextSubRun{nullptr};
97 const SubRunType fType;
98 GrTextBlob* const fBlob;
99 const GrMaskFormat fMaskFormat;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500100 const SkSpan<GrGlyph*> fGlyphs;
101 const SkSpan<char> fVertexData;
Herb Derbye3c7ff42019-12-10 17:49:28 -0500102 const SkStrikeSpec fStrikeSpec;
103 sk_sp<GrTextStrike> fStrike;
104 struct {
105 bool useLCDText:1;
106 bool antiAliased:1;
107 } fFlags{false, false};
Herb Derbye3c7ff42019-12-10 17:49:28 -0500108 GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
109 SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
110 uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500111 GrColor fCurrentColor;
Herb Derby5bf5b042019-12-12 16:37:03 -0500112 SkPoint fCurrentOrigin;
Herb Derby1c5be7b2019-12-13 12:03:06 -0500113 SkMatrix fCurrentMatrix;
Herb Derbye3c7ff42019-12-10 17:49:28 -0500114 std::vector<PathGlyph> fPaths;
Herb Derby05eb83b2019-12-23 15:42:47 -0500115
116private:
117 bool hasW() const;
118
Herb Derbye3c7ff42019-12-10 17:49:28 -0500119}; // SubRun
120
Herb Derbya9047642019-12-06 12:12:11 -0500121GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
Herb Derbyc514e7d2019-12-11 17:00:31 -0500122 GrMaskFormat format,
123 const SkSpan<GrGlyph*>& glyphs, const SkSpan<char>& vertexData,
Herb Derbya9047642019-12-06 12:12:11 -0500124 sk_sp<GrTextStrike>&& grStrike)
125 : fType{type}
126 , fBlob{textBlob}
127 , fMaskFormat{format}
Herb Derbyc514e7d2019-12-11 17:00:31 -0500128 , fGlyphs{glyphs}
129 , fVertexData{vertexData}
Herb Derbya9047642019-12-06 12:12:11 -0500130 , fStrikeSpec{strikeSpec}
131 , fStrike{grStrike}
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500132 , fCurrentColor{textBlob->fColor}
Herb Derby5bf5b042019-12-12 16:37:03 -0500133 , fCurrentOrigin{textBlob->fInitialOrigin}
Herb Derby1c5be7b2019-12-13 12:03:06 -0500134 , fCurrentMatrix{textBlob->fInitialMatrix} {
Herb Derbya9047642019-12-06 12:12:11 -0500135 SkASSERT(type != kTransformedPath);
Herb Derbycb718892019-12-07 00:07:42 -0500136 textBlob->insertSubRun(this);
Herb Derbya9047642019-12-06 12:12:11 -0500137}
138
139GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
140 : fType{kTransformedPath}
141 , fBlob{textBlob}
142 , fMaskFormat{kA8_GrMaskFormat}
Herb Derbyc514e7d2019-12-11 17:00:31 -0500143 , fGlyphs{SkSpan<GrGlyph*>{}}
144 , fVertexData{SkSpan<char>{}}
Herb Derbya9047642019-12-06 12:12:11 -0500145 , fStrikeSpec{strikeSpec}
146 , fStrike{nullptr}
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500147 , fCurrentColor{textBlob->fColor}
Herb Derbycb718892019-12-07 00:07:42 -0500148 , fPaths{} {
149 textBlob->insertSubRun(this);
150}
Herb Derbya9047642019-12-06 12:12:11 -0500151
152void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
153 GrTextStrike* grStrike = fStrike.get();
154 SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
Herb Derbyc514e7d2019-12-11 17:00:31 -0500155 GrGlyph** glyphCursor = fGlyphs.data();
156 char* vertexCursor = fVertexData.data();
Herb Derbya2d72252019-12-23 15:02:33 -0500157 size_t vertexStride = this->vertexStride();
Herb Derbya9047642019-12-06 12:12:11 -0500158 // We always write the third position component used by SDFs. If it is unused it gets
159 // overwritten. Similarly, we always write the color and the blob will later overwrite it
160 // with texture coords if it is unused.
Herb Derby3d3150c2019-12-23 15:26:44 -0500161 size_t colorOffset = this->colorOffset();
Herb Derbya9047642019-12-06 12:12:11 -0500162 for (auto [variant, pos] : drawables) {
163 SkGlyph* skGlyph = variant;
164 GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
165 // Only floor the device coordinates.
166 SkRect dstRect;
167 if (!this->needsTransform()) {
168 pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
169 dstRect = grGlyph->destRect(pos);
170 } else {
171 dstRect = grGlyph->destRect(pos, strikeToSource);
172 }
173
174 this->joinGlyphBounds(dstRect);
175
Herb Derbya9047642019-12-06 12:12:11 -0500176 // V0
Herb Derbyc514e7d2019-12-11 17:00:31 -0500177 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fTop, 1.f};
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500178 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500179 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500180
181 // V1
Herb Derbyc514e7d2019-12-11 17:00:31 -0500182 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fBottom, 1.f};
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500183 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500184 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500185
186 // V2
Herb Derbyc514e7d2019-12-11 17:00:31 -0500187 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fTop, 1.f};
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500188 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500189 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500190
191 // V3
Herb Derbyc514e7d2019-12-11 17:00:31 -0500192 *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fBottom, 1.f};
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500193 *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500194 vertexCursor += vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500195
Herb Derbyc514e7d2019-12-11 17:00:31 -0500196 *glyphCursor++ = grGlyph;
Herb Derbya9047642019-12-06 12:12:11 -0500197 }
Herb Derbya9047642019-12-06 12:12:11 -0500198}
199
200void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
201
202GrDrawOpAtlas::BulkUseTokenUpdater* GrTextBlob::SubRun::bulkUseToken() { return &fBulkUseToken; }
203void GrTextBlob::SubRun::setStrike(sk_sp<GrTextStrike> strike) { fStrike = std::move(strike); }
204GrTextStrike* GrTextBlob::SubRun::strike() const { return fStrike.get(); }
Herb Derbya9047642019-12-06 12:12:11 -0500205void GrTextBlob::SubRun::setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
206uint64_t GrTextBlob::SubRun::atlasGeneration() const { return fAtlasGeneration; }
Herb Derbya9047642019-12-06 12:12:11 -0500207GrMaskFormat GrTextBlob::SubRun::maskFormat() const { return fMaskFormat; }
Herb Derbya2d72252019-12-23 15:02:33 -0500208size_t GrTextBlob::SubRun::vertexStride() const {
209 return GetVertexStride(this->maskFormat(), this->hasW());
210}
Herb Derby3d3150c2019-12-23 15:26:44 -0500211size_t GrTextBlob::SubRun::colorOffset() const {
212 return this->hasW() ? offsetof(SDFT3DVertex, color) : offsetof(Mask2DVertex, color);
213}
Herb Derby91fd46a2019-12-26 11:36:30 -0500214char* GrTextBlob::SubRun::quadStart(size_t index) const {
215 return SkTAddOffset<char>(
216 fVertexData.data(), index * kVerticesPerGlyph * this->vertexStride());
217}
Herb Derbya2d72252019-12-23 15:02:33 -0500218
Herb Derbya9047642019-12-06 12:12:11 -0500219const SkRect& GrTextBlob::SubRun::vertexBounds() const { return fVertexBounds; }
220void GrTextBlob::SubRun::joinGlyphBounds(const SkRect& glyphBounds) {
221 fVertexBounds.joinNonEmptyArg(glyphBounds);
222}
223
Herb Derbya9047642019-12-06 12:12:11 -0500224bool GrTextBlob::SubRun::drawAsDistanceFields() const { return fType == kTransformedSDFT; }
225
226bool GrTextBlob::SubRun::drawAsPaths() const { return fType == kTransformedPath; }
227
228bool GrTextBlob::SubRun::needsTransform() const {
229 return fType == kTransformedPath
230 || fType == kTransformedMask
231 || fType == kTransformedSDFT;
232}
233
234bool GrTextBlob::SubRun::hasW() const {
235 return fBlob->hasW(fType);
236}
237
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500238void GrTextBlob::SubRun::translateVerticesIfNeeded(
239 const SkMatrix& drawMatrix, SkPoint drawOrigin) {
240 SkVector translation;
241 if (this->needsTransform()) {
242 // If transform is needed, then the vertices are in source space, calculate the source
243 // space translation.
244 translation = drawOrigin - fCurrentOrigin;
245 } else {
246 // Calculate the translation in source space to a translation in device space. Calculate
247 // the translation by mapping (0, 0) through both the current matrix, and the draw
248 // matrix, and taking the difference.
249 SkMatrix currentMatrix{fCurrentMatrix};
250 currentMatrix.preTranslate(fCurrentOrigin.x(), fCurrentOrigin.y());
251 SkPoint currentDeviceOrigin{0, 0};
252 currentMatrix.mapPoints(&currentDeviceOrigin, 1);
253 SkMatrix completeDrawMatrix{drawMatrix};
254 completeDrawMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
255 SkPoint drawDeviceOrigin{0, 0};
256 completeDrawMatrix.mapPoints(&drawDeviceOrigin, 1);
257 translation = drawDeviceOrigin - currentDeviceOrigin;
258 }
259
260 if (translation != SkPoint{0, 0}) {
261 size_t vertexStride = this->vertexStride();
262 for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
263 SkPoint* vertexCursor = reinterpret_cast<SkPoint*>(quadStart(quad));
264 for (int i = 0; i < 4; ++i) {
265 *vertexCursor += translation;
266 vertexCursor = SkTAddOffset<SkPoint>(vertexCursor, vertexStride);
267 }
Herb Derby91fd46a2019-12-26 11:36:30 -0500268 }
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500269 fCurrentMatrix = drawMatrix;
270 fCurrentOrigin = drawOrigin;
Herb Derby91fd46a2019-12-26 11:36:30 -0500271 }
272}
273
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500274void GrTextBlob::SubRun::updateVerticesColorIfNeeded(GrColor newColor) {
275 if (this->maskFormat() != kARGB_GrMaskFormat && fCurrentColor != newColor) {
276 size_t vertexStride = this->vertexStride();
277 size_t colorOffset = this->colorOffset();
278 for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
279 GrColor* colorCursor = SkTAddOffset<GrColor>(quadStart(quad), colorOffset);
280 for (int i = 0; i < 4; ++i) {
281 *colorCursor = newColor;
282 colorCursor = SkTAddOffset<GrColor>(colorCursor, vertexStride);
283 }
Herb Derby6ca4f312019-12-26 15:23:49 -0500284 }
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500285 this->fCurrentColor = newColor;
Herb Derby6ca4f312019-12-26 15:23:49 -0500286 }
Herb Derby6ca4f312019-12-26 15:23:49 -0500287}
288
Herb Derbya9047642019-12-06 12:12:11 -0500289void GrTextBlob::SubRun::setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
290bool GrTextBlob::SubRun::hasUseLCDText() const { return fFlags.useLCDText; }
291void GrTextBlob::SubRun::setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
292bool GrTextBlob::SubRun::isAntiAliased() const { return fFlags.antiAliased; }
293const SkStrikeSpec& GrTextBlob::SubRun::strikeSpec() const { return fStrikeSpec; }
294
295// -- GrTextBlob -----------------------------------------------------------------------------------
296void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
297void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
298void* GrTextBlob::operator new(size_t, void* p) { return p; }
299
300GrTextBlob::~GrTextBlob() = default;
301
Herb Derby659e4092019-12-06 15:38:10 -0500302sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
Herb Derbyeba195f2019-12-03 16:44:47 -0500303 GrStrikeCache* strikeCache,
Herb Derby1c5be7b2019-12-13 12:03:06 -0500304 const SkMatrix& drawMatrix,
Herb Derbya00da612019-03-04 17:10:01 -0500305 GrColor color,
Herb Derbyeba195f2019-12-03 16:44:47 -0500306 bool forceWForDistanceFields) {
Herb Derbycd498e12019-12-06 14:17:31 -0500307
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500308 static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
Herb Derbycb718892019-12-07 00:07:42 -0500309 static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex));
310 size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph;
Herb Derby1c5be7b2019-12-13 12:03:06 -0500311 if (drawMatrix.hasPerspective() || forceWForDistanceFields) {
Herb Derby3d1a3bb2019-12-06 18:15:49 -0500312 static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex));
Herb Derbycb718892019-12-07 00:07:42 -0500313 static_assert(alignof(ARGB3DVertex) <= alignof(SDFT3DVertex));
314 quadSize = sizeof(SDFT3DVertex) * kVerticesPerGlyph;
Herb Derbye74c7762019-12-04 14:15:41 -0500315 }
316
Herb Derbycb718892019-12-07 00:07:42 -0500317 // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments.
318 static_assert(alignof(SDFT3DVertex) >= alignof(Mask2DVertex));
Herb Derbyc514e7d2019-12-11 17:00:31 -0500319 // Assume there is no padding needed between glyph pointers and vertices.
Herb Derbycb718892019-12-07 00:07:42 -0500320 static_assert(alignof(GrGlyph*) >= alignof(SDFT3DVertex));
Herb Derbyc514e7d2019-12-11 17:00:31 -0500321
322 // In the arena, the layout is GrGlyph*... | SDFT3DVertex... | SubRun, so there is no padding
323 // between GrGlyph* and SDFT3DVertex, but padding is needed between the Mask2DVertex array
324 // and the SubRun.
325 size_t vertexToSubRunPadding = alignof(SDFT3DVertex) - alignof(SubRun);
326 size_t arenaSize =
327 sizeof(GrGlyph*) * glyphRunList.totalGlyphCount()
328 + quadSize * glyphRunList.totalGlyphCount()
329 + glyphRunList.runCount() * (sizeof(SubRun) + vertexToSubRunPadding);
330
331 size_t allocationSize = sizeof(GrTextBlob) + arenaSize;
Ben Wagner75d6db72018-09-20 14:39:39 -0400332
Herb Derbycb718892019-12-07 00:07:42 -0500333 void* allocation = ::operator new (allocationSize);
Herb Derbyb12175f2018-05-23 16:38:09 -0400334
Herb Derbyaebc5f82019-12-10 14:07:10 -0500335 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(glyphRunList.paint());
Herb Derby00ae9592019-12-03 15:55:56 -0500336 sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
Herb Derby1c5be7b2019-12-13 12:03:06 -0500337 arenaSize, strikeCache, drawMatrix, glyphRunList.origin(),
Herb Derbyaebc5f82019-12-10 14:07:10 -0500338 color, initialLuminance, forceWForDistanceFields}};
joshualitt92303772016-02-10 11:55:52 -0800339
Herb Derbyf7d5d742018-11-16 13:24:32 -0500340 return blob;
joshualitt92303772016-02-10 11:55:52 -0800341}
342
Herb Derbya9047642019-12-06 12:12:11 -0500343void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::BlurRec& blurRec,
344 const SkPaint& paint) {
345 fKey = key;
346 if (key.fHasBlur) {
347 fBlurRec = blurRec;
348 }
349 if (key.fStyle != SkPaint::kFill_Style) {
350 fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
351 fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
352 fStrokeInfo.fJoin = paint.getStrokeJoin();
353 }
354}
355const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
356uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
357
Herb Derbycb718892019-12-07 00:07:42 -0500358bool GrTextBlob::hasDistanceField() const {
359 return SkToBool(fTextType & kHasDistanceField_TextType);
360}
Herb Derbya9047642019-12-06 12:12:11 -0500361bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
Herb Derby1c5be7b2019-12-13 12:03:06 -0500362bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
Herb Derbya9047642019-12-06 12:12:11 -0500363
364void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
365void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; }
366void GrTextBlob::setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
367 // we init fMaxMinScale and fMinMaxScale in the constructor
368 fMaxMinScale = SkMaxScalar(scaledMin, fMaxMinScale);
369 fMinMaxScale = SkMinScalar(scaledMax, fMinMaxScale);
370}
371
372size_t GrTextBlob::GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
373 switch (maskFormat) {
374 case kA8_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500375 return hasWCoord ? sizeof(SDFT3DVertex) : sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500376 case kARGB_GrMaskFormat:
Herb Derbycd498e12019-12-06 14:17:31 -0500377 return hasWCoord ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500378 default:
379 SkASSERT(!hasWCoord);
Herb Derbycd498e12019-12-06 14:17:31 -0500380 return sizeof(Mask2DVertex);
Herb Derbya9047642019-12-06 12:12:11 -0500381 }
382}
383
Herb Derby0edb2142018-10-16 17:04:11 -0400384bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
385 const SkMaskFilterBase::BlurRec& blurRec,
Herb Derby5bf5b042019-12-12 16:37:03 -0500386 const SkMatrix& drawMatrix, SkPoint drawOrigin) {
joshualittfd5f6c12015-12-10 07:44:50 -0800387 // If we have LCD text then our canonical color will be set to transparent, in this case we have
388 // to regenerate the blob on any color change
389 // We use the grPaint to get any color filter effects
390 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
Herb Derbyaebc5f82019-12-10 14:07:10 -0500391 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800392 return true;
393 }
394
Herb Derby1c5be7b2019-12-13 12:03:06 -0500395 if (fInitialMatrix.hasPerspective() != drawMatrix.hasPerspective()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800396 return true;
397 }
398
Brian Salomon5c6ac642017-12-19 11:09:32 -0500399 /** This could be relaxed for blobs with only distance field glyphs. */
Mike Reed2c383152019-12-18 16:47:47 -0500400 if (fInitialMatrix.hasPerspective() && !SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800401 return true;
402 }
403
404 // We only cache one masked version
405 if (fKey.fHasBlur &&
Mike Reed1be1f8d2018-03-14 13:01:17 -0400406 (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
joshualittfd5f6c12015-12-10 07:44:50 -0800407 return true;
408 }
409
410 // Similarly, we only cache one version for each style
411 if (fKey.fStyle != SkPaint::kFill_Style &&
Herb Derbybc6f9c92018-08-08 13:58:45 -0400412 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
413 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
414 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
joshualittfd5f6c12015-12-10 07:44:50 -0800415 return true;
416 }
417
418 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
419 // for mixed blobs if this becomes an issue.
420 if (this->hasBitmap() && this->hasDistanceField()) {
Herb Derbyeba195f2019-12-03 16:44:47 -0500421 // Identical view matrices and we can reuse in all cases
Mike Reed2c383152019-12-18 16:47:47 -0500422 return !(SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix) &&
423 drawOrigin == fInitialOrigin);
joshualittfd5f6c12015-12-10 07:44:50 -0800424 }
425
426 if (this->hasBitmap()) {
Herb Derby1c5be7b2019-12-13 12:03:06 -0500427 if (fInitialMatrix.getScaleX() != drawMatrix.getScaleX() ||
428 fInitialMatrix.getScaleY() != drawMatrix.getScaleY() ||
429 fInitialMatrix.getSkewX() != drawMatrix.getSkewX() ||
430 fInitialMatrix.getSkewY() != drawMatrix.getSkewY()) {
joshualittfd5f6c12015-12-10 07:44:50 -0800431 return true;
432 }
433
Herb Derby9830fc42019-09-12 10:58:26 -0400434 // TODO(herb): this is not needed for full pixel glyph choice, but is needed to adjust
435 // the quads properly. Devise a system that regenerates the quads from original data
436 // using the transform to allow this to be used in general.
437
438 // We can update the positions in the text blob without regenerating the whole
439 // blob, but only for integer translations.
440 // This cool bit of math will determine the necessary translation to apply to the
441 // already generated vertex coordinates to move them to the correct position.
442 // Figure out the translation in view space given a translation in source space.
Herb Derby1c5be7b2019-12-13 12:03:06 -0500443 SkScalar transX = drawMatrix.getTranslateX() +
Herb Derby5bf5b042019-12-12 16:37:03 -0500444 drawMatrix.getScaleX() * (drawOrigin.x() - fInitialOrigin.x()) +
445 drawMatrix.getSkewX() * (drawOrigin.y() - fInitialOrigin.y()) -
Herb Derby1c5be7b2019-12-13 12:03:06 -0500446 fInitialMatrix.getTranslateX();
447 SkScalar transY = drawMatrix.getTranslateY() +
Herb Derby5bf5b042019-12-12 16:37:03 -0500448 drawMatrix.getSkewY() * (drawOrigin.x() - fInitialOrigin.x()) +
449 drawMatrix.getScaleY() * (drawOrigin.y() - fInitialOrigin.y()) -
Herb Derby1c5be7b2019-12-13 12:03:06 -0500450 fInitialMatrix.getTranslateY();
Herb Derby9830fc42019-09-12 10:58:26 -0400451 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
452 return true;
joshualittfd5f6c12015-12-10 07:44:50 -0800453 }
joshualittfd5f6c12015-12-10 07:44:50 -0800454 } else if (this->hasDistanceField()) {
455 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
456 // distance field being generated, so we have to regenerate in those cases
Herb Derby1c5be7b2019-12-13 12:03:06 -0500457 SkScalar newMaxScale = drawMatrix.getMaxScale();
458 SkScalar oldMaxScale = fInitialMatrix.getMaxScale();
joshualittfd5f6c12015-12-10 07:44:50 -0800459 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
460 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
461 return true;
462 }
joshualittfd5f6c12015-12-10 07:44:50 -0800463 }
464
joshualittfd5f6c12015-12-10 07:44:50 -0800465 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
466 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
467 // the blob anyways at flush time, so no need to regenerate explicitly
468 return false;
469}
470
Herb Derbyc1b482c2018-08-09 15:02:27 -0400471void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
472 const GrDistanceFieldAdjustTable* distanceAdjustTable,
Brian Osmancf860852018-10-31 14:04:39 -0400473 const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
Herb Derby5bf5b042019-12-12 16:37:03 -0500474 const SkMatrix& drawMatrix, SkPoint drawOrigin) {
Jim Van Verth54d9c882018-02-08 16:14:48 -0500475
Herb Derbycb718892019-12-07 00:07:42 -0500476 for (SubRun* subRun = fFirstSubRun; subRun != nullptr; subRun = subRun->fNextSubRun) {
477 if (subRun->drawAsPaths()) {
Herb Derbydac1ed52018-09-12 17:04:21 -0400478 SkPaint runPaint{paint};
Herb Derbycb718892019-12-07 00:07:42 -0500479 runPaint.setAntiAlias(subRun->isAntiAliased());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500480 // If there are shaders, blurs or styles, the path must be scaled into source
481 // space independently of the CTM. This allows the CTM to be correct for the
482 // different effects.
483 GrStyle style(runPaint);
Herb Derby9f491482018-08-08 11:53:00 -0400484
Herb Derby1b8dcd12019-11-15 15:21:15 -0500485 bool scalePath = runPaint.getShader()
486 || style.applies()
487 || runPaint.getMaskFilter();
Robert Phillips137ca522018-08-15 10:14:33 -0400488
Herb Derby1b8dcd12019-11-15 15:21:15 -0500489 // The origin for the blob may have changed, so figure out the delta.
Herb Derby5bf5b042019-12-12 16:37:03 -0500490 SkVector originShift = drawOrigin - fInitialOrigin;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500491
Herb Derbycb718892019-12-07 00:07:42 -0500492 for (const auto& pathGlyph : subRun->fPaths) {
Herb Derby1c5be7b2019-12-13 12:03:06 -0500493 SkMatrix ctm{drawMatrix};
Herb Derbycb718892019-12-07 00:07:42 -0500494 SkMatrix pathMatrix = SkMatrix::MakeScale(
495 subRun->fStrikeSpec.strikeToSourceRatio());
Herb Derby1b8dcd12019-11-15 15:21:15 -0500496 // Shift the original glyph location in source space to the position of the new
497 // blob.
498 pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
499 originShift.y() + pathGlyph.fOrigin.y());
Herb Derbydac1ed52018-09-12 17:04:21 -0400500
501 // TmpPath must be in the same scope as GrShape shape below.
Robert Phillips137ca522018-08-15 10:14:33 -0400502 SkTLazy<SkPath> tmpPath;
Herb Derby1b8dcd12019-11-15 15:21:15 -0500503 const SkPath* path = &pathGlyph.fPath;
Herb Derby2984d262019-11-20 14:40:39 -0500504 if (!scalePath) {
505 // Scale can be applied to CTM -- no effects.
Herb Derby2984d262019-11-20 14:40:39 -0500506 ctm.preConcat(pathMatrix);
Robert Phillipsd20d2612018-08-28 10:09:01 -0400507 } else {
Herb Derby2984d262019-11-20 14:40:39 -0500508 // Scale the outline into source space.
Herb Derbydac1ed52018-09-12 17:04:21 -0400509
Herb Derby2984d262019-11-20 14:40:39 -0500510 // Transform the path form the normalized outline to source space. This
511 // way the CTM will remain the same so it can be used by the effects.
512 SkPath* sourceOutline = tmpPath.init();
513 path->transform(pathMatrix, sourceOutline);
514 sourceOutline->setIsVolatile(true);
515 path = sourceOutline;
Robert Phillips137ca522018-08-15 10:14:33 -0400516 }
517
Robert Phillips46a13382018-08-23 13:53:01 -0400518 // TODO: we are losing the mutability of the path here
519 GrShape shape(*path, paint);
Herb Derbydac1ed52018-09-12 17:04:21 -0400520 target->drawShape(clip, runPaint, ctm, shape);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500521 }
Herb Derby1b8dcd12019-11-15 15:21:15 -0500522 } else {
Herb Derbyc514e7d2019-12-11 17:00:31 -0500523 int glyphCount = subRun->fGlyphs.size();
Jim Van Verth54d9c882018-02-08 16:14:48 -0500524 if (0 == glyphCount) {
525 continue;
526 }
527
528 bool skipClip = false;
529 bool submitOp = true;
530 SkIRect clipRect = SkIRect::MakeEmpty();
531 SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
532 SkRRect clipRRect;
533 GrAA aa;
Jim Van Verthb515ae72018-05-23 16:44:55 -0400534 // We can clip geometrically if we're not using SDFs or transformed glyphs,
Jim Van Verth54d9c882018-02-08 16:14:48 -0500535 // and we have an axis-aligned rectangular non-AA clip
Herb Derbycb718892019-12-07 00:07:42 -0500536 if (!subRun->drawAsDistanceFields() && !subRun->needsTransform() &&
Jim Van Verthcf838c72018-03-05 14:40:36 -0500537 clip.isRRect(rtBounds, &clipRRect, &aa) &&
Jim Van Verth54d9c882018-02-08 16:14:48 -0500538 clipRRect.isRect() && GrAA::kNo == aa) {
539 skipClip = true;
540 // We only need to do clipping work if the subrun isn't contained by the clip
541 SkRect subRunBounds;
Herb Derby5bf5b042019-12-12 16:37:03 -0500542 this->computeSubRunBounds(
543 &subRunBounds, *subRun, drawMatrix, drawOrigin, false);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500544 if (!clipRRect.getBounds().contains(subRunBounds)) {
545 // If the subrun is completely outside, don't add an op for it
546 if (!clipRRect.getBounds().intersects(subRunBounds)) {
547 submitOp = false;
548 }
549 else {
550 clipRRect.getBounds().round(&clipRect);
551 }
552 }
553 }
554
555 if (submitOp) {
Herb Derby5bf5b042019-12-12 16:37:03 -0500556 auto op = this->makeOp(*subRun, glyphCount, drawMatrix, drawOrigin,
Herb Derbybc6f9c92018-08-08 13:58:45 -0400557 clipRect, paint, filteredColor, props, distanceAdjustTable,
Robert Phillips5a66efb2018-03-07 15:13:18 -0500558 target);
Jim Van Verth54d9c882018-02-08 16:14:48 -0500559 if (op) {
560 if (skipClip) {
561 target->addDrawOp(GrNoClip(), std::move(op));
562 }
563 else {
564 target->addDrawOp(clip, std::move(op));
565 }
566 }
567 }
568 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000569 }
Jim Van Verth89737de2018-02-06 21:30:20 +0000570}
571
Herb Derby1c5be7b2019-12-13 12:03:06 -0500572void GrTextBlob::computeSubRunBounds(SkRect* outBounds, const SubRun& subRun,
Herb Derby5bf5b042019-12-12 16:37:03 -0500573 const SkMatrix& drawMatrix, SkPoint drawOrigin,
Herb Derbya9047642019-12-06 12:12:11 -0500574 bool needsGlyphTransform) {
575 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
576 // into device space.
577 // We handle vertex bounds differently for distance field text and bitmap text because
578 // the vertex bounds of bitmap text are in device space. If we are flushing multiple runs
579 // from one blob then we are going to pay the price here of mapping the rect for each run.
580 *outBounds = subRun.vertexBounds();
581 if (needsGlyphTransform) {
582 // Distance field text is positioned with the (X,Y) as part of the glyph position,
583 // and currently the view matrix is applied on the GPU
Herb Derby5bf5b042019-12-12 16:37:03 -0500584 outBounds->offset(drawOrigin - fInitialOrigin);
Herb Derby1c5be7b2019-12-13 12:03:06 -0500585 drawMatrix.mapRect(outBounds);
Herb Derbya9047642019-12-06 12:12:11 -0500586 } else {
587 // Bitmap text is fully positioned on the CPU, and offset by an (X,Y) translate in
588 // device space.
Herb Derby1c5be7b2019-12-13 12:03:06 -0500589 SkMatrix boundsMatrix = fInitialMatrixInverse;
Herb Derbya9047642019-12-06 12:12:11 -0500590
591 boundsMatrix.postTranslate(-fInitialOrigin.x(), -fInitialOrigin.y());
592
Herb Derby5bf5b042019-12-12 16:37:03 -0500593 boundsMatrix.postTranslate(drawOrigin.x(), drawOrigin.y());
Herb Derbya9047642019-12-06 12:12:11 -0500594
Herb Derby1c5be7b2019-12-13 12:03:06 -0500595 boundsMatrix.postConcat(drawMatrix);
Herb Derbya9047642019-12-06 12:12:11 -0500596 boundsMatrix.mapRect(outBounds);
597
598 // Due to floating point numerical inaccuracies, we have to round out here
599 outBounds->roundOut(outBounds);
600 }
joshualitt323c2eb2016-01-20 06:48:47 -0800601}
joshualitt2e2202e2015-12-10 11:22:08 -0800602
Herb Derbya9047642019-12-06 12:12:11 -0500603const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
604size_t GrTextBlob::size() const { return fSize; }
605
606std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
Herb Derby1c5be7b2019-12-13 12:03:06 -0500607 int glyphCount, const SkMatrix& drawMatrix,
Herb Derby5bf5b042019-12-12 16:37:03 -0500608 SkPoint drawOrigin, const SkPaint& paint, const SkPMColor4f& filteredColor,
Herb Derbya9047642019-12-06 12:12:11 -0500609 const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
610 GrTextTarget* target) {
Herb Derbycb718892019-12-07 00:07:42 -0500611 SubRun* info = fFirstSubRun;
Herb Derbya9047642019-12-06 12:12:11 -0500612 SkIRect emptyRect = SkIRect::MakeEmpty();
Herb Derby5bf5b042019-12-12 16:37:03 -0500613 return this->makeOp(*info, glyphCount, drawMatrix, drawOrigin, emptyRect,
Herb Derbya9047642019-12-06 12:12:11 -0500614 paint, filteredColor, props, distanceAdjustTable, target);
615}
616
617bool GrTextBlob::hasW(GrTextBlob::SubRunType type) const {
618 if (type == kTransformedSDFT) {
619 return this->hasPerspective() || fForceWForDistanceFields;
620 } else if (type == kTransformedMask || type == kTransformedPath) {
621 return this->hasPerspective();
Herb Derbye9f691d2019-12-04 12:11:13 -0500622 }
Herb Derbya9047642019-12-06 12:12:11 -0500623
624 // The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
625 // used.
626 return false;
627}
628
629GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
630 const SkZip<SkGlyphVariant, SkPoint>& drawables,
631 const SkStrikeSpec& strikeSpec,
632 GrMaskFormat format) {
Herb Derbyc514e7d2019-12-11 17:00:31 -0500633 SkSpan<GrGlyph*> glyphs{fAlloc.makeArrayDefault<GrGlyph*>(drawables.size()), drawables.size()};
Herb Derbya9047642019-12-06 12:12:11 -0500634 bool hasW = this->hasW(type);
Herb Derby3d3150c2019-12-23 15:26:44 -0500635
636 SkASSERT(!fInitialMatrix.hasPerspective() || hasW);
637
Herb Derbyc514e7d2019-12-11 17:00:31 -0500638 size_t vertexDataSize = drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
639 SkSpan<char> vertexData{fAlloc.makeArrayDefault<char>(vertexDataSize), vertexDataSize};
Herb Derbya9047642019-12-06 12:12:11 -0500640
641 sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
642
Herb Derbycb718892019-12-07 00:07:42 -0500643 SubRun* subRun = fAlloc.make<SubRun>(
Herb Derbyc514e7d2019-12-11 17:00:31 -0500644 type, this, strikeSpec, format, glyphs, vertexData, std::move(grStrike));
Herb Derbya9047642019-12-06 12:12:11 -0500645
Herb Derbycb718892019-12-07 00:07:42 -0500646 subRun->appendGlyphs(drawables);
Herb Derbya9047642019-12-06 12:12:11 -0500647
Herb Derbycb718892019-12-07 00:07:42 -0500648 return subRun;
Herb Derbya9047642019-12-06 12:12:11 -0500649}
650
651void GrTextBlob::addSingleMaskFormat(
652 SubRunType type,
653 const SkZip<SkGlyphVariant, SkPoint>& drawables,
654 const SkStrikeSpec& strikeSpec,
655 GrMaskFormat format) {
656 this->makeSubRun(type, drawables, strikeSpec, format);
657}
658
659void GrTextBlob::addMultiMaskFormat(
660 SubRunType type,
661 const SkZip<SkGlyphVariant, SkPoint>& drawables,
662 const SkStrikeSpec& strikeSpec) {
663 this->setHasBitmap();
664 if (drawables.empty()) { return; }
665
666 auto glyphSpan = drawables.get<0>();
667 SkGlyph* glyph = glyphSpan[0];
668 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
669 size_t startIndex = 0;
670 for (size_t i = 1; i < drawables.size(); i++) {
671 glyph = glyphSpan[i];
672 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
673 if (format != nextFormat) {
674 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
675 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
676 format = nextFormat;
677 startIndex = i;
678 }
679 }
680 auto sameFormat = drawables.last(drawables.size() - startIndex);
681 this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
682}
683
684void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
685 const SkStrikeSpec& strikeSpec,
686 const SkFont& runFont,
687 SkScalar minScale,
688 SkScalar maxScale) {
689 this->setHasDistanceField();
690 this->setMinAndMaxScale(minScale, maxScale);
691
692 SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
693 subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
694 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbye9f691d2019-12-04 12:11:13 -0500695}
696
Herb Derbycb718892019-12-07 00:07:42 -0500697GrTextBlob::GrTextBlob(size_t allocSize,
Herb Derbyeba195f2019-12-03 16:44:47 -0500698 GrStrikeCache* strikeCache,
Herb Derby1c5be7b2019-12-13 12:03:06 -0500699 const SkMatrix& drawMatrix,
Herb Derbyeba195f2019-12-03 16:44:47 -0500700 SkPoint origin,
701 GrColor color,
Herb Derbyaebc5f82019-12-10 14:07:10 -0500702 SkColor initialLuminance,
Herb Derby00ae9592019-12-03 15:55:56 -0500703 bool forceWForDistanceFields)
Herb Derbycb718892019-12-07 00:07:42 -0500704 : fSize{allocSize}
Herb Derby00ae9592019-12-03 15:55:56 -0500705 , fStrikeCache{strikeCache}
Herb Derby1c5be7b2019-12-13 12:03:06 -0500706 , fInitialMatrix{drawMatrix}
707 , fInitialMatrixInverse{make_inverse(drawMatrix)}
Herb Derbyeba195f2019-12-03 16:44:47 -0500708 , fInitialOrigin{origin}
Herb Derby00ae9592019-12-03 15:55:56 -0500709 , fForceWForDistanceFields{forceWForDistanceFields}
Herb Derbycb718892019-12-07 00:07:42 -0500710 , fColor{color}
Herb Derbyaebc5f82019-12-10 14:07:10 -0500711 , fInitialLuminance{initialLuminance}
Herb Derbycb718892019-12-07 00:07:42 -0500712 , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
Herb Derby00ae9592019-12-03 15:55:56 -0500713
Herb Derbycb718892019-12-07 00:07:42 -0500714void GrTextBlob::insertSubRun(SubRun* subRun) {
715 if (fFirstSubRun == nullptr) {
716 fFirstSubRun = subRun;
717 fLastSubRun = subRun;
718 } else {
719 fLastSubRun->fNextSubRun = subRun;
720 fLastSubRun = subRun;
721 }
722}
723
724std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
Herb Derbya9047642019-12-06 12:12:11 -0500725 SubRun& info, int glyphCount,
Herb Derby5bf5b042019-12-12 16:37:03 -0500726 const SkMatrix& drawMatrix, SkPoint drawOrigin, const SkIRect& clipRect,
Herb Derbya9047642019-12-06 12:12:11 -0500727 const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
728 const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
729 GrMaskFormat format = info.maskFormat();
730
731 GrPaint grPaint;
Herb Derby1c5be7b2019-12-13 12:03:06 -0500732 target->makeGrPaint(info.maskFormat(), paint, drawMatrix, &grPaint);
Herb Derbya9047642019-12-06 12:12:11 -0500733 std::unique_ptr<GrAtlasTextOp> op;
734 if (info.drawAsDistanceFields()) {
735 // TODO: Can we be even smarter based on the dest transfer function?
736 op = GrAtlasTextOp::MakeDistanceField(
737 target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable,
738 target->colorInfo().isLinearlyBlended(), SkPaintPriv::ComputeLuminanceColor(paint),
739 props, info.isAntiAliased(), info.hasUseLCDText());
Herb Derbyeba195f2019-12-03 16:44:47 -0500740 } else {
Herb Derbya9047642019-12-06 12:12:11 -0500741 op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
742 info.needsTransform());
743 }
744 GrAtlasTextOp::Geometry& geometry = op->geometry();
Herb Derby1c5be7b2019-12-13 12:03:06 -0500745 geometry.fDrawMatrix = drawMatrix;
Herb Derbya9047642019-12-06 12:12:11 -0500746 geometry.fClipRect = clipRect;
747 geometry.fBlob = SkRef(this);
748 geometry.fSubRunPtr = &info;
749 geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
Herb Derby5bf5b042019-12-12 16:37:03 -0500750 geometry.fDrawOrigin = drawOrigin;
Herb Derbya9047642019-12-06 12:12:11 -0500751 op->init();
752 return op;
753}
Herb Derbyeba195f2019-12-03 16:44:47 -0500754
Herb Derbya9047642019-12-06 12:12:11 -0500755void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
756 const SkStrikeSpec& strikeSpec) {
757 this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
758}
Herb Derbyeba195f2019-12-03 16:44:47 -0500759
Herb Derbya9047642019-12-06 12:12:11 -0500760void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
761 const SkFont& runFont,
762 const SkStrikeSpec& strikeSpec) {
763 this->setHasBitmap();
Herb Derbycb718892019-12-07 00:07:42 -0500764 SubRun* subRun = fAlloc.make<SubRun>(this, strikeSpec);
765 subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
Herb Derbya9047642019-12-06 12:12:11 -0500766 for (auto [variant, pos] : drawables) {
Herb Derbycb718892019-12-07 00:07:42 -0500767 subRun->fPaths.emplace_back(*variant.path(), pos);
Herb Derbyeba195f2019-12-03 16:44:47 -0500768 }
769}
770
Herb Derbya9047642019-12-06 12:12:11 -0500771void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
772 const SkStrikeSpec& strikeSpec,
773 const SkFont& runFont,
774 SkScalar minScale,
775 SkScalar maxScale) {
776 this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
joshualitt8e0ef292016-02-19 14:13:03 -0800777}
Herb Derbya9047642019-12-06 12:12:11 -0500778
779void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
780 const SkStrikeSpec& strikeSpec) {
781 this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
782}
783
784// -- GrTextBlob::VertexRegenerator ----------------------------------------------------------------
Herb Derbya9047642019-12-06 12:12:11 -0500785static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
786 bool useDistanceFields) {
787 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
788 // vertices, hence vertexStride - sizeof(SkIPoint16)
789 size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
790
791 uint16_t u0, v0, u1, v1;
792 SkASSERT(glyph);
793 int width = glyph->fBounds.width();
794 int height = glyph->fBounds.height();
795
796 if (useDistanceFields) {
797 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
798 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
799 u1 = u0 + width - 2 * SK_DistanceFieldInset;
800 v1 = v0 + height - 2 * SK_DistanceFieldInset;
801 } else {
802 u0 = glyph->fAtlasLocation.fX;
803 v0 = glyph->fAtlasLocation.fY;
804 u1 = u0 + width;
805 v1 = v0 + height;
806 }
807 // We pack the 2bit page index in the low bit of the u and v texture coords
808 uint32_t pageIndex = glyph->pageIndex();
809 SkASSERT(pageIndex < 4);
810 uint16_t uBit = (pageIndex >> 1) & 0x1;
811 uint16_t vBit = pageIndex & 0x1;
812 u0 <<= 1;
813 u0 |= uBit;
814 v0 <<= 1;
815 v0 |= vBit;
816 u1 <<= 1;
817 u1 |= uBit;
818 v1 <<= 1;
819 v1 |= vBit;
820
821 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
822 textureCoords[0] = u0;
823 textureCoords[1] = v0;
824 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
825 textureCoords[0] = u0;
826 textureCoords[1] = v1;
827 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
828 textureCoords[0] = u1;
829 textureCoords[1] = v0;
830 textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
831 textureCoords[0] = u1;
832 textureCoords[1] = v1;
833
834#ifdef DISPLAY_PAGE_INDEX
835 // Enable this to visualize the page from which each glyph is being drawn.
836 // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
837 GrColor hackColor;
838 switch (pageIndex) {
839 case 0:
840 hackColor = GrColorPackRGBA(0, 255, 0, 255);
841 break;
842 case 1:
843 hackColor = GrColorPackRGBA(255, 0, 0, 255);;
844 break;
845 case 2:
846 hackColor = GrColorPackRGBA(255, 0, 255, 255);
847 break;
848 case 3:
849 hackColor = GrColorPackRGBA(0, 255, 255, 255);
850 break;
851 default:
852 hackColor = GrColorPackRGBA(0, 0, 0, 255);
853 break;
854 }
855 regen_colors(vertex, vertexStride, hackColor);
856#endif
857}
858
859GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
Herb Derbya9047642019-12-06 12:12:11 -0500860 GrTextBlob::SubRun* subRun,
Herb Derby5bf5b042019-12-12 16:37:03 -0500861 const SkMatrix& drawMatrix,
862 SkPoint drawOrigin,
Herb Derbya9047642019-12-06 12:12:11 -0500863 GrColor color,
864 GrDeferredUploadTarget* uploadTarget,
865 GrStrikeCache* grStrikeCache,
866 GrAtlasManager* fullAtlasManager)
867 : fResourceProvider(resourceProvider)
Herb Derbya9047642019-12-06 12:12:11 -0500868 , fUploadTarget(uploadTarget)
869 , fGrStrikeCache(grStrikeCache)
870 , fFullAtlasManager(fullAtlasManager)
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500871 , fSubRun(subRun){
Herb Derbya9047642019-12-06 12:12:11 -0500872 // Because the GrStrikeCache may evict the strike a blob depends on using for
873 // generating its texture coords, we have to track whether or not the strike has
874 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
875 // otherwise we have to get the new strike, and use that to get the correct glyphs.
876 // Because we do not have the packed ids, and thus can't look up our glyphs in the
877 // new strike, we instead keep our ref to the old strike and use the packed ids from
878 // it. These ids will still be valid as long as we hold the ref. When we are done
879 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
Herb Derby73630212019-12-13 16:29:14 -0500880 fActions.regenTextureCoordinates = fSubRun->strike()->isAbandoned();
881 fActions.regenStrike = fSubRun->strike()->isAbandoned();
Herb Derbydf2c1ee2019-12-26 17:54:41 -0500882
883 fSubRun->updateVerticesColorIfNeeded(color);
884 fSubRun->translateVerticesIfNeeded(drawMatrix, drawOrigin);
Herb Derbya9047642019-12-06 12:12:11 -0500885}
886
Herb Derby73630212019-12-13 16:29:14 -0500887bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result) {
888 SkASSERT(!fActions.regenStrike || fActions.regenTextureCoordinates);
889 if (fActions.regenTextureCoordinates) {
Herb Derbya9047642019-12-06 12:12:11 -0500890 fSubRun->resetBulkUseToken();
891
892 const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
893
894 if (!fMetricsAndImages.isValid()
895 || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
896 fMetricsAndImages.init(strikeSpec);
897 }
898
Herb Derby73630212019-12-13 16:29:14 -0500899 if (fActions.regenStrike) {
Herb Derbya9047642019-12-06 12:12:11 -0500900 // Take the glyphs from the old strike, and translate them a new strike.
901 sk_sp<GrTextStrike> newStrike = strikeSpec.findOrCreateGrStrike(fGrStrikeCache);
902
903 // Start this batch at the start of the subRun plus any glyphs that were previously
904 // processed.
Herb Derbyc514e7d2019-12-11 17:00:31 -0500905 SkSpan<GrGlyph*> glyphs = fSubRun->fGlyphs.last(fSubRun->fGlyphs.size() - fCurrGlyph);
Herb Derbya9047642019-12-06 12:12:11 -0500906
907 // Convert old glyphs to newStrike.
908 for (auto& glyph : glyphs) {
909 SkPackedGlyphID id = glyph->fPackedID;
910 glyph = newStrike->getGlyph(id, fMetricsAndImages.get());
911 SkASSERT(id == glyph->fPackedID);
912 }
913
914 fSubRun->setStrike(newStrike);
915 }
916 }
917
Herb Derby7cf4a2e2019-12-23 14:51:55 -0500918 GrTextStrike* grStrike = fSubRun->strike();
Herb Derbya2d72252019-12-23 15:02:33 -0500919 auto vertexStride = fSubRun->vertexStride();
Herb Derbyc514e7d2019-12-11 17:00:31 -0500920 char* currVertex = fSubRun->fVertexData.data() + fCurrGlyph * kVerticesPerGlyph * vertexStride;
Herb Derbya9047642019-12-06 12:12:11 -0500921 result->fFirstVertex = currVertex;
922
Herb Derbyc514e7d2019-12-11 17:00:31 -0500923 for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->fGlyphs.size(); glyphIdx++) {
Herb Derbya9047642019-12-06 12:12:11 -0500924 GrGlyph* glyph = nullptr;
Herb Derby73630212019-12-13 16:29:14 -0500925 if (fActions.regenTextureCoordinates) {
Herb Derbyc514e7d2019-12-11 17:00:31 -0500926 glyph = fSubRun->fGlyphs[glyphIdx];
Herb Derbya9047642019-12-06 12:12:11 -0500927 SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
928
929 if (!fFullAtlasManager->hasGlyph(glyph)) {
Herb Derby7cf4a2e2019-12-23 14:51:55 -0500930 GrDrawOpAtlas::ErrorCode code = grStrike->addGlyphToAtlas(
931 fResourceProvider, fUploadTarget, fGrStrikeCache, fFullAtlasManager, glyph,
932 fMetricsAndImages.get(), fSubRun->maskFormat(), fSubRun->needsTransform());
Herb Derbya9047642019-12-06 12:12:11 -0500933 if (GrDrawOpAtlas::ErrorCode::kError == code) {
934 // Something horrible has happened - drop the op
935 return false;
936 }
937 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
938 fBrokenRun = glyphIdx > 0;
939 result->fFinished = false;
940 return true;
941 }
942 }
943 auto tokenTracker = fUploadTarget->tokenTracker();
944 fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
945 tokenTracker->nextDrawToken());
946 }
947
Herb Derby73630212019-12-13 16:29:14 -0500948 if (fActions.regenTextureCoordinates) {
Herb Derbya9047642019-12-06 12:12:11 -0500949 regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
950 }
951
952 currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
953 ++result->fGlyphsRegenerated;
954 ++fCurrGlyph;
955 }
956
Herb Derby73630212019-12-13 16:29:14 -0500957 if (fActions.regenTextureCoordinates) {
Herb Derbya9047642019-12-06 12:12:11 -0500958 fSubRun->setAtlasGeneration(fBrokenRun
959 ? GrDrawOpAtlas::kInvalidAtlasGeneration
960 : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
961 } else {
962 // For the non-texCoords case we need to ensure that we update the associated use tokens
963 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
964 fUploadTarget->tokenTracker()->nextDrawToken(),
965 fSubRun->maskFormat());
966 }
967 return true;
968}
969
970bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
971 uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
972 // If regenerate() is called multiple times then the atlas gen may have changed. So we check
973 // this each time.
Herb Derby73630212019-12-13 16:29:14 -0500974 fActions.regenTextureCoordinates |= fSubRun->atlasGeneration() != currentAtlasGen;
Herb Derbya9047642019-12-06 12:12:11 -0500975
Herb Derby73630212019-12-13 16:29:14 -0500976 if (fActions.regenStrike
Herb Derby6ca4f312019-12-26 15:23:49 -0500977 |fActions.regenTextureCoordinates) {
Herb Derby73630212019-12-13 16:29:14 -0500978 return this->doRegen(result);
Herb Derbya9047642019-12-06 12:12:11 -0500979 } else {
Herb Derbya2d72252019-12-23 15:02:33 -0500980 auto vertexStride = fSubRun->vertexStride();
Herb Derbya9047642019-12-06 12:12:11 -0500981 result->fFinished = true;
Herb Derbyc514e7d2019-12-11 17:00:31 -0500982 result->fGlyphsRegenerated = fSubRun->fGlyphs.size() - fCurrGlyph;
983 result->fFirstVertex = fSubRun->fVertexData.data() +
984 fCurrGlyph * kVerticesPerGlyph * vertexStride;
985 fCurrGlyph = fSubRun->fGlyphs.size();
Herb Derbya9047642019-12-06 12:12:11 -0500986
987 // set use tokens for all of the glyphs in our subrun. This is only valid if we
988 // have a valid atlas generation
989 fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
990 fUploadTarget->tokenTracker()->nextDrawToken(),
991 fSubRun->maskFormat());
992 return true;
993 }
994 SK_ABORT("Should not get here");
995}
996
997
998
999