• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "include/core/SkColorFilter.h"
9 #include "include/gpu/GrContext.h"
10 #include "include/private/SkTemplates.h"
11 #include "src/core/SkMaskFilterBase.h"
12 #include "src/core/SkPaintPriv.h"
13 #include "src/gpu/GrBlurUtils.h"
14 #include "src/gpu/GrClip.h"
15 #include "src/gpu/GrStyle.h"
16 #include "src/gpu/geometry/GrShape.h"
17 #include "src/gpu/ops/GrAtlasTextOp.h"
18 #include "src/gpu/text/GrAtlasManager.h"
19 #include "src/gpu/text/GrTextBlob.h"
20 #include "src/gpu/text/GrTextTarget.h"
21 
22 #include <cstddef>
23 #include <new>
24 
make_inverse(const SkMatrix & matrix)25 static 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 ------------------------------------------------------------------------------
Key()34 GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); }
35 
operator ==(const GrTextBlob::Key & other) const36 bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const {
37     return 0 == memcmp(this, &other, sizeof(Key));
38 }
39 
40 // -- GrTextBlob::PathGlyph ------------------------------------------------------------------------
PathGlyph(const SkPath & path,SkPoint origin)41 GrTextBlob::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
42         : fPath(path)
43         , fOrigin(origin) {}
44 
45 // -- GrTextBlob::SubRun ---------------------------------------------------------------------------
SubRun(SubRunType type,GrTextBlob * textBlob,const SkStrikeSpec & strikeSpec,GrMaskFormat format,const SkSpan<GrGlyph * > & glyphs,const SkSpan<char> & vertexData,sk_sp<GrTextStrike> && grStrike)46 GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
47                            GrMaskFormat format,
48                            const SkSpan<GrGlyph*>& glyphs, const SkSpan<char>& vertexData,
49                            sk_sp<GrTextStrike>&& grStrike)
50         : fType{type}
51         , fBlob{textBlob}
52         , fMaskFormat{format}
53         , fGlyphs{glyphs}
54         , fVertexData{vertexData}
55         , fStrikeSpec{strikeSpec}
56         , fStrike{grStrike}
57         , fCurrentColor{textBlob->fColor}
58         , fCurrentOrigin{textBlob->fInitialOrigin}
59         , fCurrentMatrix{textBlob->fInitialMatrix} {
60     SkASSERT(type != kTransformedPath);
61     textBlob->insertSubRun(this);
62 }
63 
SubRun(GrTextBlob * textBlob,const SkStrikeSpec & strikeSpec)64 GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
65         : fType{kTransformedPath}
66         , fBlob{textBlob}
67         , fMaskFormat{kA8_GrMaskFormat}
68         , fGlyphs{SkSpan<GrGlyph*>{}}
69         , fVertexData{SkSpan<char>{}}
70         , fStrikeSpec{strikeSpec}
71         , fStrike{nullptr}
72         , fCurrentColor{textBlob->fColor}
73         , fPaths{} {
74     textBlob->insertSubRun(this);
75 }
76 
appendGlyphs(const SkZip<SkGlyphVariant,SkPoint> & drawables)77 void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
78     GrTextStrike* grStrike = fStrike.get();
79     SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
80     GrGlyph** glyphCursor = fGlyphs.data();
81     char* vertexCursor = fVertexData.data();
82     size_t vertexStride = this->vertexStride();
83     // We always write the third position component used by SDFs. If it is unused it gets
84     // overwritten. Similarly, we always write the color and the blob will later overwrite it
85     // with texture coords if it is unused.
86     size_t colorOffset = this->colorOffset();
87     for (auto [variant, pos] : drawables) {
88         SkGlyph* skGlyph = variant;
89         GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
90         // Only floor the device coordinates.
91         SkRect dstRect;
92         if (!this->needsTransform()) {
93             pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
94             dstRect = grGlyph->destRect(pos);
95         } else {
96             dstRect = grGlyph->destRect(pos, strikeToSource);
97         }
98 
99         this->joinGlyphBounds(dstRect);
100 
101         // V0
102         *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fTop, 1.f};
103         *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
104         vertexCursor += vertexStride;
105 
106         // V1
107         *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fBottom, 1.f};
108         *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
109         vertexCursor += vertexStride;
110 
111         // V2
112         *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fTop, 1.f};
113         *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
114         vertexCursor += vertexStride;
115 
116         // V3
117         *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fBottom, 1.f};
118         *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
119         vertexCursor += vertexStride;
120 
121         *glyphCursor++ = grGlyph;
122     }
123 }
124 
resetBulkUseToken()125 void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
126 
bulkUseToken()127 GrDrawOpAtlas::BulkUseTokenUpdater* GrTextBlob::SubRun::bulkUseToken() { return &fBulkUseToken; }
setStrike(sk_sp<GrTextStrike> strike)128 void GrTextBlob::SubRun::setStrike(sk_sp<GrTextStrike> strike) { fStrike = std::move(strike); }
strike() const129 GrTextStrike* GrTextBlob::SubRun::strike() const { return fStrike.get(); }
maskFormat() const130 GrMaskFormat GrTextBlob::SubRun::maskFormat() const { return fMaskFormat; }
vertexStride() const131 size_t GrTextBlob::SubRun::vertexStride() const {
132     return GetVertexStride(this->maskFormat(), this->hasW());
133 }
colorOffset() const134 size_t GrTextBlob::SubRun::colorOffset() const {
135     return this->hasW() ? offsetof(SDFT3DVertex, color) : offsetof(Mask2DVertex, color);
136 }
137 
texCoordOffset() const138 size_t GrTextBlob::SubRun::texCoordOffset() const {
139     switch (fMaskFormat) {
140         case kA8_GrMaskFormat:
141             return this->hasW() ? offsetof(SDFT3DVertex, atlasPos)
142                                 : offsetof(Mask2DVertex, atlasPos);
143         case kARGB_GrMaskFormat:
144             return this->hasW() ? offsetof(ARGB3DVertex, atlasPos)
145                                 : offsetof(ARGB2DVertex, atlasPos);
146         default:
147             SkASSERT(!this->hasW());
148             return offsetof(Mask2DVertex, atlasPos);
149     }
150 }
151 
quadStart(size_t index) const152 char* GrTextBlob::SubRun::quadStart(size_t index) const {
153     return SkTAddOffset<char>(fVertexData.data(), this->quadOffset(index));
154 }
155 
quadOffset(size_t index) const156 size_t GrTextBlob::SubRun::quadOffset(size_t index) const {
157     return index * kVerticesPerGlyph * this->vertexStride();
158 }
159 
vertexBounds() const160 const SkRect& GrTextBlob::SubRun::vertexBounds() const { return fVertexBounds; }
joinGlyphBounds(const SkRect & glyphBounds)161 void GrTextBlob::SubRun::joinGlyphBounds(const SkRect& glyphBounds) {
162     fVertexBounds.joinNonEmptyArg(glyphBounds);
163 }
164 
drawAsDistanceFields() const165 bool GrTextBlob::SubRun::drawAsDistanceFields() const { return fType == kTransformedSDFT; }
166 
drawAsPaths() const167 bool GrTextBlob::SubRun::drawAsPaths() const { return fType == kTransformedPath; }
168 
needsTransform() const169 bool GrTextBlob::SubRun::needsTransform() const {
170     return fType == kTransformedPath
171            || fType == kTransformedMask
172            || fType == kTransformedSDFT;
173 }
174 
hasW() const175 bool GrTextBlob::SubRun::hasW() const {
176     return fBlob->hasW(fType);
177 }
178 
translateVerticesIfNeeded(const SkMatrix & drawMatrix,SkPoint drawOrigin)179 void GrTextBlob::SubRun::translateVerticesIfNeeded(
180         const SkMatrix& drawMatrix, SkPoint drawOrigin) {
181     SkVector translation;
182     if (this->needsTransform()) {
183         // If transform is needed, then the vertices are in source space, calculate the source
184         // space translation.
185         translation = drawOrigin - fCurrentOrigin;
186     } else {
187         // Calculate the translation in source space to a translation in device space. Calculate
188         // the translation by mapping (0, 0) through both the current matrix, and the draw
189         // matrix, and taking the difference.
190         SkMatrix currentMatrix{fCurrentMatrix};
191         currentMatrix.preTranslate(fCurrentOrigin.x(), fCurrentOrigin.y());
192         SkPoint currentDeviceOrigin{0, 0};
193         currentMatrix.mapPoints(&currentDeviceOrigin, 1);
194         SkMatrix completeDrawMatrix{drawMatrix};
195         completeDrawMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
196         SkPoint drawDeviceOrigin{0, 0};
197         completeDrawMatrix.mapPoints(&drawDeviceOrigin, 1);
198         translation = drawDeviceOrigin - currentDeviceOrigin;
199     }
200 
201     if (translation != SkPoint{0, 0}) {
202         size_t vertexStride = this->vertexStride();
203         for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
204             SkPoint* vertexCursor = reinterpret_cast<SkPoint*>(quadStart(quad));
205             for (int i = 0; i < 4; ++i) {
206                 *vertexCursor += translation;
207                 vertexCursor = SkTAddOffset<SkPoint>(vertexCursor, vertexStride);
208             }
209         }
210         fCurrentMatrix = drawMatrix;
211         fCurrentOrigin = drawOrigin;
212     }
213 }
214 
updateVerticesColorIfNeeded(GrColor newColor)215 void GrTextBlob::SubRun::updateVerticesColorIfNeeded(GrColor newColor) {
216     if (this->maskFormat() != kARGB_GrMaskFormat && fCurrentColor != newColor) {
217         size_t vertexStride = this->vertexStride();
218         size_t colorOffset = this->colorOffset();
219         for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
220             GrColor* colorCursor = SkTAddOffset<GrColor>(quadStart(quad), colorOffset);
221             for (int i = 0; i < 4; ++i) {
222                 *colorCursor = newColor;
223                 colorCursor = SkTAddOffset<GrColor>(colorCursor, vertexStride);
224             }
225         }
226         this->fCurrentColor = newColor;
227     }
228 }
229 
updateTexCoords(int begin,int end)230 void GrTextBlob::SubRun::updateTexCoords(int begin, int end) {
231     const size_t vertexStride = this->vertexStride();
232     const size_t texCoordOffset = this->texCoordOffset();
233     char* vertex = this->quadStart(begin);
234     uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
235     for (int i = begin; i < end; i++) {
236         GrGlyph* glyph = this->fGlyphs[i];
237         SkASSERT(glyph != nullptr);
238 
239         int width = glyph->fBounds.width();
240         int height = glyph->fBounds.height();
241         uint16_t u0, v0, u1, v1;
242         if (this->drawAsDistanceFields()) {
243             u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
244             v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
245             u1 = u0 + width - 2 * SK_DistanceFieldInset;
246             v1 = v0 + height - 2 * SK_DistanceFieldInset;
247         } else {
248             u0 = glyph->fAtlasLocation.fX;
249             v0 = glyph->fAtlasLocation.fY;
250             u1 = u0 + width;
251             v1 = v0 + height;
252         }
253 
254         // We pack the 2bit page index in the low bit of the u and v texture coords
255         uint32_t pageIndex = glyph->pageIndex();
256         std::tie(u0, v0) = GrDrawOpAtlas::PackIndexInTexCoords(u0, v0, pageIndex);
257         std::tie(u1, v1) = GrDrawOpAtlas::PackIndexInTexCoords(u1, v1, pageIndex);
258 
259         textureCoords[0] = u0;
260         textureCoords[1] = v0;
261         textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
262         textureCoords[0] = u0;
263         textureCoords[1] = v1;
264         textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
265         textureCoords[0] = u1;
266         textureCoords[1] = v0;
267         textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
268         textureCoords[0] = u1;
269         textureCoords[1] = v1;
270         textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
271     }
272 }
273 
274 
setUseLCDText(bool useLCDText)275 void GrTextBlob::SubRun::setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
hasUseLCDText() const276 bool GrTextBlob::SubRun::hasUseLCDText() const { return fFlags.useLCDText; }
setAntiAliased(bool antiAliased)277 void GrTextBlob::SubRun::setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
isAntiAliased() const278 bool GrTextBlob::SubRun::isAntiAliased() const { return fFlags.antiAliased; }
strikeSpec() const279 const SkStrikeSpec& GrTextBlob::SubRun::strikeSpec() const { return fStrikeSpec; }
280 
281 // -- GrTextBlob -----------------------------------------------------------------------------------
operator delete(void * p)282 void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
operator new(size_t)283 void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
operator new(size_t,void * p)284 void* GrTextBlob::operator new(size_t, void* p) { return p; }
285 
286 GrTextBlob::~GrTextBlob() = default;
287 
Make(const SkGlyphRunList & glyphRunList,GrStrikeCache * strikeCache,const SkMatrix & drawMatrix,GrColor color,bool forceWForDistanceFields)288 sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
289                                    GrStrikeCache* strikeCache,
290                                    const SkMatrix& drawMatrix,
291                                    GrColor color,
292                                    bool forceWForDistanceFields) {
293 
294     static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
295     static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex));
296     size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph;
297     if (drawMatrix.hasPerspective() || forceWForDistanceFields) {
298         static_assert(sizeof(ARGB3DVertex) <= sizeof(SDFT3DVertex));
299         static_assert(alignof(ARGB3DVertex) <= alignof(SDFT3DVertex));
300         quadSize = sizeof(SDFT3DVertex) * kVerticesPerGlyph;
301     }
302 
303     // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments.
304     static_assert(alignof(SDFT3DVertex) >= alignof(Mask2DVertex));
305     // Assume there is no padding needed between glyph pointers and vertices.
306     static_assert(alignof(GrGlyph*) >= alignof(SDFT3DVertex));
307 
308     // In the arena, the layout is GrGlyph*... | SDFT3DVertex... | SubRun, so there is no padding
309     // between GrGlyph* and SDFT3DVertex, but padding is needed between the Mask2DVertex array
310     // and the SubRun.
311     size_t vertexToSubRunPadding = alignof(SDFT3DVertex) - alignof(SubRun);
312     size_t arenaSize =
313             sizeof(GrGlyph*) * glyphRunList.totalGlyphCount()
314           + quadSize * glyphRunList.totalGlyphCount()
315           + glyphRunList.runCount() * (sizeof(SubRun) + vertexToSubRunPadding);
316 
317     size_t allocationSize = sizeof(GrTextBlob) + arenaSize;
318 
319     void* allocation = ::operator new (allocationSize);
320 
321     SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(glyphRunList.paint());
322     sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
323             arenaSize, strikeCache, drawMatrix, glyphRunList.origin(),
324             color, initialLuminance, forceWForDistanceFields}};
325 
326     return blob;
327 }
328 
setupKey(const GrTextBlob::Key & key,const SkMaskFilterBase::BlurRec & blurRec,const SkPaint & paint)329 void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::BlurRec& blurRec,
330                           const SkPaint& paint) {
331     fKey = key;
332     if (key.fHasBlur) {
333         fBlurRec = blurRec;
334     }
335     if (key.fStyle != SkPaint::kFill_Style) {
336         fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
337         fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
338         fStrokeInfo.fJoin = paint.getStrokeJoin();
339     }
340 }
GetKey(const GrTextBlob & blob)341 const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
Hash(const GrTextBlob::Key & key)342 uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
343 
hasDistanceField() const344 bool GrTextBlob::hasDistanceField() const {
345     return SkToBool(fTextType & kHasDistanceField_TextType);
346 }
hasBitmap() const347 bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
hasPerspective() const348 bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
349 
setHasDistanceField()350 void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
setHasBitmap()351 void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; }
setMinAndMaxScale(SkScalar scaledMin,SkScalar scaledMax)352 void GrTextBlob::setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
353     // we init fMaxMinScale and fMinMaxScale in the constructor
354     fMaxMinScale = std::max(scaledMin, fMaxMinScale);
355     fMinMaxScale = std::min(scaledMax, fMinMaxScale);
356 }
357 
GetVertexStride(GrMaskFormat maskFormat,bool hasWCoord)358 size_t GrTextBlob::GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
359     switch (maskFormat) {
360         case kA8_GrMaskFormat:
361             return hasWCoord ? sizeof(SDFT3DVertex) : sizeof(Mask2DVertex);
362         case kARGB_GrMaskFormat:
363             return hasWCoord ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
364         default:
365             SkASSERT(!hasWCoord);
366             return sizeof(Mask2DVertex);
367     }
368 }
369 
mustRegenerate(const SkPaint & paint,bool anyRunHasSubpixelPosition,const SkMaskFilterBase::BlurRec & blurRec,const SkMatrix & drawMatrix,SkPoint drawOrigin)370 bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
371                                 const SkMaskFilterBase::BlurRec& blurRec,
372                                 const SkMatrix& drawMatrix, SkPoint drawOrigin) {
373     // If we have LCD text then our canonical color will be set to transparent, in this case we have
374     // to regenerate the blob on any color change
375     // We use the grPaint to get any color filter effects
376     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
377         fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
378         return true;
379     }
380 
381     if (fInitialMatrix.hasPerspective() != drawMatrix.hasPerspective()) {
382         return true;
383     }
384 
385     /** This could be relaxed for blobs with only distance field glyphs. */
386     if (fInitialMatrix.hasPerspective() && !SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix)) {
387         return true;
388     }
389 
390     // We only cache one masked version
391     if (fKey.fHasBlur &&
392         (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
393         return true;
394     }
395 
396     // Similarly, we only cache one version for each style
397     if (fKey.fStyle != SkPaint::kFill_Style &&
398         (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
399          fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
400          fStrokeInfo.fJoin != paint.getStrokeJoin())) {
401         return true;
402     }
403 
404     // Mixed blobs must be regenerated.  We could probably figure out a way to do integer scrolls
405     // for mixed blobs if this becomes an issue.
406     if (this->hasBitmap() && this->hasDistanceField()) {
407         // Identical view matrices and we can reuse in all cases
408         return !(SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix) &&
409                  drawOrigin == fInitialOrigin);
410     }
411 
412     if (this->hasBitmap()) {
413         if (fInitialMatrix.getScaleX() != drawMatrix.getScaleX() ||
414             fInitialMatrix.getScaleY() != drawMatrix.getScaleY() ||
415             fInitialMatrix.getSkewX() != drawMatrix.getSkewX() ||
416             fInitialMatrix.getSkewY() != drawMatrix.getSkewY()) {
417             return true;
418         }
419 
420         // TODO(herb): this is not needed for full pixel glyph choice, but is needed to adjust
421         //  the quads properly. Devise a system that regenerates the quads from original data
422         //  using the transform to allow this to be used in general.
423 
424         // We can update the positions in the text blob without regenerating the whole
425         // blob, but only for integer translations.
426         // Calculate the translation in source space to a translation in device space by mapping
427         // (0, 0) through both the initial matrix and the draw matrix; take the difference.
428         SkMatrix initialMatrix{fInitialMatrix};
429         initialMatrix.preTranslate(fInitialOrigin.x(), fInitialOrigin.y());
430         SkPoint initialDeviceOrigin{0, 0};
431         initialMatrix.mapPoints(&initialDeviceOrigin, 1);
432         SkMatrix completeDrawMatrix{drawMatrix};
433         completeDrawMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
434         SkPoint drawDeviceOrigin{0, 0};
435         completeDrawMatrix.mapPoints(&drawDeviceOrigin, 1);
436         SkPoint translation = drawDeviceOrigin - initialDeviceOrigin;
437 
438         if (!SkScalarIsInt(translation.x()) || !SkScalarIsInt(translation.y())) {
439             return true;
440         }
441     } else if (this->hasDistanceField()) {
442         // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
443         // distance field being generated, so we have to regenerate in those cases
444         SkScalar newMaxScale = drawMatrix.getMaxScale();
445         SkScalar oldMaxScale = fInitialMatrix.getMaxScale();
446         SkScalar scaleAdjust = newMaxScale / oldMaxScale;
447         if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
448             return true;
449         }
450     }
451 
452     // It is possible that a blob has neither distanceField nor bitmaptext.  This is in the case
453     // when all of the runs inside the blob are drawn as paths.  In this case, we always regenerate
454     // the blob anyways at flush time, so no need to regenerate explicitly
455     return false;
456 }
457 
flush(GrTextTarget * target,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,const SkPaint & paint,const SkPMColor4f & filteredColor,const GrClip & clip,const SkMatrix & drawMatrix,SkPoint drawOrigin)458 void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
459                        const GrDistanceFieldAdjustTable* distanceAdjustTable,
460                        const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
461                        const SkMatrix& drawMatrix, SkPoint drawOrigin) {
462 
463     for (SubRun* subRun = fFirstSubRun; subRun != nullptr; subRun = subRun->fNextSubRun) {
464         if (subRun->drawAsPaths()) {
465             SkPaint runPaint{paint};
466             runPaint.setAntiAlias(subRun->isAntiAliased());
467             // If there are shaders, blurs or styles, the path must be scaled into source
468             // space independently of the CTM. This allows the CTM to be correct for the
469             // different effects.
470             GrStyle style(runPaint);
471 
472             bool scalePath = runPaint.getShader()
473                              || style.applies()
474                              || runPaint.getMaskFilter();
475 
476 
477             for (const auto& pathGlyph : subRun->fPaths) {
478                 SkMatrix ctm{drawMatrix};
479                 ctm.preTranslate(drawOrigin.x(), drawOrigin.y());
480                 SkMatrix pathMatrix = SkMatrix::MakeScale(
481                         subRun->fStrikeSpec.strikeToSourceRatio());
482                 pathMatrix.postTranslate(pathGlyph.fOrigin.x(), pathGlyph.fOrigin.y());
483 
484                 // TmpPath must be in the same scope as GrShape shape below.
485                 SkTLazy<SkPath> tmpPath;
486                 const SkPath* path = &pathGlyph.fPath;
487                 if (!scalePath) {
488                     // Scale can be applied to CTM -- no effects.
489                     ctm.preConcat(pathMatrix);
490                 } else {
491                     // Scale the outline into source space.
492 
493                     // Transform the path form the normalized outline to source space. This
494                     // way the CTM will remain the same so it can be used by the effects.
495                     SkPath* sourceOutline = tmpPath.init();
496                     path->transform(pathMatrix, sourceOutline);
497                     sourceOutline->setIsVolatile(true);
498                     path = sourceOutline;
499                 }
500 
501                 // TODO: we are losing the mutability of the path here
502                 GrShape shape(*path, paint);
503                 target->drawShape(clip, runPaint, ctm, shape);
504             }
505         } else {
506             int glyphCount = subRun->fGlyphs.size();
507             if (0 == glyphCount) {
508                 continue;
509             }
510 
511             bool skipClip = false;
512             bool submitOp = true;
513             SkIRect clipRect = SkIRect::MakeEmpty();
514             SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
515             SkRRect clipRRect;
516             GrAA aa;
517             // We can clip geometrically if we're not using SDFs or transformed glyphs,
518             // and we have an axis-aligned rectangular non-AA clip
519             if (!subRun->drawAsDistanceFields() && !subRun->needsTransform() &&
520                 clip.isRRect(rtBounds, &clipRRect, &aa) &&
521                 clipRRect.isRect() && GrAA::kNo == aa) {
522                 skipClip = true;
523                 // We only need to do clipping work if the subrun isn't contained by the clip
524                 SkRect subRunBounds;
525                 this->computeSubRunBounds(
526                         &subRunBounds, *subRun, drawMatrix, drawOrigin, false);
527                 if (!clipRRect.getBounds().contains(subRunBounds)) {
528                     // If the subrun is completely outside, don't add an op for it
529                     if (!clipRRect.getBounds().intersects(subRunBounds)) {
530                         submitOp = false;
531                     }
532                     else {
533                         clipRRect.getBounds().round(&clipRect);
534                     }
535                 }
536             }
537 
538             if (submitOp) {
539                 auto op = this->makeOp(*subRun, glyphCount, drawMatrix, drawOrigin,
540                                        clipRect, paint, filteredColor, props, distanceAdjustTable,
541                                        target);
542                 if (op) {
543                     if (skipClip) {
544                         target->addDrawOp(GrNoClip(), std::move(op));
545                     }
546                     else {
547                         target->addDrawOp(clip, std::move(op));
548                     }
549                 }
550             }
551         }
552     }
553 }
554 
computeSubRunBounds(SkRect * outBounds,const SubRun & subRun,const SkMatrix & drawMatrix,SkPoint drawOrigin,bool needsGlyphTransform)555 void GrTextBlob::computeSubRunBounds(SkRect* outBounds, const SubRun& subRun,
556                                      const SkMatrix& drawMatrix, SkPoint drawOrigin,
557                                      bool needsGlyphTransform) {
558     // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
559     // into device space.
560     // We handle vertex bounds differently for distance field text and bitmap text because
561     // the vertex bounds of bitmap text are in device space.  If we are flushing multiple runs
562     // from one blob then we are going to pay the price here of mapping the rect for each run.
563     *outBounds = subRun.vertexBounds();
564     if (needsGlyphTransform) {
565         // Distance field text is positioned with the (X,Y) as part of the glyph position,
566         // and currently the view matrix is applied on the GPU
567         outBounds->offset(drawOrigin - fInitialOrigin);
568         drawMatrix.mapRect(outBounds);
569     } else {
570         // Bitmap text is fully positioned on the CPU, and offset by an (X,Y) translate in
571         // device space.
572         SkMatrix boundsMatrix = fInitialMatrixInverse;
573 
574         boundsMatrix.postTranslate(-fInitialOrigin.x(), -fInitialOrigin.y());
575 
576         boundsMatrix.postTranslate(drawOrigin.x(), drawOrigin.y());
577 
578         boundsMatrix.postConcat(drawMatrix);
579         boundsMatrix.mapRect(outBounds);
580 
581         // Due to floating point numerical inaccuracies, we have to round out here
582         outBounds->roundOut(outBounds);
583     }
584 }
585 
key() const586 const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
size() const587 size_t GrTextBlob::size() const { return fSize; }
588 
test_makeOp(int glyphCount,const SkMatrix & drawMatrix,SkPoint drawOrigin,const SkPaint & paint,const SkPMColor4f & filteredColor,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,GrTextTarget * target)589 std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
590         int glyphCount, const SkMatrix& drawMatrix,
591         SkPoint drawOrigin, const SkPaint& paint, const SkPMColor4f& filteredColor,
592         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
593         GrTextTarget* target) {
594     SubRun* info = fFirstSubRun;
595     SkIRect emptyRect = SkIRect::MakeEmpty();
596     return this->makeOp(*info, glyphCount, drawMatrix, drawOrigin, emptyRect,
597                         paint, filteredColor, props, distanceAdjustTable, target);
598 }
599 
hasW(GrTextBlob::SubRunType type) const600 bool GrTextBlob::hasW(GrTextBlob::SubRunType type) const {
601     if (type == kTransformedSDFT) {
602         return this->hasPerspective() || fForceWForDistanceFields;
603     } else if (type == kTransformedMask || type == kTransformedPath) {
604         return this->hasPerspective();
605     }
606 
607     // The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
608     // used.
609     return false;
610 }
611 
makeSubRun(SubRunType type,const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,GrMaskFormat format)612 GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
613                                            const SkZip<SkGlyphVariant, SkPoint>& drawables,
614                                            const SkStrikeSpec& strikeSpec,
615                                            GrMaskFormat format) {
616     SkSpan<GrGlyph*> glyphs{fAlloc.makeArrayDefault<GrGlyph*>(drawables.size()), drawables.size()};
617     bool hasW = this->hasW(type);
618 
619     SkASSERT(!fInitialMatrix.hasPerspective() || hasW);
620 
621     size_t vertexDataSize = drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
622     SkSpan<char> vertexData{fAlloc.makeArrayDefault<char>(vertexDataSize), vertexDataSize};
623 
624     sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
625 
626     SubRun* subRun = fAlloc.make<SubRun>(
627             type, this, strikeSpec, format, glyphs, vertexData, std::move(grStrike));
628 
629     subRun->appendGlyphs(drawables);
630 
631     return subRun;
632 }
633 
addSingleMaskFormat(SubRunType type,const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,GrMaskFormat format)634 void GrTextBlob::addSingleMaskFormat(
635         SubRunType type,
636         const SkZip<SkGlyphVariant, SkPoint>& drawables,
637         const SkStrikeSpec& strikeSpec,
638         GrMaskFormat format) {
639     this->makeSubRun(type, drawables, strikeSpec, format);
640 }
641 
addMultiMaskFormat(SubRunType type,const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)642 void GrTextBlob::addMultiMaskFormat(
643         SubRunType type,
644         const SkZip<SkGlyphVariant, SkPoint>& drawables,
645         const SkStrikeSpec& strikeSpec) {
646     this->setHasBitmap();
647     if (drawables.empty()) { return; }
648 
649     auto glyphSpan = drawables.get<0>();
650     SkGlyph* glyph = glyphSpan[0];
651     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
652     size_t startIndex = 0;
653     for (size_t i = 1; i < drawables.size(); i++) {
654         glyph = glyphSpan[i];
655         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
656         if (format != nextFormat) {
657             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
658             this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
659             format = nextFormat;
660             startIndex = i;
661         }
662     }
663     auto sameFormat = drawables.last(drawables.size() - startIndex);
664     this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
665 }
666 
addSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)667 void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
668                          const SkStrikeSpec& strikeSpec,
669                          const SkFont& runFont,
670                          SkScalar minScale,
671                          SkScalar maxScale) {
672     this->setHasDistanceField();
673     this->setMinAndMaxScale(minScale, maxScale);
674 
675     SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
676     subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
677     subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
678 }
679 
GrTextBlob(size_t allocSize,GrStrikeCache * strikeCache,const SkMatrix & drawMatrix,SkPoint origin,GrColor color,SkColor initialLuminance,bool forceWForDistanceFields)680 GrTextBlob::GrTextBlob(size_t allocSize,
681                        GrStrikeCache* strikeCache,
682                        const SkMatrix& drawMatrix,
683                        SkPoint origin,
684                        GrColor color,
685                        SkColor initialLuminance,
686                        bool forceWForDistanceFields)
687         : fSize{allocSize}
688         , fStrikeCache{strikeCache}
689         , fInitialMatrix{drawMatrix}
690         , fInitialMatrixInverse{make_inverse(drawMatrix)}
691         , fInitialOrigin{origin}
692         , fForceWForDistanceFields{forceWForDistanceFields}
693         , fColor{color}
694         , fInitialLuminance{initialLuminance}
695         , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
696 
insertSubRun(SubRun * subRun)697 void GrTextBlob::insertSubRun(SubRun* subRun) {
698     if (fFirstSubRun == nullptr) {
699         fFirstSubRun = subRun;
700         fLastSubRun = subRun;
701     } else {
702         fLastSubRun->fNextSubRun = subRun;
703         fLastSubRun = subRun;
704     }
705 }
706 
makeOp(SubRun & info,int glyphCount,const SkMatrix & drawMatrix,SkPoint drawOrigin,const SkIRect & clipRect,const SkPaint & paint,const SkPMColor4f & filteredColor,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,GrTextTarget * target)707 std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
708         SubRun& info, int glyphCount,
709         const SkMatrix& drawMatrix, SkPoint drawOrigin, const SkIRect& clipRect,
710         const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
711         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
712     GrMaskFormat format = info.maskFormat();
713 
714     GrPaint grPaint;
715     target->makeGrPaint(info.maskFormat(), paint, drawMatrix, &grPaint);
716     std::unique_ptr<GrAtlasTextOp> op;
717     if (info.drawAsDistanceFields()) {
718         // TODO: Can we be even smarter based on the dest transfer function?
719         op = GrAtlasTextOp::MakeDistanceField(
720                 target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable,
721                 target->colorInfo().isLinearlyBlended(), SkPaintPriv::ComputeLuminanceColor(paint),
722                 props, info.isAntiAliased(), info.hasUseLCDText());
723     } else {
724         op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
725                                        info.needsTransform());
726     }
727     GrAtlasTextOp::Geometry& geometry = op->geometry();
728     geometry.fDrawMatrix = drawMatrix;
729     geometry.fClipRect = clipRect;
730     geometry.fBlob = SkRef(this);
731     geometry.fSubRunPtr = &info;
732     geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
733     geometry.fDrawOrigin = drawOrigin;
734     op->init();
735     return op;
736 }
737 
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)738 void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
739                                     const SkStrikeSpec& strikeSpec) {
740     this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
741 }
742 
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,const SkStrikeSpec & strikeSpec)743 void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
744                                     const SkFont& runFont,
745                                     const SkStrikeSpec& strikeSpec) {
746     this->setHasBitmap();
747     SubRun* subRun = fAlloc.make<SubRun>(this, strikeSpec);
748     subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
749     for (auto [variant, pos] : drawables) {
750         subRun->fPaths.emplace_back(*variant.path(), pos);
751     }
752 }
753 
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)754 void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
755                                    const SkStrikeSpec& strikeSpec,
756                                    const SkFont& runFont,
757                                    SkScalar minScale,
758                                    SkScalar maxScale) {
759     this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
760 }
761 
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)762 void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
763                                     const SkStrikeSpec& strikeSpec) {
764     this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
765 }
766 
767 // -- GrTextBlob::VertexRegenerator ----------------------------------------------------------------
VertexRegenerator(GrResourceProvider * resourceProvider,GrTextBlob::SubRun * subRun,GrDeferredUploadTarget * uploadTarget,GrAtlasManager * fullAtlasManager)768 GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
769                                                  GrTextBlob::SubRun* subRun,
770                                                  GrDeferredUploadTarget* uploadTarget,
771                                                  GrAtlasManager* fullAtlasManager)
772         : fResourceProvider(resourceProvider)
773         , fUploadTarget(uploadTarget)
774         , fFullAtlasManager(fullAtlasManager)
775         , fSubRun(subRun) { }
776 
updateTextureCoordinates(const int begin,const int end)777 std::tuple<bool, int> GrTextBlob::VertexRegenerator::updateTextureCoordinates(
778         const int begin, const int end) {
779 
780     const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
781 
782     if (!fMetricsAndImages.isValid()
783             || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
784         fMetricsAndImages.init(strikeSpec);
785     }
786 
787     // Update the atlas information in the GrStrike.
788     auto code = GrDrawOpAtlas::ErrorCode::kSucceeded;
789     GrTextStrike* grStrike = fSubRun->strike();
790     auto tokenTracker = fUploadTarget->tokenTracker();
791     int i = begin;
792     for (; i < end; i++) {
793         GrGlyph* grGlyph = fSubRun->fGlyphs[i];
794         SkASSERT(grGlyph && grGlyph->fMaskFormat == fSubRun->maskFormat());
795 
796         if (!fFullAtlasManager->hasGlyph(grGlyph)) {
797             const SkGlyph& skGlyph = *fMetricsAndImages->glyph(grGlyph->fPackedID);
798             if (skGlyph.image() == nullptr) { return {false, 0}; }
799             code = grStrike->addGlyphToAtlas(skGlyph,
800                     fSubRun->maskFormat(),
801                     fSubRun->needsTransform(),
802                     fResourceProvider, fUploadTarget, fFullAtlasManager, grGlyph);
803             if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
804                 break;
805             }
806         }
807         fFullAtlasManager->addGlyphToBulkAndSetUseToken(
808                 fSubRun->bulkUseToken(), grGlyph, tokenTracker->nextDrawToken());
809     }
810     int glyphsPlacedInAtlas = i - begin;
811 
812     // Update the quads with the new atlas coordinates.
813     fSubRun->updateTexCoords(begin, begin + glyphsPlacedInAtlas);
814 
815     return {code != GrDrawOpAtlas::ErrorCode::kError, glyphsPlacedInAtlas};
816 }
817 
regenerate(int begin,int end)818 std::tuple<bool, int> GrTextBlob::VertexRegenerator::regenerate(int begin, int end) {
819     uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
820 
821     if (fSubRun->fAtlasGeneration != currentAtlasGen) {
822         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
823         // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
824         fSubRun->resetBulkUseToken();
825         auto [success, glyphsPlacedInAtlas] = this->updateTextureCoordinates(begin, end);
826 
827         // Update atlas generation if there are no more glyphs to put in the atlas.
828         if (success && begin + glyphsPlacedInAtlas == (int)fSubRun->fGlyphs.size()) {
829             // Need to get the freshest value of the atlas' generation because
830             // updateTextureCoordinates may have changed it.
831             fSubRun->fAtlasGeneration = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
832         }
833         return {success, glyphsPlacedInAtlas};
834     } else {
835         // The atlas hasn't changed, so our texture coordinates are still valid.
836         if (end == (int)fSubRun->fGlyphs.size()) {
837             // The atlas hasn't changed and the texture coordinates are all still valid. Update
838             // all the plots used to the new use token.
839             fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
840                                                fUploadTarget->tokenTracker()->nextDrawToken(),
841                                                fSubRun->maskFormat());
842         }
843         return {true, end - begin};
844     }
845 }
846