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/GrRecordingContext.h"
10 #include "include/private/SkTemplates.h"
11 #include "src/core/SkMaskFilterBase.h"
12 #include "src/core/SkMatrixPriv.h"
13 #include "src/core/SkMatrixProvider.h"
14 #include "src/core/SkPaintPriv.h"
15 #include "src/core/SkStrikeSpec.h"
16 #include "src/gpu/GrBlurUtils.h"
17 #include "src/gpu/GrClip.h"
18 #include "src/gpu/GrMemoryPool.h"
19 #include "src/gpu/GrRecordingContextPriv.h"
20 #include "src/gpu/GrStyle.h"
21 #include "src/gpu/GrSurfaceDrawContext.h"
22 #include "src/gpu/SkGr.h"
23 #include "src/gpu/effects/GrDistanceFieldGeoProc.h"
24 #include "src/gpu/geometry/GrStyledShape.h"
25 #include "src/gpu/ops/GrAtlasTextOp.h"
26 #include "src/gpu/text/GrAtlasManager.h"
27 #include "src/gpu/text/GrStrikeCache.h"
28 #include "src/gpu/text/GrTextBlob.h"
29 
30 namespace {
31 struct AtlasPt {
32     uint16_t u;
33     uint16_t v;
34 };
35 
36 // Normal text mask, SDFT, or color.
37 struct Mask2DVertex {
38     SkPoint devicePos;
39     GrColor color;
40     AtlasPt atlasPos;
41 };
42 
43 struct ARGB2DVertex {
ARGB2DVertex__anon68cae19c0111::ARGB2DVertex44     ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
45 
46     SkPoint devicePos;
47     AtlasPt atlasPos;
48 };
49 
50 // Perspective SDFT or SDFT forced to 3D or perspective color.
51 struct Mask3DVertex {
52     SkPoint3 devicePos;
53     GrColor color;
54     AtlasPt atlasPos;
55 };
56 
57 struct ARGB3DVertex {
ARGB3DVertex__anon68cae19c0111::ARGB3DVertex58     ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
59 
60     SkPoint3 devicePos;
61     AtlasPt atlasPos;
62 };
63 
op_mask_type(GrMaskFormat grMaskFormat)64 GrAtlasTextOp::MaskType op_mask_type(GrMaskFormat grMaskFormat) {
65     switch (grMaskFormat) {
66         case kA8_GrMaskFormat:   return GrAtlasTextOp::MaskType::kGrayscaleCoverage;
67         case kA565_GrMaskFormat: return GrAtlasTextOp::MaskType::kLCDCoverage;
68         case kARGB_GrMaskFormat: return GrAtlasTextOp::MaskType::kColorBitmap;
69     }
70     SkUNREACHABLE;
71 }
72 
calculate_colors(GrSurfaceDrawContext * sdc,const SkPaint & paint,const SkMatrixProvider & matrix,GrMaskFormat grMaskFormat,GrPaint * grPaint)73 SkPMColor4f calculate_colors(GrSurfaceDrawContext* sdc,
74                              const SkPaint& paint,
75                              const SkMatrixProvider& matrix,
76                              GrMaskFormat grMaskFormat,
77                              GrPaint* grPaint) {
78     GrRecordingContext* rContext = sdc->recordingContext();
79     const GrColorInfo& colorInfo = sdc->colorInfo();
80     if (grMaskFormat == kARGB_GrMaskFormat) {
81         SkPaintToGrPaintWithPrimitiveColor(rContext, colorInfo, paint, matrix, grPaint);
82         return SK_PMColor4fWHITE;
83     } else {
84         SkPaintToGrPaint(rContext, colorInfo, paint, matrix, grPaint);
85         return grPaint->getColor4f();
86     }
87 }
88 
89 template<typename Quad, typename VertexData>
fill_transformed_vertices_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,SkScalar dstPadding,SkScalar strikeToSource,GrColor color,const SkMatrix & matrix)90 void fill_transformed_vertices_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
91                                   SkScalar dstPadding,
92                                   SkScalar strikeToSource,
93                                   GrColor color,
94                                   const SkMatrix& matrix) {
95     SkPoint inset = {dstPadding, dstPadding};
96     for (auto[quad, glyph, vertexData] : quadData) {
97         auto[pos, rect] = vertexData;
98         auto[l, t, r, b] = rect;
99         SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
100                 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
101         SkPoint lt = matrix.mapXY(sLT.x(), sLT.y()),
102                 lb = matrix.mapXY(sLT.x(), sRB.y()),
103                 rt = matrix.mapXY(sRB.x(), sLT.y()),
104                 rb = matrix.mapXY(sRB.x(), sRB.y());
105         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
106         quad[0] = {lt, color, {al, at}};  // L,T
107         quad[1] = {lb, color, {al, ab}};  // L,B
108         quad[2] = {rt, color, {ar, at}};  // R,T
109         quad[3] = {rb, color, {ar, ab}};  // R,B
110     }
111 }
112 
113 template<typename Quad, typename VertexData>
fill_transformed_vertices_3D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,SkScalar dstPadding,SkScalar strikeToSource,GrColor color,const SkMatrix & positionMatrix)114 void fill_transformed_vertices_3D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
115                                   SkScalar dstPadding,
116                                   SkScalar strikeToSource,
117                                   GrColor color,
118                                   const SkMatrix& positionMatrix) {
119     SkPoint inset = {dstPadding, dstPadding};
120     auto mapXYZ = [&](SkScalar x, SkScalar y) {
121         SkPoint pt{x, y};
122         SkPoint3 result;
123         positionMatrix.mapHomogeneousPoints(&result, &pt, 1);
124         return result;
125     };
126     for (auto[quad, glyph, vertexData] : quadData) {
127         auto[pos, rect] = vertexData;
128         auto [l, t, r, b] = rect;
129         SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
130                 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
131         SkPoint3 lt = mapXYZ(sLT.x(), sLT.y()),
132                  lb = mapXYZ(sLT.x(), sRB.y()),
133                  rt = mapXYZ(sRB.x(), sLT.y()),
134                  rb = mapXYZ(sRB.x(), sRB.y());
135         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
136         quad[0] = {lt, color, {al, at}};  // L,T
137         quad[1] = {lb, color, {al, ab}};  // L,B
138         quad[2] = {rt, color, {ar, at}};  // R,T
139         quad[3] = {rb, color, {ar, ab}};  // R,B
140     }
141 }
142 
143 // Check for integer translate with the same 2x2 matrix.
check_integer_translate(const SkMatrix & initialMatrix,const SkMatrix & drawMatrix)144 std::tuple<bool, SkVector> check_integer_translate(
145         const SkMatrix& initialMatrix, const SkMatrix& drawMatrix) {
146     if (initialMatrix.getScaleX() != drawMatrix.getScaleX() ||
147         initialMatrix.getScaleY() != drawMatrix.getScaleY() ||
148         initialMatrix.getSkewX()  != drawMatrix.getSkewX()  ||
149         initialMatrix.getSkewY()  != drawMatrix.getSkewY()) {
150         return {false, {0, 0}};
151     }
152 
153     // We can update the positions in the text blob without regenerating the whole
154     // blob, but only for integer translations.
155     // Calculate the translation in source space to a translation in device space by mapping
156     // (0, 0) through both the initial matrix and the draw matrix; take the difference.
157     SkVector translation = drawMatrix.mapXY(0, 0) - initialMatrix.mapXY(0, 0);
158 
159     return {SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()), translation};
160 }
161 
162 // -- PathSubRun -----------------------------------------------------------------------------------
163 class PathSubRun final : public GrSubRun {
164     struct PathGlyph;
165 
166 public:
167     PathSubRun(bool isAntiAliased,
168                const SkStrikeSpec& strikeSpec,
169                const GrTextBlob& blob,
170                SkSpan<PathGlyph> paths,
171                std::unique_ptr<PathGlyph[], GrSubRunAllocator::ArrayDestroyer> pathData);
172 
173     void draw(const GrClip* clip,
174               const SkMatrixProvider& viewMatrix,
175               const SkGlyphRunList& glyphRunList,
176               const SkPaint& paint,
177               GrSurfaceDrawContext* sdc) const override;
178 
179     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
180 
181     GrAtlasSubRun* testingOnly_atlasSubRun() override;
182 
183     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
184                               bool isAntiAliased,
185                               const SkStrikeSpec& strikeSpec,
186                               const GrTextBlob& blob,
187                               GrSubRunAllocator* alloc);
188 
189 private:
190     struct PathGlyph {
191         PathGlyph(const SkPath& path, SkPoint origin);
192         SkPath fPath;
193         SkPoint fOrigin;
194     };
195 
196     const bool fIsAntiAliased;
197     const SkStrikeSpec fStrikeSpec;
198     const SkSpan<const PathGlyph> fPaths;
199     const std::unique_ptr<PathGlyph[], GrSubRunAllocator::ArrayDestroyer> fPathData;
200 };
201 
PathSubRun(bool isAntiAliased,const SkStrikeSpec & strikeSpec,const GrTextBlob & blob,SkSpan<PathGlyph> paths,std::unique_ptr<PathGlyph[],GrSubRunAllocator::ArrayDestroyer> pathData)202 PathSubRun::PathSubRun(bool isAntiAliased,
203                        const SkStrikeSpec& strikeSpec,
204                        const GrTextBlob& blob,
205                        SkSpan<PathGlyph> paths,
206                        std::unique_ptr<PathGlyph[], GrSubRunAllocator::ArrayDestroyer> pathData)
207     : fIsAntiAliased{isAntiAliased}
208     , fStrikeSpec{strikeSpec}
209     , fPaths{paths}
210     , fPathData{std::move(pathData)} {}
211 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc) const212 void PathSubRun::draw(const GrClip* clip,
213                       const SkMatrixProvider& viewMatrix,
214                       const SkGlyphRunList& glyphRunList,
215                       const SkPaint& paint,
216                       GrSurfaceDrawContext* sdc) const {
217     SkASSERT(!fPaths.empty());
218     SkPoint drawOrigin = glyphRunList.origin();
219     SkPaint runPaint{paint};
220     runPaint.setAntiAlias(fIsAntiAliased);
221     // If there are shaders, blurs or styles, the path must be scaled into source
222     // space independently of the CTM. This allows the CTM to be correct for the
223     // different effects.
224     GrStyle style(runPaint);
225 
226     bool needsExactCTM = runPaint.getShader()
227                          || style.applies()
228                          || runPaint.getMaskFilter();
229 
230     // Calculate the matrix that maps the path glyphs from their size in the strike to
231     // the graphics source space.
232     SkScalar scale = this->fStrikeSpec.strikeToSourceRatio();
233     SkMatrix strikeToSource = SkMatrix::Scale(scale, scale);
234     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
235     if (!needsExactCTM) {
236         for (const auto& pathPos : fPaths) {
237             const SkPath& path = pathPos.fPath;
238             const SkPoint pos = pathPos.fOrigin;  // Transform the glyph to source space.
239             SkMatrix pathMatrix = strikeToSource;
240             pathMatrix.postTranslate(pos.x(), pos.y());
241             SkPreConcatMatrixProvider strikeToDevice(viewMatrix, pathMatrix);
242 
243             GrStyledShape shape(path, paint);
244             GrBlurUtils::drawShapeWithMaskFilter(sdc->recordingContext(), sdc, clip, runPaint,
245                                                  strikeToDevice, shape);
246         }
247     } else {
248         // Transform the path to device because the deviceMatrix must be unchanged to
249         // draw effect, filter or shader paths.
250         for (const auto& pathPos : fPaths) {
251             const SkPath& path = pathPos.fPath;
252             const SkPoint pos = pathPos.fOrigin;
253             // Transform the glyph to source space.
254             SkMatrix pathMatrix = strikeToSource;
255             pathMatrix.postTranslate(pos.x(), pos.y());
256 
257             SkPath deviceOutline;
258             path.transform(pathMatrix, &deviceOutline);
259             deviceOutline.setIsVolatile(true);
260             GrStyledShape shape(deviceOutline, paint);
261             GrBlurUtils::drawShapeWithMaskFilter(sdc->recordingContext(), sdc, clip, runPaint,
262                                                  viewMatrix, shape);
263         }
264     }
265 }
266 
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const267 bool PathSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
268     return true;
269 }
270 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,bool isAntiAliased,const SkStrikeSpec & strikeSpec,const GrTextBlob & blob,GrSubRunAllocator * alloc)271 GrSubRunOwner PathSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
272                                bool isAntiAliased,
273                                const SkStrikeSpec& strikeSpec,
274                                const GrTextBlob& blob,
275                                GrSubRunAllocator* alloc) {
276     auto pathData = alloc->makeUniqueArray<PathGlyph>(
277             drawables.size(),
278             [&](int i){
279                 auto [variant, pos] = drawables[i];
280                 return PathGlyph{*variant.path(), pos};
281             });
282     SkSpan<PathGlyph> paths{pathData.get(), drawables.size()};
283 
284     return alloc->makeUnique<PathSubRun>(
285             isAntiAliased, strikeSpec, blob, paths, std::move(pathData));
286 }
287 
testingOnly_atlasSubRun()288 GrAtlasSubRun* PathSubRun::testingOnly_atlasSubRun() {
289     return nullptr;
290 };
291 
292 // -- PathSubRun::PathGlyph ------------------------------------------------------------------------
PathGlyph(const SkPath & path,SkPoint origin)293 PathSubRun::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
294         : fPath(path)
295         , fOrigin(origin) {}
296 
297 // -- GlyphVector ----------------------------------------------------------------------------------
298 class GlyphVector {
299 public:
300     union Variant {
301         // Initially, filled with packed id, but changed to GrGlyph* in the onPrepare stage.
302         SkPackedGlyphID packedGlyphID;
303         GrGlyph* grGlyph;
304         // Add ctors to help SkArenaAlloc create arrays.
Variant()305         Variant() : grGlyph{nullptr} {}
Variant(SkPackedGlyphID id)306         Variant(SkPackedGlyphID id) : packedGlyphID{id} {}
307     };
308 
309     GlyphVector(const SkStrikeSpec& spec, SkSpan<Variant> glyphs);
310 
311     static GlyphVector Make(
312             const SkStrikeSpec& spec, SkSpan<SkGlyphVariant> glyphs, GrSubRunAllocator* alloc);
313     SkSpan<const GrGlyph*> glyphs() const;
314 
strikeToSourceRatio() const315     SkScalar strikeToSourceRatio() const { return fStrikeSpec.strikeToSourceRatio(); }
316 
317     void packedGlyphIDToGrGlyph(GrStrikeCache* cache);
318 
319     std::tuple<bool, int> regenerateAtlas(
320             int begin, int end,
321             GrMaskFormat maskFormat,
322             int srcPadding,
323             GrMeshDrawOp::Target *target,
324             bool bilerpPadding = false);
325 
GlyphVectorSize(size_t count)326     static size_t GlyphVectorSize(size_t count) {
327         return sizeof(Variant) * count;
328     }
329 
330 private:
331     const SkStrikeSpec fStrikeSpec;
332     SkSpan<Variant> fGlyphs;
333     sk_sp<GrTextStrike> fStrike{nullptr};
334     uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
335     GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
336 };
337 
GlyphVector(const SkStrikeSpec & spec,SkSpan<Variant> glyphs)338 GlyphVector::GlyphVector(const SkStrikeSpec& spec, SkSpan<Variant> glyphs)
339     : fStrikeSpec{spec}
340     , fGlyphs{glyphs} { }
341 
Make(const SkStrikeSpec & spec,SkSpan<SkGlyphVariant> glyphs,GrSubRunAllocator * alloc)342 GlyphVector GlyphVector::Make(
343         const SkStrikeSpec &spec, SkSpan<SkGlyphVariant> glyphs, GrSubRunAllocator* alloc) {
344 
345     Variant* variants = alloc->makePODArray<Variant>(glyphs.size());
346     for (auto [i, gv] : SkMakeEnumerate(glyphs)) {
347         variants[i] = gv.glyph()->getPackedID();
348     }
349 
350     return GlyphVector{spec, SkMakeSpan(variants, glyphs.size())};
351 }
352 
glyphs() const353 SkSpan<const GrGlyph*> GlyphVector::glyphs() const {
354     return SkMakeSpan(reinterpret_cast<const GrGlyph**>(fGlyphs.data()), fGlyphs.size());
355 }
356 
packedGlyphIDToGrGlyph(GrStrikeCache * cache)357 void GlyphVector::packedGlyphIDToGrGlyph(GrStrikeCache* cache) {
358     if (fStrike == nullptr) {
359         fStrike = fStrikeSpec.findOrCreateGrStrike(cache);
360 
361         for (auto& variant : fGlyphs) {
362             variant.grGlyph = fStrike->getGlyph(variant.packedGlyphID);
363         }
364     }
365 }
366 
regenerateAtlas(int begin,int end,GrMaskFormat maskFormat,int srcPadding,GrMeshDrawOp::Target * target,bool bilerpPadding)367 std::tuple<bool, int> GlyphVector::regenerateAtlas(int begin, int end,
368                                                    GrMaskFormat maskFormat,
369                                                    int srcPadding,
370                                                    GrMeshDrawOp::Target* target,
371                                                    bool bilerpPadding) {
372     GrAtlasManager* atlasManager = target->atlasManager();
373     GrDeferredUploadTarget* uploadTarget = target->deferredUploadTarget();
374 
375     uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
376 
377     this->packedGlyphIDToGrGlyph(target->strikeCache());
378 
379     if (fAtlasGeneration != currentAtlasGen) {
380         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
381         // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
382         fBulkUseToken.reset();
383 
384         SkBulkGlyphMetricsAndImages metricsAndImages{fStrikeSpec};
385 
386         // Update the atlas information in the GrStrike.
387         auto tokenTracker = uploadTarget->tokenTracker();
388         auto glyphs = fGlyphs.subspan(begin, end - begin);
389         int glyphsPlacedInAtlas = 0;
390         bool success = true;
391         for (const Variant& variant : glyphs) {
392             GrGlyph* grGlyph = variant.grGlyph;
393             SkASSERT(grGlyph != nullptr);
394 
395             if (!atlasManager->hasGlyph(maskFormat, grGlyph)) {
396                 const SkGlyph& skGlyph = *metricsAndImages.glyph(grGlyph->fPackedID);
397                 auto code = atlasManager->addGlyphToAtlas(
398                         skGlyph, grGlyph, srcPadding, target->resourceProvider(),
399                         uploadTarget, bilerpPadding);
400                 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
401                     success = code != GrDrawOpAtlas::ErrorCode::kError;
402                     break;
403                 }
404             }
405             atlasManager->addGlyphToBulkAndSetUseToken(
406                     &fBulkUseToken, maskFormat, grGlyph,
407                     tokenTracker->nextDrawToken());
408             glyphsPlacedInAtlas++;
409         }
410 
411         // Update atlas generation if there are no more glyphs to put in the atlas.
412         if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
413             // Need to get the freshest value of the atlas' generation because
414             // updateTextureCoordinates may have changed it.
415             fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
416         }
417 
418         return {success, glyphsPlacedInAtlas};
419     } else {
420         // The atlas hasn't changed, so our texture coordinates are still valid.
421         if (end == SkCount(fGlyphs)) {
422             // The atlas hasn't changed and the texture coordinates are all still valid. Update
423             // all the plots used to the new use token.
424             atlasManager->setUseTokenBulk(fBulkUseToken,
425                                           uploadTarget->tokenTracker()->nextDrawToken(),
426                                           maskFormat);
427         }
428         return {true, end - begin};
429     }
430 }
431 
432 // -- DirectMaskSubRun -----------------------------------------------------------------------------
433 class DirectMaskSubRun final : public GrSubRun, public GrAtlasSubRun {
434 public:
435     using DevicePosition = skvx::Vec<2, int16_t>;
436 
437     DirectMaskSubRun(GrMaskFormat format,
438                      GrTextBlob* blob,
439                      const SkGlyphRect& deviceBounds,
440                      SkSpan<const DevicePosition> devicePositions,
441                      GlyphVector glyphs,
442                      bool glyphsOutOfBounds);
443 
444     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
445                               const SkStrikeSpec& strikeSpec,
446                               GrMaskFormat format,
447                               GrTextBlob* blob,
448                               GrSubRunAllocator* alloc);
449 
450     void draw(const GrClip* clip,
451               const SkMatrixProvider& viewMatrix,
452               const SkGlyphRunList& glyphRunList,
453               const SkPaint& paint,
454               GrSurfaceDrawContext* sdc) const override;
455 
456     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
457 
458     GrAtlasSubRun* testingOnly_atlasSubRun() override;
459 
460     size_t vertexStride(const SkMatrix& drawMatrix) const override;
461 
462     int glyphCount() const override;
463 
464     std::tuple<const GrClip*, GrOp::Owner>
465     makeAtlasTextOp(const GrClip* clip,
466                     const SkMatrixProvider& viewMatrix,
467                     const SkGlyphRunList& glyphRunList,
468                     const SkPaint& paint,
469                     GrSurfaceDrawContext* sdc,
470                     GrAtlasSubRunOwner) const override;
471 
472     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
473 
474     std::tuple<bool, int>
475     regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const override;
476 
477     void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
478                         const SkMatrix& positionMatrix, SkIRect clip) const override;
479 private:
480     // The rectangle that surrounds all the glyph bounding boxes in device space.
481     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
482 
483     const GrMaskFormat fMaskFormat;
484     GrTextBlob* const fBlob;
485 
486     // The union of all the glyph bounds in device space.
487     const SkGlyphRect fGlyphDeviceBounds;
488     const SkSpan<const DevicePosition> fLeftTopDevicePos;
489     const bool fSomeGlyphsExcluded;
490 
491     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
492     // be single threaded.
493     mutable GlyphVector fGlyphs;
494 };
495 
DirectMaskSubRun(GrMaskFormat format,GrTextBlob * blob,const SkGlyphRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GlyphVector glyphs,bool glyphsOutOfBounds)496 DirectMaskSubRun::DirectMaskSubRun(GrMaskFormat format,
497                                    GrTextBlob* blob,
498                                    const SkGlyphRect& deviceBounds,
499                                    SkSpan<const DevicePosition> devicePositions,
500                                    GlyphVector glyphs,
501                                    bool glyphsOutOfBounds)
502         : fMaskFormat{format}
503         , fBlob{blob}
504         , fGlyphDeviceBounds{deviceBounds}
505         , fLeftTopDevicePos{devicePositions}
506         , fSomeGlyphsExcluded{glyphsOutOfBounds}
507         , fGlyphs{glyphs} {}
508 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,GrMaskFormat format,GrTextBlob * blob,GrSubRunAllocator * alloc)509 GrSubRunOwner DirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
510                                      const SkStrikeSpec& strikeSpec,
511                                      GrMaskFormat format,
512                                      GrTextBlob* blob,
513                                      GrSubRunAllocator* alloc) {
514     DevicePosition* glyphLeftTop = alloc->makePODArray<DevicePosition>(drawables.size());
515     GlyphVector::Variant* glyphIDs = alloc->makePODArray<GlyphVector::Variant>(drawables.size());
516 
517     // Because this is the direct case, the maximum width or height is the size that fits in the
518     // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
519     // not overflow.
520     constexpr SkScalar kMaxPos =
521             std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
522     SkGlyphRect runBounds = skglyph::empty_rect();
523     size_t goodPosCount = 0;
524     for (auto [variant, pos] : drawables) {
525         auto [x, y] = pos;
526         // Ensure that the .offset() call below does not overflow. And, at this point none of the
527         // rectangles are empty because they were culled before the run was created. Basically,
528         // cull all the glyphs that can't appear on the screen.
529         if (-kMaxPos < x && x < kMaxPos && -kMaxPos  < y && y < kMaxPos) {
530             const SkGlyph* const skGlyph = variant;
531             const SkGlyphRect deviceBounds =
532                     skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
533             runBounds = skglyph::rect_union(runBounds, deviceBounds);
534             glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
535             glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
536             goodPosCount += 1;
537         }
538     }
539 
540     // Wow! no glyphs are in bounds and had non-empty bounds.
541     if (goodPosCount == 0) {
542         return nullptr;
543     }
544 
545     // If some of the glyphs were excluded by the bounds, then this subrun can't be generally be
546     // used for other draws. Mark the subrun as not general.
547     bool glyphsExcluded = goodPosCount != drawables.size();
548     SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
549     return alloc->makeUnique<DirectMaskSubRun>(
550             format, blob, runBounds, leftTop,
551             GlyphVector{strikeSpec, {glyphIDs, goodPosCount}}, glyphsExcluded);
552 }
553 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc) const554 void DirectMaskSubRun::draw(const GrClip* clip, const SkMatrixProvider& viewMatrix,
555                             const SkGlyphRunList& glyphRunList,
556                             const SkPaint& paint,
557                             GrSurfaceDrawContext* sdc) const{
558     auto[drawingClip, op] = this->makeAtlasTextOp(
559             clip, viewMatrix, glyphRunList, paint, sdc, nullptr);
560     if (op != nullptr) {
561         sdc->addDrawOp(drawingClip, std::move(op));
562     }
563 }
564 
565 bool
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const566 DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
567     auto [reuse, translation] = check_integer_translate(fBlob->initialMatrix(), drawMatrix);
568 
569     // If glyphs were excluded because of position bounds, then this subrun can only be reused if
570     // there is no change in position.
571     if (fSomeGlyphsExcluded) {
572         return translation.x() == 0 && translation.y() == 0;
573     }
574 
575     return reuse;
576 }
577 
vertexStride(const SkMatrix &) const578 size_t DirectMaskSubRun::vertexStride(const SkMatrix&) const {
579     if (fMaskFormat != kARGB_GrMaskFormat) {
580         return sizeof(Mask2DVertex);
581     } else {
582         return sizeof(ARGB2DVertex);
583     }
584 }
585 
glyphCount() const586 int DirectMaskSubRun::glyphCount() const {
587     return SkCount(fGlyphs.glyphs());
588 }
589 
590 namespace {
591 enum ClipMethod {
592     kClippedOut,
593     kUnclipped,
594     kGPUClipped,
595     kGeometryClipped
596 };
597 
598 std::tuple<ClipMethod, SkIRect>
calculate_clip(const GrClip * clip,SkRect deviceBounds,SkRect glyphBounds)599 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
600     if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
601         return {kClippedOut, SkIRect::MakeEmpty()};
602     } else if (clip != nullptr) {
603         switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
604             case GrClip::Effect::kClippedOut:
605                 return {kClippedOut, SkIRect::MakeEmpty()};
606             case GrClip::Effect::kUnclipped:
607                 return {kUnclipped, SkIRect::MakeEmpty()};
608             case GrClip::Effect::kClipped: {
609                 if (result.fIsRRect && result.fRRect.isRect()) {
610                     SkRect r = result.fRRect.rect();
611                     if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
612                         SkIRect clipRect = SkIRect::MakeEmpty();
613                         // Clip geometrically during onPrepare using clipRect.
614                         r.round(&clipRect);
615                         if (clipRect.contains(glyphBounds)) {
616                             // If fully within the clip, signal no clipping using the empty rect.
617                             return {kUnclipped, SkIRect::MakeEmpty()};
618                         }
619                         // Use the clipRect to clip the geometry.
620                         return {kGeometryClipped, clipRect};
621                     }
622                     // Partial pixel clipped at this point. Have the GPU handle it.
623                 }
624             }
625             break;
626         }
627     }
628     return {kGPUClipped, SkIRect::MakeEmpty()};
629 }
630 }  // namespace
631 
632 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc,GrAtlasSubRunOwner) const633 DirectMaskSubRun::makeAtlasTextOp(const GrClip* clip, const SkMatrixProvider& viewMatrix,
634                                   const SkGlyphRunList& glyphRunList,
635                                   const SkPaint& paint,
636                                   GrSurfaceDrawContext* sdc,
637                                   GrAtlasSubRunOwner) const {
638     SkASSERT(this->glyphCount() != 0);
639 
640     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
641     const SkPoint drawOrigin = glyphRunList.origin();
642 
643     // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
644     // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
645     // needed.
646     const SkRect subRunBounds = this->deviceRect(drawMatrix, drawOrigin);
647     const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
648     auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunBounds);
649 
650     switch (clipMethod) {
651         case kClippedOut:
652             // Returning nullptr as op means skip this op.
653             return {nullptr, nullptr};
654         case kUnclipped:
655         case kGeometryClipped:
656             // GPU clip is not needed.
657             clip = nullptr;
658             break;
659         case kGPUClipped:
660             // Use the the GPU clip; clipRect is ignored.
661             break;
662     }
663 
664     if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
665 
666     GrPaint grPaint;
667     const SkPMColor4f drawingColor =
668             calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
669 
670     GrAtlasTextOp::Geometry* geometry = GrAtlasTextOp::Geometry::MakeForBlob(
671             *this,
672             drawMatrix,
673             drawOrigin,
674             clipRect,
675             sk_ref_sp<GrTextBlob>(fBlob),
676             drawingColor,
677             sdc->arenaAlloc());
678 
679     GrRecordingContext* const rContext = sdc->recordingContext();
680     GrOp::Owner op = GrOp::Make<GrAtlasTextOp>(rContext,
681                                                op_mask_type(fMaskFormat),
682                                                false,
683                                                this->glyphCount(),
684                                                subRunBounds,
685                                                geometry,
686                                                std::move(grPaint));
687 
688     return {clip, std::move(op)};
689 }
690 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)691 void DirectMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
692     fGlyphs.packedGlyphIDToGrGlyph(cache);
693 }
694 
695 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawOp::Target * target) const696 DirectMaskSubRun::regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const {
697     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
698 }
699 
700 // The 99% case. No clip. Non-color only.
direct_2D(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRun::DevicePosition> quadData,GrColor color,SkIPoint integralOriginOffset)701 void direct_2D(SkZip<Mask2DVertex[4],
702         const GrGlyph*,
703         const DirectMaskSubRun::DevicePosition> quadData,
704                GrColor color,
705                SkIPoint integralOriginOffset) {
706     for (auto[quad, glyph, leftTop] : quadData) {
707         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
708         SkScalar dl = leftTop[0] + integralOriginOffset.x(),
709                  dt = leftTop[1] + integralOriginOffset.y(),
710                  dr = dl + (ar - al),
711                  db = dt + (ab - at);
712 
713         quad[0] = {{dl, dt}, color, {al, at}};  // L,T
714         quad[1] = {{dl, db}, color, {al, ab}};  // L,B
715         quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
716         quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
717     }
718 }
719 
720 template <typename Rect>
ltbr(const Rect & r)721 auto ltbr(const Rect& r) {
722     return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
723 }
724 
725 // Handle any combination of BW or color and clip or no clip.
726 template<typename Quad, typename VertexData>
generalized_direct_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,SkIPoint integralOriginOffset,SkIRect * clip=nullptr)727 void generalized_direct_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
728                            GrColor color,
729                            SkIPoint integralOriginOffset,
730                            SkIRect* clip = nullptr) {
731     for (auto[quad, glyph, leftTop] : quadData) {
732         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
733         uint16_t w = ar - al,
734                  h = ab - at;
735         SkScalar l = (SkScalar)leftTop[0] + integralOriginOffset.x(),
736                  t = (SkScalar)leftTop[1] + integralOriginOffset.y();
737         if (clip == nullptr) {
738             auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
739             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
740             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
741             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
742             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
743         } else {
744             SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
745             SkScalar dl, dt, dr, db;
746             if (!clip->containsNoEmptyCheck(devIRect)) {
747                 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
748                     al += clipped.left()   - devIRect.left();
749                     at += clipped.top()    - devIRect.top();
750                     ar += clipped.right()  - devIRect.right();
751                     ab += clipped.bottom() - devIRect.bottom();
752                     std::tie(dl, dt, dr, db) = ltbr(clipped);
753                 } else {
754                     // TODO: omit generating any vertex data for fully clipped glyphs ?
755                     std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
756                     std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
757                 }
758             } else {
759                 std::tie(dl, dt, dr, db) = ltbr(devIRect);
760             }
761             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
762             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
763             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
764             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
765         }
766     }
767 }
768 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const769 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
770                                       const SkMatrix& positionMatrix, SkIRect clip) const {
771     auto quadData = [&](auto dst) {
772         return SkMakeZip(dst,
773                          fGlyphs.glyphs().subspan(offset, count),
774                          fLeftTopDevicePos.subspan(offset, count));
775     };
776 
777     SkPoint originOffset = positionMatrix.mapOrigin() - fBlob->initialMatrix().mapOrigin();
778     SkIPoint integralOriginOffset =
779             {SkScalarRoundToInt(originOffset.x()), SkScalarRoundToInt(originOffset.y())};
780 
781     if (clip.isEmpty()) {
782         if (fMaskFormat != kARGB_GrMaskFormat) {
783             using Quad = Mask2DVertex[4];
784             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
785             direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
786         } else {
787             using Quad = ARGB2DVertex[4];
788             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
789             generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
790         }
791     } else {
792         if (fMaskFormat != kARGB_GrMaskFormat) {
793             using Quad = Mask2DVertex[4];
794             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
795             generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
796         } else {
797             using Quad = ARGB2DVertex[4];
798             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
799             generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
800         }
801     }
802 }
803 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const804 SkRect DirectMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
805     SkIRect outBounds = fGlyphDeviceBounds.iRect();
806 
807     // Calculate the offset from the initial device origin to the current device origin.
808     SkVector offset = drawMatrix.mapPoint(drawOrigin) - fBlob->initialMatrix().mapOrigin();
809 
810     // The offset should be integer, but make sure.
811     SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
812 
813     return SkRect::Make(outBounds.makeOffset(iOffset));
814 }
815 
testingOnly_atlasSubRun()816 GrAtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() {
817     return this;
818 }
819 
820 // -- TransformedMaskSubRun ------------------------------------------------------------------------
821 class TransformedMaskSubRun final : public GrSubRun, public GrAtlasSubRun {
822 public:
823     struct VertexData {
824         const SkPoint pos;
825         // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
826         // device space rect.
827         GrIRect16 rect;
828     };
829 
830     TransformedMaskSubRun(GrMaskFormat format,
831                           GrTextBlob* blob,
832                           const SkRect& bounds,
833                           SkSpan<const VertexData> vertexData,
834                           GlyphVector glyphs);
835 
836     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
837                               const SkStrikeSpec& strikeSpec,
838                               GrMaskFormat format,
839                               GrTextBlob* blob,
840                               GrSubRunAllocator* alloc);
841 
842     void draw(const GrClip* clip,
843               const SkMatrixProvider& viewMatrix,
844               const SkGlyphRunList& glyphRunList,
845               const SkPaint& paint,
846               GrSurfaceDrawContext* sdc) const override;
847 
848     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
849 
850     GrAtlasSubRun* testingOnly_atlasSubRun() override;
851 
852     std::tuple<const GrClip*, GrOp::Owner>
853     makeAtlasTextOp(const GrClip* clip,
854                     const SkMatrixProvider& viewMatrix,
855                     const SkGlyphRunList& glyphRunList,
856                     const SkPaint& paint,
857                     GrSurfaceDrawContext* sdc,
858                     GrAtlasSubRunOwner) const override;
859 
860     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
861 
862     std::tuple<bool, int> regenerateAtlas(
863             int begin, int end, GrMeshDrawOp::Target* target) const override;
864 
865     void fillVertexData(
866             void* vertexDst, int offset, int count,
867             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
868 
869     size_t vertexStride(const SkMatrix& drawMatrix) const override;
870     int glyphCount() const override;
871 
872 private:
873     // The rectangle that surrounds all the glyph bounding boxes in device space.
874     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
875 
876     const GrMaskFormat fMaskFormat;
877     GrTextBlob* fBlob;
878 
879     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
880     const SkRect fVertexBounds;
881     const SkSpan<const VertexData> fVertexData;
882 
883     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
884     // be single threaded.
885     mutable GlyphVector fGlyphs;
886 };
887 
TransformedMaskSubRun(GrMaskFormat format,GrTextBlob * blob,const SkRect & bounds,SkSpan<const VertexData> vertexData,GlyphVector glyphs)888 TransformedMaskSubRun::TransformedMaskSubRun(GrMaskFormat format,
889                                              GrTextBlob* blob,
890                                              const SkRect& bounds,
891                                              SkSpan<const VertexData> vertexData,
892                                              GlyphVector glyphs)
893         : fMaskFormat{format}
894         , fBlob{blob}
895         , fVertexBounds{bounds}
896         , fVertexData{vertexData}
897         , fGlyphs{glyphs} { }
898 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,GrMaskFormat format,GrTextBlob * blob,GrSubRunAllocator * alloc)899 GrSubRunOwner TransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
900                                           const SkStrikeSpec& strikeSpec,
901                                           GrMaskFormat format,
902                                           GrTextBlob* blob,
903                                           GrSubRunAllocator* alloc) {
904     SkRect bounds = SkRectPriv::MakeLargestInverted();
905 
906     SkScalar strikeToSource = strikeSpec.strikeToSourceRatio();
907     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(
908             drawables,
909             [&](auto e) {
910                 auto [variant, pos] = e;
911                 SkGlyph* skGlyph = variant;
912                 int16_t l = skGlyph->left(),
913                         t = skGlyph->top(),
914                         r = l + skGlyph->width(),
915                         b = t + skGlyph->height();
916                 SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
917                         rb = SkPoint::Make(r, b) * strikeToSource + pos;
918 
919                 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
920                 return VertexData{pos, {l, t, r, b}};
921             });
922 
923     return alloc->makeUnique<TransformedMaskSubRun>(
924             format, blob, bounds, vertexData,
925             GlyphVector::Make(strikeSpec, drawables.get<0>(), alloc));
926 }
927 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc) const928 void TransformedMaskSubRun::draw(const GrClip* clip,
929                                  const SkMatrixProvider& viewMatrix,
930                                  const SkGlyphRunList& glyphRunList,
931                                  const SkPaint& paint,
932                                  GrSurfaceDrawContext* sdc) const {
933     auto[drawingClip, op] = this->makeAtlasTextOp(
934             clip, viewMatrix, glyphRunList, paint, sdc, nullptr);
935     if (op != nullptr) {
936         sdc->addDrawOp(drawingClip, std::move(op));
937     }
938 }
939 
940 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may be
941 // better.
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const942 bool TransformedMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
943     if (fBlob->initialMatrix().getMaxScale() < 1) {
944         return false;
945     }
946     return true;
947 }
948 
949 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc,GrAtlasSubRunOwner) const950 TransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
951                                        const SkMatrixProvider& viewMatrix,
952                                        const SkGlyphRunList& glyphRunList,
953                                        const SkPaint& paint,
954                                        GrSurfaceDrawContext* sdc,
955                                        GrAtlasSubRunOwner) const {
956     SkASSERT(this->glyphCount() != 0);
957 
958     SkPoint drawOrigin = glyphRunList.origin();
959     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
960 
961     GrPaint grPaint;
962     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
963 
964     GrAtlasTextOp::Geometry* geometry = GrAtlasTextOp::Geometry::MakeForBlob(
965             *this,
966             drawMatrix,
967             drawOrigin,
968             SkIRect::MakeEmpty(),
969             sk_ref_sp<GrTextBlob>(fBlob),
970             drawingColor,
971             sdc->arenaAlloc());
972 
973     GrRecordingContext* const rContext = sdc->recordingContext();
974     GrOp::Owner op = GrOp::Make<GrAtlasTextOp>(
975             rContext,
976             op_mask_type(fMaskFormat),
977             true,
978             this->glyphCount(),
979             this->deviceRect(drawMatrix, drawOrigin),
980             geometry,
981             std::move(grPaint));
982     return {clip, std::move(op)};
983 }
984 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)985 void TransformedMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
986     fGlyphs.packedGlyphIDToGrGlyph(cache);
987 }
988 
regenerateAtlas(int begin,int end,GrMeshDrawOp::Target * target) const989 std::tuple<bool, int> TransformedMaskSubRun::regenerateAtlas(int begin, int end,
990                                                              GrMeshDrawOp::Target* target) const {
991     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
992 }
993 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const994 void TransformedMaskSubRun::fillVertexData(
995         void* vertexDst, int offset, int count, GrColor color, const SkMatrix& positionMatrix,
996         SkIRect clip) const {
997     constexpr SkScalar kDstPadding = 0.f;
998 
999     auto quadData = [&](auto dst) {
1000         return SkMakeZip(dst,
1001                          fGlyphs.glyphs().subspan(offset, count),
1002                          fVertexData.subspan(offset, count));
1003     };
1004 
1005     if (!positionMatrix.hasPerspective()) {
1006         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
1007             using Quad = ARGB2DVertex[4];
1008             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1009             fill_transformed_vertices_2D(
1010                     quadData((Quad*) vertexDst),
1011                     kDstPadding,
1012                     fGlyphs.strikeToSourceRatio(),
1013                     color,
1014                     positionMatrix);
1015         } else {
1016             using Quad = Mask2DVertex[4];
1017             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1018             fill_transformed_vertices_2D(
1019                     quadData((Quad*) vertexDst),
1020                     kDstPadding,
1021                     fGlyphs.strikeToSourceRatio(),
1022                     color,
1023                     positionMatrix);
1024         }
1025     } else {
1026         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
1027             using Quad = ARGB3DVertex[4];
1028             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1029             fill_transformed_vertices_3D(
1030                     quadData((Quad*) vertexDst),
1031                     kDstPadding,
1032                     fGlyphs.strikeToSourceRatio(),
1033                     color,
1034                     positionMatrix);
1035         } else {
1036             using Quad = Mask3DVertex[4];
1037             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1038             fill_transformed_vertices_3D(
1039                     quadData((Quad*) vertexDst),
1040                     kDstPadding,
1041                     fGlyphs.strikeToSourceRatio(),
1042                     color,
1043                     positionMatrix);
1044         }
1045     }
1046 }
1047 
vertexStride(const SkMatrix & drawMatrix) const1048 size_t TransformedMaskSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1049     switch (fMaskFormat) {
1050         case kA8_GrMaskFormat:
1051             return drawMatrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
1052         case kARGB_GrMaskFormat:
1053             return drawMatrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
1054         default:
1055             SkASSERT(!drawMatrix.hasPerspective());
1056             return sizeof(Mask2DVertex);
1057     }
1058     SkUNREACHABLE;
1059 }
1060 
glyphCount() const1061 int TransformedMaskSubRun::glyphCount() const {
1062     return SkCount(fVertexData);
1063 }
1064 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1065 SkRect TransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1066     SkRect outBounds = fVertexBounds;
1067     outBounds.offset(drawOrigin);
1068     return drawMatrix.mapRect(outBounds);
1069 }
1070 
testingOnly_atlasSubRun()1071 GrAtlasSubRun* TransformedMaskSubRun::testingOnly_atlasSubRun() {
1072     return this;
1073 }
1074 
1075 // -- SDFTSubRun -----------------------------------------------------------------------------------
1076 class SDFTSubRun final : public GrSubRun, public GrAtlasSubRun {
1077 public:
1078     struct VertexData {
1079         const SkPoint pos;
1080         // The rectangle of the glyphs in strike space.
1081         GrIRect16 rect;
1082     };
1083 
1084     SDFTSubRun(GrMaskFormat format,
1085                GrTextBlob* blob,
1086                SkRect vertexBounds,
1087                SkSpan<const VertexData> vertexData,
1088                GlyphVector glyphs,
1089                bool useLCDText,
1090                bool antiAliased);
1091 
1092     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1093                               const SkFont& runFont,
1094                               const SkStrikeSpec& strikeSpec,
1095                               GrTextBlob* blob,
1096                               GrSubRunAllocator* alloc);
1097 
1098     void draw(const GrClip* clip,
1099               const SkMatrixProvider& viewMatrix,
1100               const SkGlyphRunList& glyphRunList,
1101               const SkPaint& paint,
1102               GrSurfaceDrawContext* sdc) const override;
1103 
1104     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
1105 
1106     GrAtlasSubRun* testingOnly_atlasSubRun() override;
1107 
1108     std::tuple<const GrClip*, GrOp::Owner>
1109     makeAtlasTextOp(const GrClip* clip,
1110                     const SkMatrixProvider& viewMatrix,
1111                     const SkGlyphRunList& glyphRunList,
1112                     const SkPaint& paint,
1113                     GrSurfaceDrawContext* sdc,
1114                     GrAtlasSubRunOwner) const override;
1115 
1116     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1117 
1118     std::tuple<bool, int> regenerateAtlas(
1119             int begin, int end, GrMeshDrawOp::Target* target) const override;
1120 
1121     void fillVertexData(
1122             void* vertexDst, int offset, int count,
1123             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
1124 
1125     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1126     int glyphCount() const override;
1127 
1128 private:
1129     // The rectangle that surrounds all the glyph bounding boxes in device space.
1130     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1131 
1132     const GrMaskFormat fMaskFormat;
1133     GrTextBlob* fBlob;
1134 
1135     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1136     const SkRect fVertexBounds;
1137     const SkSpan<const VertexData> fVertexData;
1138 
1139     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1140     // be single threaded.
1141     mutable GlyphVector fGlyphs;
1142 
1143     const bool fUseLCDText;
1144     const bool fAntiAliased;
1145 };
1146 
SDFTSubRun(GrMaskFormat format,GrTextBlob * textBlob,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GlyphVector glyphs,bool useLCDText,bool antiAliased)1147 SDFTSubRun::SDFTSubRun(GrMaskFormat format,
1148                        GrTextBlob* textBlob,
1149                        SkRect vertexBounds,
1150                        SkSpan<const VertexData> vertexData,
1151                        GlyphVector glyphs,
1152                        bool useLCDText,
1153                        bool antiAliased)
1154         : fMaskFormat{format}
1155         , fBlob{textBlob}
1156         , fVertexBounds{vertexBounds}
1157         , fVertexData{vertexData}
1158         , fGlyphs{glyphs}
1159         , fUseLCDText{useLCDText}
1160         , fAntiAliased{antiAliased} { }
1161 
has_some_antialiasing(const SkFont & font)1162 bool has_some_antialiasing(const SkFont& font ) {
1163     SkFont::Edging edging = font.getEdging();
1164     return edging == SkFont::Edging::kAntiAlias
1165            || edging == SkFont::Edging::kSubpixelAntiAlias;
1166 }
1167 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,const SkStrikeSpec & strikeSpec,GrTextBlob * blob,GrSubRunAllocator * alloc)1168 GrSubRunOwner SDFTSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1169                                const SkFont& runFont,
1170                                const SkStrikeSpec& strikeSpec,
1171                                GrTextBlob* blob,
1172                                GrSubRunAllocator* alloc) {
1173     SkRect bounds = SkRectPriv::MakeLargestInverted();
1174     auto mapper = [&, strikeToSource=strikeSpec.strikeToSourceRatio()](const auto& d) {
1175         auto& [variant, pos] = d;
1176         SkGlyph* skGlyph = variant;
1177         int16_t l = skGlyph->left(),
1178                 t = skGlyph->top(),
1179                 r = l + skGlyph->width(),
1180                 b = t + skGlyph->height();
1181         SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
1182                 rb = SkPoint::Make(r, b) * strikeToSource + pos;
1183 
1184         bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1185         return VertexData{pos, {l, t, r, b}};
1186     };
1187 
1188     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, mapper);
1189 
1190     return alloc->makeUnique<SDFTSubRun>(
1191             kA8_GrMaskFormat,
1192             blob,
1193             bounds,
1194             vertexData,
1195             GlyphVector::Make(strikeSpec, drawables.get<0>(), alloc),
1196             runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1197             has_some_antialiasing(runFont));
1198 }
1199 
calculate_sdf_parameters(const GrSurfaceDrawContext & sdc,const SkMatrix & drawMatrix,bool useLCDText,bool isAntiAliased)1200 static std::tuple<GrAtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1201         const GrSurfaceDrawContext& sdc,
1202         const SkMatrix& drawMatrix,
1203         bool useLCDText,
1204         bool isAntiAliased) {
1205     const GrColorInfo& colorInfo = sdc.colorInfo();
1206     const SkSurfaceProps& props = sdc.surfaceProps();
1207     bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1208     bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1209     using MT = GrAtlasTextOp::MaskType;
1210     MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1211                                  : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1212                                                   : MT::kLCDDistanceField)
1213                                          : MT::kGrayscaleDistanceField;
1214 
1215     bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1216     uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1217     DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1218     DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1219     DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1220 
1221     if (isLCD) {
1222         DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1223         DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1224     }
1225     return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1226 }
1227 
1228 std::tuple<const GrClip*, GrOp::Owner >
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc,GrAtlasSubRunOwner) const1229 SDFTSubRun::makeAtlasTextOp(const GrClip* clip,
1230                             const SkMatrixProvider& viewMatrix,
1231                             const SkGlyphRunList& glyphRunList,
1232                             const SkPaint& paint,
1233                             GrSurfaceDrawContext* sdc,
1234                             GrAtlasSubRunOwner) const {
1235     SkASSERT(this->glyphCount() != 0);
1236     SkASSERT(!viewMatrix.localToDevice().hasPerspective());
1237 
1238     SkPoint drawOrigin = glyphRunList.origin();
1239     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1240 
1241     GrPaint grPaint;
1242     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1243 
1244     auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1245         calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
1246 
1247     GrAtlasTextOp::Geometry* geometry = GrAtlasTextOp::Geometry::MakeForBlob(
1248             *this,
1249             drawMatrix,
1250             drawOrigin,
1251             SkIRect::MakeEmpty(),
1252             sk_ref_sp<GrTextBlob>(fBlob),
1253             drawingColor,
1254             sdc->arenaAlloc());
1255 
1256     GrRecordingContext* const rContext = sdc->recordingContext();
1257     GrOp::Owner op = GrOp::Make<GrAtlasTextOp>(
1258             rContext,
1259             maskType,
1260             true,
1261             this->glyphCount(),
1262             this->deviceRect(drawMatrix, drawOrigin),
1263             SkPaintPriv::ComputeLuminanceColor(paint),
1264             useGammaCorrectDistanceTable,
1265             DFGPFlags,
1266             geometry,
1267             std::move(grPaint));
1268 
1269     return {clip, std::move(op)};
1270 }
1271 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc) const1272 void SDFTSubRun::draw(const GrClip* clip,
1273                       const SkMatrixProvider& viewMatrix,
1274                       const SkGlyphRunList& glyphRunList,
1275                       const SkPaint& paint,
1276                       GrSurfaceDrawContext* sdc) const {
1277     auto[drawingClip, op] = this->makeAtlasTextOp(
1278             clip, viewMatrix, glyphRunList, paint, sdc, nullptr);
1279     if (op != nullptr) {
1280         sdc->addDrawOp(drawingClip, std::move(op));
1281     }
1282 }
1283 
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1284 bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1285     const SkMatrix& initialMatrix = fBlob->initialMatrix();
1286 
1287     // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
1288     // distance field being generated, so we have to regenerate in those cases
1289     SkScalar newMaxScale = drawMatrix.getMaxScale();
1290     SkScalar oldMaxScale = initialMatrix.getMaxScale();
1291     SkScalar scaleAdjust = newMaxScale / oldMaxScale;
1292     auto [maxMinScale, minMaxScale] = fBlob->scaleBounds();
1293     if (scaleAdjust < maxMinScale || scaleAdjust > minMaxScale) {
1294         return false;
1295     }
1296     return true;
1297 }
1298 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1299 void SDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1300     fGlyphs.packedGlyphIDToGrGlyph(cache);
1301 }
1302 
regenerateAtlas(int begin,int end,GrMeshDrawOp::Target * target) const1303 std::tuple<bool, int> SDFTSubRun::regenerateAtlas(
1304         int begin, int end, GrMeshDrawOp::Target *target) const {
1305 
1306     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
1307 }
1308 
vertexStride(const SkMatrix & drawMatrix) const1309 size_t SDFTSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1310     return sizeof(Mask2DVertex);
1311 }
1312 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1313 void SDFTSubRun::fillVertexData(
1314         void *vertexDst, int offset, int count,
1315         GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const {
1316 
1317     using Quad = Mask2DVertex[4];
1318     SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1319     fill_transformed_vertices_2D(
1320             SkMakeZip((Quad*)vertexDst,
1321                       fGlyphs.glyphs().subspan(offset, count),
1322                       fVertexData.subspan(offset, count)),
1323             SK_DistanceFieldInset,
1324             fGlyphs.strikeToSourceRatio(),
1325             color,
1326             positionMatrix);
1327 }
1328 
glyphCount() const1329 int SDFTSubRun::glyphCount() const {
1330     return SkCount(fVertexData);
1331 }
1332 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1333 SkRect SDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1334     SkRect outBounds = fVertexBounds;
1335     outBounds.offset(drawOrigin);
1336     return drawMatrix.mapRect(outBounds);
1337 }
1338 
testingOnly_atlasSubRun()1339 GrAtlasSubRun* SDFTSubRun::testingOnly_atlasSubRun() {
1340     return this;
1341 }
1342 }  // namespace
1343 
1344 // -- GrTextBlob::Key ------------------------------------------------------------------------------
1345 
compute_canonical_color(const SkPaint & paint,bool lcd)1346 static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
1347     SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint);
1348     if (lcd) {
1349         // This is the correct computation for canonicalColor, but there are tons of cases where LCD
1350         // can be modified. For now we just regenerate if any run in a textblob has LCD.
1351         // TODO figure out where all of these modifications are and see if we can incorporate that
1352         //      logic at a higher level *OR* use sRGB
1353         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
1354 
1355         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
1356         // see the note above.  We pick a placeholder value for LCD text to ensure we always match
1357         // the same key
1358         return SK_ColorTRANSPARENT;
1359     } else {
1360         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
1361         // gamma corrected masks anyways, nor color
1362         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
1363                                        SkColorGetG(canonicalColor),
1364                                        SkColorGetB(canonicalColor));
1365         // reduce to our finite number of bits
1366         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
1367     }
1368     return canonicalColor;
1369 }
1370 
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const GrColorInfo & colorInfo,const SkMatrix & drawMatrix,const GrSDFTControl & control)1371 auto GrTextBlob::Key::Make(const SkGlyphRunList& glyphRunList,
1372                            const SkPaint& paint,
1373                            const SkSurfaceProps& surfaceProps,
1374                            const GrColorInfo& colorInfo,
1375                            const SkMatrix& drawMatrix,
1376                            const GrSDFTControl& control) -> std::tuple<bool, Key> {
1377     SkMaskFilterBase::BlurRec blurRec;
1378     // It might be worth caching these things, but its not clear at this time
1379     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
1380     const SkMaskFilter* maskFilter = paint.getMaskFilter();
1381     bool canCache = glyphRunList.canCache() &&
1382                     !(paint.getPathEffect() ||
1383                         (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
1384 
1385     // If we're doing linear blending, then we can disable the gamma hacks.
1386     // Otherwise, leave them on. In either case, we still want the contrast boost:
1387     // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
1388     SkScalerContextFlags scalerContextFlags = colorInfo.isLinearlyBlended()
1389                                               ? SkScalerContextFlags::kBoostContrast
1390                                               : SkScalerContextFlags::kFakeGammaAndBoostContrast;
1391 
1392     GrTextBlob::Key key;
1393     if (canCache) {
1394         bool hasLCD = glyphRunList.anyRunsLCD();
1395 
1396         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
1397         SkPixelGeometry pixelGeometry =
1398                 hasLCD ? surfaceProps.pixelGeometry() : kUnknown_SkPixelGeometry;
1399 
1400         GrColor canonicalColor = compute_canonical_color(paint, hasLCD);
1401 
1402         key.fPixelGeometry = pixelGeometry;
1403         key.fUniqueID = glyphRunList.uniqueID();
1404         key.fStyle = paint.getStyle();
1405         if (key.fStyle != SkPaint::kFill_Style) {
1406             key.fFrameWidth = paint.getStrokeWidth();
1407             key.fMiterLimit = paint.getStrokeMiter();
1408             key.fJoin = paint.getStrokeJoin();
1409         }
1410         key.fHasBlur = maskFilter != nullptr;
1411         if (key.fHasBlur) {
1412             key.fBlurRec = blurRec;
1413         }
1414         key.fCanonicalColor = canonicalColor;
1415         key.fScalerContextFlags = scalerContextFlags;
1416 
1417         // Calculate the set of drawing types.
1418         key.fSetOfDrawingTypes = 0;
1419         for (auto& run : glyphRunList) {
1420             key.fSetOfDrawingTypes |= control.drawingType(run.font(), paint, drawMatrix);
1421         }
1422 
1423         if (key.fSetOfDrawingTypes & GrSDFTControl::kDirect) {
1424             // Store the fractional offset of the position. We know that the matrix can't be
1425             // perspective at this point.
1426             SkPoint mappedOrigin = drawMatrix.mapOrigin();
1427             key.fDrawMatrix = drawMatrix;
1428             key.fDrawMatrix.setTranslateX(
1429                     mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
1430             key.fDrawMatrix.setTranslateY(
1431                     mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
1432         } else {
1433             // For path and SDFT, the matrix doesn't matter.
1434             key.fDrawMatrix = SkMatrix::I();
1435         }
1436     }
1437 
1438     return {canCache, key};
1439 }
1440 
operator ==(const GrTextBlob::Key & that) const1441 bool GrTextBlob::Key::operator==(const GrTextBlob::Key& that) const {
1442     if (fUniqueID != that.fUniqueID) { return false; }
1443     if (fCanonicalColor != that.fCanonicalColor) { return false; }
1444     if (fStyle != that.fStyle) { return false; }
1445     if (fStyle != SkPaint::kFill_Style) {
1446         if (fFrameWidth != that.fFrameWidth ||
1447             fMiterLimit != that.fMiterLimit ||
1448             fJoin != that.fJoin) {
1449             return false;
1450         }
1451     }
1452     if (fPixelGeometry != that.fPixelGeometry) { return false; }
1453     if (fHasBlur != that.fHasBlur) { return false; }
1454     if (fHasBlur) {
1455         if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
1456             return false;
1457         }
1458     }
1459     if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
1460 
1461     // Just punt on perspective.
1462     if (fDrawMatrix.hasPerspective()) {
1463         return false;
1464     }
1465 
1466     if (fSetOfDrawingTypes != that.fSetOfDrawingTypes) {
1467         return false;
1468     }
1469 
1470     if (fSetOfDrawingTypes & GrSDFTControl::kDirect) {
1471         auto [compatible, _] = check_integer_translate(fDrawMatrix, that.fDrawMatrix);
1472         return compatible;
1473     }
1474 
1475     return true;
1476 }
1477 
1478 // -- GrTextBlob -----------------------------------------------------------------------------------
operator delete(void * p)1479 void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
operator new(size_t)1480 void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
operator new(size_t,void * p)1481 void* GrTextBlob::operator new(size_t, void* p) { return p; }
1482 
1483 GrTextBlob::~GrTextBlob() = default;
1484 
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkMatrix & drawMatrix,const GrSDFTControl & control,SkGlyphRunListPainter * painter)1485 sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
1486                                    const SkPaint& paint,
1487                                    const SkMatrix& drawMatrix,
1488                                    const GrSDFTControl& control,
1489                                    SkGlyphRunListPainter* painter) {
1490     // The difference in alignment from the per-glyph data to the SubRun;
1491     constexpr size_t alignDiff =
1492             alignof(DirectMaskSubRun) - alignof(DirectMaskSubRun::DevicePosition);
1493     constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
1494     size_t totalGlyphCount = glyphRunList.totalGlyphCount();
1495 
1496     // The neededForSubRun is optimized for DirectMaskSubRun which is by far the most common case.
1497     size_t bytesNeededForSubRun = GrBagOfBytes::PlatformMinimumSizeWithOverhead(
1498             totalGlyphCount * sizeof(DirectMaskSubRun::DevicePosition)
1499             + GlyphVector::GlyphVectorSize(totalGlyphCount)
1500             + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding),
1501             alignof(GrTextBlob));
1502 
1503     size_t allocationSize = sizeof(GrTextBlob) + bytesNeededForSubRun;
1504 
1505     void* allocation = ::operator new (allocationSize);
1506 
1507     SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(paint);
1508     sk_sp<GrTextBlob> blob{new (allocation)
1509                             GrTextBlob(bytesNeededForSubRun, drawMatrix, initialLuminance)};
1510 
1511     for (auto& glyphRun : glyphRunList) {
1512         painter->processGlyphRun(glyphRun,
1513                                  drawMatrix,
1514                                  paint,
1515                                  control,
1516                                  blob.get(),
1517                                  "GrTextBlob");
1518     }
1519 
1520     return blob;
1521 }
1522 
addKey(const Key & key)1523 void GrTextBlob::addKey(const Key& key) {
1524     fKey = key;
1525 }
1526 
hasPerspective() const1527 bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
1528 
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1529 bool GrTextBlob::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1530     // A singular matrix will create a GrTextBlob with no SubRuns, but unknown glyphs can
1531     // also cause empty runs. If there are no subRuns or some glyphs were excluded or perspective,
1532     // then regenerate when the matrices don't match.
1533     if ((fSubRunList.isEmpty() || fSomeGlyphsExcluded || hasPerspective()) &&
1534          fInitialMatrix != drawMatrix)
1535     {
1536         return false;
1537     }
1538 
1539     // If we have LCD text then our canonical color will be set to transparent, in this case we have
1540     // to regenerate the blob on any color change
1541     // We use the grPaint to get any color filter effects
1542     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
1543         fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
1544         return false;
1545     }
1546 
1547     for (const GrSubRun& subRun : fSubRunList) {
1548         if (!subRun.canReuse(paint, drawMatrix)) {
1549             return false;
1550         }
1551     }
1552 
1553     return true;
1554 }
1555 
key() const1556 const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
size() const1557 size_t GrTextBlob::size() const { return fSize; }
1558 
1559 template<typename AddSingleMaskFormat>
addMultiMaskFormat(AddSingleMaskFormat addSingle,const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)1560 void GrTextBlob::addMultiMaskFormat(
1561         AddSingleMaskFormat addSingle,
1562         const SkZip<SkGlyphVariant, SkPoint>& drawables,
1563         const SkStrikeSpec& strikeSpec) {
1564     if (drawables.empty()) { return; }
1565 
1566     auto addSameFormat = [&](const SkZip<SkGlyphVariant, SkPoint>& drawable, GrMaskFormat format) {
1567         GrSubRunOwner subRun = addSingle(drawable, strikeSpec, format, this, &fAlloc);
1568         if (subRun != nullptr) {
1569             fSubRunList.append(std::move(subRun));
1570         } else {
1571             fSomeGlyphsExcluded = true;
1572         }
1573     };
1574 
1575     auto glyphSpan = drawables.get<0>();
1576     SkGlyph* glyph = glyphSpan[0];
1577     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1578     size_t startIndex = 0;
1579     for (size_t i = 1; i < drawables.size(); i++) {
1580         glyph = glyphSpan[i];
1581         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1582         if (format != nextFormat) {
1583             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
1584             addSameFormat(sameFormat, format);
1585             format = nextFormat;
1586             startIndex = i;
1587         }
1588     }
1589     auto sameFormat = drawables.last(drawables.size() - startIndex);
1590     addSameFormat(sameFormat, format);
1591 }
1592 
GrTextBlob(int allocSize,const SkMatrix & drawMatrix,SkColor initialLuminance)1593 GrTextBlob::GrTextBlob(int allocSize,
1594                        const SkMatrix& drawMatrix,
1595                        SkColor initialLuminance)
1596         : fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2}
1597         , fSize{allocSize}
1598         , fInitialMatrix{drawMatrix}
1599         , fInitialLuminance{initialLuminance} { }
1600 
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)1601 void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1602                                     const SkStrikeSpec& strikeSpec) {
1603 
1604     this->addMultiMaskFormat(DirectMaskSubRun::Make, drawables, strikeSpec);
1605 }
1606 
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,const SkStrikeSpec & strikeSpec)1607 void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1608                                     const SkFont& runFont,
1609                                     const SkStrikeSpec& strikeSpec) {
1610     fSubRunList.append(PathSubRun::Make(drawables,
1611                                         has_some_antialiasing(runFont),
1612                                         strikeSpec,
1613                                         *this,
1614                                         &fAlloc));
1615 }
1616 
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)1617 void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1618                                    const SkStrikeSpec& strikeSpec,
1619                                    const SkFont& runFont,
1620                                    SkScalar minScale,
1621                                    SkScalar maxScale) {
1622 
1623     fMaxMinScale = std::max(minScale, fMaxMinScale);
1624     fMinMaxScale = std::min(maxScale, fMinMaxScale);
1625     fSubRunList.append(SDFTSubRun::Make(drawables, runFont, strikeSpec, this, &fAlloc));
1626 }
1627 
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)1628 void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1629                                     const SkStrikeSpec& strikeSpec) {
1630     this->addMultiMaskFormat(TransformedMaskSubRun::Make, drawables, strikeSpec);
1631 }
1632 
1633 // ----------------------------- Begin no cache implementation -------------------------------------
1634 namespace {
1635 // -- DirectMaskSubRunNoCache ----------------------------------------------------------------------
1636 class DirectMaskSubRunNoCache final : public GrAtlasSubRun {
1637 public:
1638     using DevicePosition = skvx::Vec<2, int16_t>;
1639 
1640     DirectMaskSubRunNoCache(GrMaskFormat format,
1641                             const SkRect& bounds,
1642                             SkSpan<const DevicePosition> devicePositions,
1643                             GlyphVector glyphs);
1644 
1645     static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1646                                    const SkStrikeSpec& strikeSpec,
1647                                    GrMaskFormat format,
1648                                    GrSubRunAllocator* alloc);
1649 
1650     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1651 
1652     int glyphCount() const override;
1653 
1654     std::tuple<const GrClip*, GrOp::Owner>
1655     makeAtlasTextOp(const GrClip* clip,
1656                     const SkMatrixProvider& viewMatrix,
1657                     const SkGlyphRunList& glyphRunList,
1658                     const SkPaint& paint,
1659                     GrSurfaceDrawContext* sdc,
1660                     GrAtlasSubRunOwner subRunOwner) const override;
1661 
1662     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1663 
1664     std::tuple<bool, int>
1665     regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const override;
1666 
1667     void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
1668                         const SkMatrix& positionMatrix, SkIRect clip) const override;
1669 
1670 private:
1671     const GrMaskFormat fMaskFormat;
1672     // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1673     const SkRect fGlyphDeviceBounds;
1674     const SkSpan<const DevicePosition> fLeftTopDevicePos;
1675 
1676     // Space for geometry
1677     alignas(alignof(GrAtlasTextOp::Geometry)) char fGeom[sizeof(GrAtlasTextOp::Geometry)];
1678 
1679     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1680     // be single threaded.
1681     mutable GlyphVector fGlyphs;
1682 };
1683 
DirectMaskSubRunNoCache(GrMaskFormat format,const SkRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GlyphVector glyphs)1684 DirectMaskSubRunNoCache::DirectMaskSubRunNoCache(GrMaskFormat format,
1685                                                  const SkRect& deviceBounds,
1686                                                  SkSpan<const DevicePosition> devicePositions,
1687                                                  GlyphVector glyphs)
1688         : fMaskFormat{format}
1689         , fGlyphDeviceBounds{deviceBounds}
1690         , fLeftTopDevicePos{devicePositions}
1691         , fGlyphs{glyphs} {}
1692 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,GrMaskFormat format,GrSubRunAllocator * alloc)1693 GrAtlasSubRunOwner DirectMaskSubRunNoCache::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1694                                                  const SkStrikeSpec& strikeSpec,
1695                                                  GrMaskFormat format,
1696                                                  GrSubRunAllocator* alloc) {
1697     DevicePosition* glyphLeftTop = alloc->makePODArray<DevicePosition>(drawables.size());
1698 
1699     GlyphVector::Variant* glyphIDs = static_cast<GlyphVector::Variant*>(
1700             alloc->alignedBytes(drawables.size() * sizeof(GlyphVector::Variant),
1701                                 alignof(GlyphVector::Variant)));
1702 
1703     // Because this is the direct case, the maximum width or height is the size that fits in the
1704     // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
1705     // not overflow.
1706     constexpr SkScalar kMaxPos =
1707             std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
1708     SkGlyphRect runBounds = skglyph::empty_rect();
1709     size_t goodPosCount = 0;
1710     for (auto [variant, pos] : drawables) {
1711         auto [x, y] = pos;
1712         // Ensure that the .offset() call below does not overflow. And, at this point none of the
1713         // rectangles are empty because they were culled before the run was created. Basically,
1714         // cull all the glyphs that can't appear on the screen.
1715         if (-kMaxPos < x && x < kMaxPos && -kMaxPos  < y && y < kMaxPos) {
1716             const SkGlyph* const skGlyph = variant;
1717             const SkGlyphRect deviceBounds =
1718                     skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
1719             runBounds = skglyph::rect_union(runBounds, deviceBounds);
1720             glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
1721             glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
1722             goodPosCount += 1;
1723         }
1724     }
1725 
1726     // Wow! no glyphs are in bounds and had non-empty bounds.
1727     if (goodPosCount == 0) {
1728         return nullptr;
1729     }
1730 
1731     SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
1732     return alloc->makeUnique<DirectMaskSubRunNoCache>(
1733             format, runBounds.rect(), leftTop,
1734             GlyphVector{strikeSpec, {glyphIDs, goodPosCount}});
1735 }
1736 
vertexStride(const SkMatrix &) const1737 size_t DirectMaskSubRunNoCache::vertexStride(const SkMatrix&) const {
1738     if (fMaskFormat != kARGB_GrMaskFormat) {
1739         return sizeof(Mask2DVertex);
1740     } else {
1741         return sizeof(ARGB2DVertex);
1742     }
1743 }
1744 
glyphCount() const1745 int DirectMaskSubRunNoCache::glyphCount() const {
1746     return SkCount(fGlyphs.glyphs());
1747 }
1748 
1749 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const1750 DirectMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
1751                                          const SkMatrixProvider& viewMatrix,
1752                                          const SkGlyphRunList& glyphRunList,
1753                                          const SkPaint& paint,
1754                                          GrSurfaceDrawContext* sdc,
1755                                          GrAtlasSubRunOwner subRunOwner) const {
1756     SkASSERT(this->glyphCount() != 0);
1757 
1758     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1759     const SkPoint drawOrigin = glyphRunList.origin();
1760 
1761     // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
1762     // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
1763     // needed.
1764     const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
1765     auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, fGlyphDeviceBounds);
1766 
1767     switch (clipMethod) {
1768         case kClippedOut:
1769             // Returning nullptr as op means skip this op.
1770             return {nullptr, nullptr};
1771         case kUnclipped:
1772         case kGeometryClipped:
1773             // GPU clip is not needed.
1774             clip = nullptr;
1775             break;
1776         case kGPUClipped:
1777             // Use the the GPU clip; clipRect is ignored.
1778             break;
1779     }
1780 
1781     if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
1782 
1783     GrPaint grPaint;
1784     const SkPMColor4f drawingColor =
1785             calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1786 
1787     GrRecordingContext* const rContext = sdc->recordingContext();
1788 
1789     GrAtlasTextOp::Geometry* geometry = new ((void*)fGeom) GrAtlasTextOp::Geometry{
1790             *this,
1791             drawMatrix,
1792             drawOrigin,
1793             clipRect,
1794             nullptr,
1795             std::move(subRunOwner),
1796             drawingColor
1797     };
1798 
1799     GrOp::Owner op = GrOp::Make<GrAtlasTextOp>(rContext,
1800                                                op_mask_type(fMaskFormat),
1801                                                false,
1802                                                this->glyphCount(),
1803                                                fGlyphDeviceBounds,
1804                                                geometry,
1805                                                std::move(grPaint));
1806 
1807     return {clip, std::move(op)};
1808 }
1809 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1810 void DirectMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1811     fGlyphs.packedGlyphIDToGrGlyph(cache);
1812 }
1813 
1814 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawOp::Target * target) const1815 DirectMaskSubRunNoCache::regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const {
1816     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
1817 }
1818 
1819 // The 99% case. No clip. Non-color only.
direct_2D2(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRunNoCache::DevicePosition> quadData,GrColor color)1820 void direct_2D2(SkZip<Mask2DVertex[4],
1821         const GrGlyph*,
1822         const DirectMaskSubRunNoCache::DevicePosition> quadData,
1823         GrColor color) {
1824     for (auto[quad, glyph, leftTop] : quadData) {
1825         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1826         SkScalar dl = leftTop[0],
1827                  dt = leftTop[1],
1828                  dr = dl + (ar - al),
1829                  db = dt + (ab - at);
1830 
1831         quad[0] = {{dl, dt}, color, {al, at}};  // L,T
1832         quad[1] = {{dl, db}, color, {al, ab}};  // L,B
1833         quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
1834         quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
1835     }
1836 }
1837 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1838 void DirectMaskSubRunNoCache::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
1839                                              const SkMatrix& positionMatrix, SkIRect clip) const {
1840     auto quadData = [&](auto dst) {
1841         return SkMakeZip(dst,
1842                          fGlyphs.glyphs().subspan(offset, count),
1843                          fLeftTopDevicePos.subspan(offset, count));
1844     };
1845 
1846     if (clip.isEmpty()) {
1847         if (fMaskFormat != kARGB_GrMaskFormat) {
1848             using Quad = Mask2DVertex[4];
1849             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1850             direct_2D2(quadData((Quad*)vertexDst), color);
1851         } else {
1852             using Quad = ARGB2DVertex[4];
1853             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1854             generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0});
1855         }
1856     } else {
1857         if (fMaskFormat != kARGB_GrMaskFormat) {
1858             using Quad = Mask2DVertex[4];
1859             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1860             generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
1861         } else {
1862             using Quad = ARGB2DVertex[4];
1863             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1864             generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
1865         }
1866     }
1867 }
1868 
1869 // -- TransformedMaskSubRunNoCache -----------------------------------------------------------------
1870 class TransformedMaskSubRunNoCache final : public GrAtlasSubRun {
1871 public:
1872     struct VertexData {
1873         const SkPoint pos;
1874         // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
1875         // device space rect.
1876         GrIRect16 rect;
1877     };
1878 
1879     TransformedMaskSubRunNoCache(GrMaskFormat format,
1880                                  const SkRect& bounds,
1881                                  SkSpan<const VertexData> vertexData,
1882                                  GlyphVector glyphs);
1883 
1884     static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1885                                    const SkStrikeSpec& strikeSpec,
1886                                    GrMaskFormat format,
1887                                    GrSubRunAllocator* alloc);
1888 
1889     std::tuple<const GrClip*, GrOp::Owner>
1890     makeAtlasTextOp(const GrClip* clip,
1891                     const SkMatrixProvider& viewMatrix,
1892                     const SkGlyphRunList& glyphRunList,
1893                     const SkPaint& paint,
1894                     GrSurfaceDrawContext* sdc,
1895                     GrAtlasSubRunOwner subRunOwner) const override;
1896 
1897     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1898 
1899     std::tuple<bool, int> regenerateAtlas(
1900             int begin, int end, GrMeshDrawOp::Target* target) const override;
1901 
1902     void fillVertexData(
1903             void* vertexDst, int offset, int count,
1904             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
1905 
1906     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1907     int glyphCount() const override;
1908 
1909 private:
1910     // The rectangle that surrounds all the glyph bounding boxes in device space.
1911     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1912 
1913     const GrMaskFormat fMaskFormat;
1914 
1915     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1916     const SkRect fVertexBounds;
1917     const SkSpan<const VertexData> fVertexData;
1918 
1919     // Space for geometry
1920     alignas(alignof(GrAtlasTextOp::Geometry)) char fGeom[sizeof(GrAtlasTextOp::Geometry)];
1921 
1922     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1923     // be single threaded.
1924     mutable GlyphVector fGlyphs;
1925 };
1926 
TransformedMaskSubRunNoCache(GrMaskFormat format,const SkRect & bounds,SkSpan<const VertexData> vertexData,GlyphVector glyphs)1927 TransformedMaskSubRunNoCache::TransformedMaskSubRunNoCache(GrMaskFormat format,
1928                                                            const SkRect& bounds,
1929                                                            SkSpan<const VertexData> vertexData,
1930                                                            GlyphVector glyphs)
1931         : fMaskFormat{format}
1932         , fVertexBounds{bounds}
1933         , fVertexData{vertexData}
1934         , fGlyphs{glyphs} { }
1935 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,GrMaskFormat format,GrSubRunAllocator * alloc)1936 GrAtlasSubRunOwner TransformedMaskSubRunNoCache::Make(
1937         const SkZip<SkGlyphVariant, SkPoint>& drawables,
1938         const SkStrikeSpec& strikeSpec,
1939         GrMaskFormat format,
1940         GrSubRunAllocator* alloc) {
1941     SkRect bounds = SkRectPriv::MakeLargestInverted();
1942     auto initializer = [&, strikeToSource=strikeSpec.strikeToSourceRatio()](auto drawable) {
1943         auto [variant, pos] = drawable;
1944         SkGlyph* skGlyph = variant;
1945         int16_t l = skGlyph->left(),
1946                 t = skGlyph->top(),
1947                 r = l + skGlyph->width(),
1948                 b = t + skGlyph->height();
1949         SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
1950                 rb = SkPoint::Make(r, b) * strikeToSource + pos;
1951 
1952         bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1953         return VertexData{pos, {l, t, r, b}};
1954     };
1955 
1956     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, initializer);
1957 
1958     return alloc->makeUnique<TransformedMaskSubRunNoCache>(
1959             format, bounds, vertexData,
1960             GlyphVector::Make(strikeSpec, drawables.get<0>(), alloc));
1961 }
1962 
1963 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const1964 TransformedMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
1965                                               const SkMatrixProvider& viewMatrix,
1966                                               const SkGlyphRunList& glyphRunList,
1967                                               const SkPaint& paint,
1968                                               GrSurfaceDrawContext* sdc,
1969                                               GrAtlasSubRunOwner subRunOwner) const {
1970     SkASSERT(this->glyphCount() != 0);
1971 
1972     SkPoint drawOrigin = glyphRunList.origin();
1973     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1974 
1975     GrPaint grPaint;
1976     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1977 
1978     // We can clip geometrically using clipRect and ignore clip if we're not using SDFs or
1979     // transformed glyphs, and we have an axis-aligned rectangular non-AA clip.
1980     GrAtlasTextOp::Geometry* geometry = new ((void*)fGeom) GrAtlasTextOp::Geometry{
1981             *this,
1982             drawMatrix,
1983             drawOrigin,
1984             SkIRect::MakeEmpty(),
1985             nullptr,
1986             std::move(subRunOwner),
1987             drawingColor
1988     };
1989 
1990     GrRecordingContext* rContext = sdc->recordingContext();
1991     GrOp::Owner op = GrOp::Make<GrAtlasTextOp>(
1992             rContext,
1993             op_mask_type(fMaskFormat),
1994             true,
1995             this->glyphCount(),
1996             this->deviceRect(drawMatrix, drawOrigin),
1997             geometry,
1998             std::move(grPaint));
1999     return {clip, std::move(op)};
2000 }
2001 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)2002 void TransformedMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
2003     fGlyphs.packedGlyphIDToGrGlyph(cache);
2004 }
2005 
regenerateAtlas(int begin,int end,GrMeshDrawOp::Target * target) const2006 std::tuple<bool, int> TransformedMaskSubRunNoCache::regenerateAtlas(
2007         int begin, int end, GrMeshDrawOp::Target* target) const {
2008     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
2009 }
2010 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const2011 void TransformedMaskSubRunNoCache::fillVertexData(
2012         void* vertexDst, int offset, int count, GrColor color,
2013         const SkMatrix& positionMatrix, SkIRect clip) const {
2014     constexpr SkScalar kDstPadding = 0.f;
2015 
2016     auto quadData = [&](auto dst) {
2017         return SkMakeZip(dst,
2018                          fGlyphs.glyphs().subspan(offset, count),
2019                          fVertexData.subspan(offset, count));
2020     };
2021 
2022     if (!positionMatrix.hasPerspective()) {
2023         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
2024             using Quad = ARGB2DVertex[4];
2025             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2026             fill_transformed_vertices_2D(
2027                     quadData((Quad*) vertexDst),
2028                     kDstPadding,
2029                     fGlyphs.strikeToSourceRatio(),
2030                     color,
2031                     positionMatrix);
2032         } else {
2033             using Quad = Mask2DVertex[4];
2034             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2035             fill_transformed_vertices_2D(
2036                     quadData((Quad*) vertexDst),
2037                     kDstPadding,
2038                     fGlyphs.strikeToSourceRatio(),
2039                     color,
2040                     positionMatrix);
2041         }
2042     } else {
2043         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
2044             using Quad = ARGB3DVertex[4];
2045             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2046             fill_transformed_vertices_3D(
2047                     quadData((Quad*) vertexDst),
2048                     kDstPadding,
2049                     fGlyphs.strikeToSourceRatio(),
2050                     color,
2051                     positionMatrix);
2052         } else {
2053             using Quad = Mask3DVertex[4];
2054             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2055             fill_transformed_vertices_3D(
2056                     quadData((Quad*) vertexDst),
2057                     kDstPadding,
2058                     fGlyphs.strikeToSourceRatio(),
2059                     color,
2060                     positionMatrix);
2061         }
2062     }
2063 }
2064 
vertexStride(const SkMatrix & drawMatrix) const2065 size_t TransformedMaskSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2066     switch (fMaskFormat) {
2067         case kA8_GrMaskFormat:
2068             return drawMatrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
2069         case kARGB_GrMaskFormat:
2070             return drawMatrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
2071         default:
2072             SkASSERT(!drawMatrix.hasPerspective());
2073             return sizeof(Mask2DVertex);
2074     }
2075     SkUNREACHABLE;
2076 }
2077 
glyphCount() const2078 int TransformedMaskSubRunNoCache::glyphCount() const {
2079     return SkCount(fVertexData);
2080 }
2081 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2082 SkRect TransformedMaskSubRunNoCache::deviceRect(
2083         const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2084     SkRect outBounds = fVertexBounds;
2085     outBounds.offset(drawOrigin);
2086     return drawMatrix.mapRect(outBounds);
2087 }
2088 
2089 
2090 // -- SDFTSubRunNoCache ----------------------------------------------------------------------------
2091 class SDFTSubRunNoCache final : public GrAtlasSubRun {
2092 public:
2093     struct VertexData {
2094         const SkPoint pos;
2095         // The rectangle of the glyphs in strike space.
2096         GrIRect16 rect;
2097     };
2098 
2099     SDFTSubRunNoCache(GrMaskFormat format,
2100                       SkRect vertexBounds,
2101                       SkSpan<const VertexData> vertexData,
2102                       GlyphVector glyphs,
2103                       bool useLCDText,
2104                       bool antiAliased);
2105 
2106     static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2107                                    const SkFont& runFont,
2108                                    const SkStrikeSpec& strikeSpec,
2109                                    GrSubRunAllocator* alloc);
2110 
2111     std::tuple<const GrClip*, GrOp::Owner>
2112     makeAtlasTextOp(const GrClip* clip,
2113                     const SkMatrixProvider& viewMatrix,
2114                     const SkGlyphRunList& glyphRunList,
2115                     const SkPaint& paint,
2116                     GrSurfaceDrawContext* sdc,
2117                     GrAtlasSubRunOwner subRunOwner) const override;
2118 
2119     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
2120 
2121     std::tuple<bool, int> regenerateAtlas(
2122             int begin, int end, GrMeshDrawOp::Target* target) const override;
2123 
2124     void fillVertexData(
2125             void* vertexDst, int offset, int count,
2126             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
2127 
2128     size_t vertexStride(const SkMatrix& drawMatrix) const override;
2129     int glyphCount() const override;
2130 
2131 private:
2132     // The rectangle that surrounds all the glyph bounding boxes in device space.
2133     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
2134 
2135     const GrMaskFormat fMaskFormat;
2136 
2137     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
2138     const SkRect fVertexBounds;
2139     const SkSpan<const VertexData> fVertexData;
2140 
2141     // Space for geometry
2142     alignas(alignof(GrAtlasTextOp::Geometry)) char fGeom[sizeof(GrAtlasTextOp::Geometry)];
2143 
2144     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2145     // be single threaded.
2146     mutable GlyphVector fGlyphs;
2147 
2148     const bool fUseLCDText;
2149     const bool fAntiAliased;
2150 };
2151 
SDFTSubRunNoCache(GrMaskFormat format,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GlyphVector glyphs,bool useLCDText,bool antiAliased)2152 SDFTSubRunNoCache::SDFTSubRunNoCache(GrMaskFormat format,
2153                                      SkRect vertexBounds,
2154                                      SkSpan<const VertexData> vertexData,
2155                                      GlyphVector glyphs,
2156                                      bool useLCDText,
2157                                      bool antiAliased)
2158         : fMaskFormat{format}
2159         , fVertexBounds{vertexBounds}
2160         , fVertexData{vertexData}
2161         , fGlyphs{glyphs}
2162         , fUseLCDText{useLCDText}
2163         , fAntiAliased{antiAliased} { }
2164 
2165 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,const SkStrikeSpec & strikeSpec,GrSubRunAllocator * alloc)2166 GrAtlasSubRunOwner SDFTSubRunNoCache::Make(
2167         const SkZip<SkGlyphVariant, SkPoint>& drawables,
2168         const SkFont& runFont,
2169         const SkStrikeSpec& strikeSpec,
2170         GrSubRunAllocator* alloc) {
2171 
2172     SkRect bounds = SkRectPriv::MakeLargestInverted();
2173     auto initializer = [&, strikeToSource=strikeSpec.strikeToSourceRatio()](auto drawable) {
2174         auto [variant, pos] = drawable;
2175         SkGlyph* skGlyph = variant;
2176         int16_t l = skGlyph->left(),
2177                 t = skGlyph->top(),
2178                 r = l + skGlyph->width(),
2179                 b = t + skGlyph->height();
2180         SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
2181                 rb = SkPoint::Make(r, b) * strikeToSource + pos;
2182 
2183         bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
2184         return VertexData{pos, {l, t, r, b}};
2185     };
2186 
2187     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, initializer);
2188 
2189     return alloc->makeUnique<SDFTSubRunNoCache>(
2190             kA8_GrMaskFormat,
2191             bounds,
2192             vertexData,
2193             GlyphVector::Make(strikeSpec, drawables.get<0>(), alloc),
2194             runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
2195             has_some_antialiasing(runFont));
2196 }
2197 
2198 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,GrSurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2199 SDFTSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2200                                    const SkMatrixProvider& viewMatrix,
2201                                    const SkGlyphRunList& glyphRunList,
2202                                    const SkPaint& paint,
2203                                    GrSurfaceDrawContext* sdc,
2204                                    GrAtlasSubRunOwner subRunOwner) const {
2205     SkASSERT(this->glyphCount() != 0);
2206 
2207     SkPoint drawOrigin = glyphRunList.origin();
2208     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2209 
2210     GrPaint grPaint;
2211     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2212 
2213     auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
2214     calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
2215 
2216     GrAtlasTextOp::Geometry* geometry = new ((void*)fGeom) GrAtlasTextOp::Geometry {
2217             *this,
2218             drawMatrix,
2219             drawOrigin,
2220             SkIRect::MakeEmpty(),
2221             nullptr,
2222             std::move(subRunOwner),
2223             drawingColor
2224     };
2225 
2226     GrRecordingContext* rContext = sdc->recordingContext();
2227     GrOp::Owner op = GrOp::Make<GrAtlasTextOp>(
2228             rContext,
2229             maskType,
2230             true,
2231             this->glyphCount(),
2232             this->deviceRect(drawMatrix, drawOrigin),
2233             SkPaintPriv::ComputeLuminanceColor(paint),
2234             useGammaCorrectDistanceTable,
2235             DFGPFlags,
2236             geometry,
2237             std::move(grPaint));
2238 
2239     return {clip, std::move(op)};
2240 }
2241 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)2242 void SDFTSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
2243     fGlyphs.packedGlyphIDToGrGlyph(cache);
2244 }
2245 
regenerateAtlas(int begin,int end,GrMeshDrawOp::Target * target) const2246 std::tuple<bool, int> SDFTSubRunNoCache::regenerateAtlas(
2247         int begin, int end, GrMeshDrawOp::Target *target) const {
2248 
2249     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
2250 }
2251 
vertexStride(const SkMatrix & drawMatrix) const2252 size_t SDFTSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2253     return sizeof(Mask2DVertex);
2254 }
2255 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const2256 void SDFTSubRunNoCache::fillVertexData(
2257         void *vertexDst, int offset, int count,
2258         GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const {
2259     using Quad = Mask2DVertex[4];
2260     SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2261     fill_transformed_vertices_2D(
2262             SkMakeZip((Quad*)vertexDst,
2263                       fGlyphs.glyphs().subspan(offset, count),
2264                       fVertexData.subspan(offset, count)),
2265             SK_DistanceFieldInset,
2266             fGlyphs.strikeToSourceRatio(),
2267             color,
2268             positionMatrix);
2269 }
2270 
glyphCount() const2271 int SDFTSubRunNoCache::glyphCount() const {
2272     return SkCount(fVertexData);
2273 }
2274 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2275 SkRect SDFTSubRunNoCache::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2276     SkRect outBounds = fVertexBounds;
2277     outBounds.offset(drawOrigin);
2278     return drawMatrix.mapRect(outBounds);
2279 }
2280 }  // namespace
2281 
2282 
GrSubRunNoCachePainter(GrSurfaceDrawContext * sdc,GrSubRunAllocator * alloc,const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint)2283 GrSubRunNoCachePainter::GrSubRunNoCachePainter(GrSurfaceDrawContext* sdc,
2284                                                GrSubRunAllocator* alloc,
2285                                                const GrClip* clip,
2286                                                const SkMatrixProvider& viewMatrix,
2287                                                const SkGlyphRunList& glyphRunList,
2288                                                const SkPaint& paint)
2289             : fSDC{sdc}
2290             , fAlloc{alloc}
2291             , fClip{clip}
2292             , fViewMatrix{viewMatrix}
2293             , fGlyphRunList{glyphRunList}
2294             , fPaint {paint} {}
2295 
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)2296 void GrSubRunNoCachePainter::processDeviceMasks(
2297         const SkZip<SkGlyphVariant, SkPoint>& drawables, const SkStrikeSpec& strikeSpec) {
2298     if (drawables.empty()) { return; }
2299 
2300     auto glyphSpan = drawables.get<0>();
2301     SkGlyph* glyph = glyphSpan[0];
2302     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2303     size_t startIndex = 0;
2304     for (size_t i = 1; i < drawables.size(); i++) {
2305         glyph = glyphSpan[i];
2306         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2307         if (format != nextFormat) {
2308             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
2309             this->draw(DirectMaskSubRunNoCache::Make(sameFormat, strikeSpec, format, fAlloc));
2310             format = nextFormat;
2311             startIndex = i;
2312         }
2313     }
2314     auto sameFormat = drawables.last(drawables.size() - startIndex);
2315     this->draw(DirectMaskSubRunNoCache::Make(sameFormat, strikeSpec, format, fAlloc));
2316 }
2317 
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec)2318 void GrSubRunNoCachePainter::processSourceMasks(
2319             const SkZip<SkGlyphVariant, SkPoint>& drawables, const SkStrikeSpec& strikeSpec) {
2320     if (drawables.empty()) {
2321         return;
2322     }
2323 
2324     auto glyphSpan = drawables.get<0>();
2325     SkGlyph* glyph = glyphSpan[0];
2326     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2327     size_t startIndex = 0;
2328     for (size_t i = 1; i < drawables.size(); i++) {
2329         glyph = glyphSpan[i];
2330         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2331         if (format != nextFormat) {
2332             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
2333             this->draw(
2334                     TransformedMaskSubRunNoCache::Make(
2335                             sameFormat, strikeSpec, format, fAlloc));
2336             format = nextFormat;
2337             startIndex = i;
2338         }
2339     }
2340     auto sameFormat = drawables.last(drawables.size() - startIndex);
2341     this->draw(
2342             TransformedMaskSubRunNoCache::Make(sameFormat, strikeSpec, format, fAlloc));
2343 }
2344 
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,const SkStrikeSpec & strikeSpec)2345 void GrSubRunNoCachePainter::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2346                                                 const SkFont& runFont,
2347                                                 const SkStrikeSpec& strikeSpec) {
2348     SkASSERT(!drawables.empty());
2349     SkPoint drawOrigin = fGlyphRunList.origin();
2350     const SkPaint& drawPaint = fPaint;
2351     SkPaint runPaint{drawPaint};
2352     runPaint.setAntiAlias(has_some_antialiasing(runFont));
2353     // If there are shaders, blurs or styles, the path must be scaled into source
2354     // space independently of the CTM. This allows the CTM to be correct for the
2355     // different effects.
2356     GrStyle style(runPaint);
2357 
2358     bool needsExactCTM = runPaint.getShader()
2359                          || style.applies()
2360                          || runPaint.getMaskFilter();
2361 
2362     // Calculate the matrix that maps the path glyphs from their size in the strike to
2363     // the graphics source space.
2364     SkScalar scale = strikeSpec.strikeToSourceRatio();
2365     SkMatrix strikeToSource = SkMatrix::Scale(scale, scale);
2366     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
2367     if (!needsExactCTM) {
2368         for (auto [variant, pos] : drawables) {
2369             const SkPath& path = *variant.path();
2370             SkMatrix pathMatrix = strikeToSource;
2371             pathMatrix.postTranslate(pos.x(), pos.y());
2372             SkPreConcatMatrixProvider strikeToDevice(fViewMatrix, pathMatrix);
2373 
2374             GrStyledShape shape(path, drawPaint);
2375             GrBlurUtils::drawShapeWithMaskFilter(
2376                     fSDC->recordingContext(), fSDC, fClip, runPaint,
2377                     strikeToDevice, shape);
2378         }
2379     } else {
2380         // Transform the path to device space because the deviceMatrix must be unchanged to
2381         // draw effect, filter or shader paths.
2382         for (auto [variant, pos] : drawables) {
2383             const SkPath& path = *variant.path();
2384             // Transform the glyph to source space.
2385             SkMatrix pathMatrix = strikeToSource;
2386             pathMatrix.postTranslate(pos.x(), pos.y());
2387 
2388             SkPath sourceSpacePath;
2389             path.transform(pathMatrix, &sourceSpacePath);
2390             sourceSpacePath.setIsVolatile(true);
2391             GrStyledShape shape(sourceSpacePath, drawPaint);
2392             GrBlurUtils::drawShapeWithMaskFilter(
2393                     fSDC->recordingContext(), fSDC, fClip, runPaint, fViewMatrix, shape);
2394         }
2395     }
2396 }
2397 
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkStrikeSpec & strikeSpec,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)2398 void GrSubRunNoCachePainter::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2399                                                const SkStrikeSpec& strikeSpec,
2400                                                const SkFont& runFont,
2401                                                SkScalar minScale, SkScalar maxScale) {
2402     if (drawables.empty()) {
2403         return;
2404     }
2405     this->draw(SDFTSubRunNoCache::Make(drawables, runFont, strikeSpec, fAlloc));
2406 }
2407 
draw(GrAtlasSubRunOwner subRun)2408 void GrSubRunNoCachePainter::draw(GrAtlasSubRunOwner subRun) {
2409     if (subRun == nullptr) {
2410         return;
2411     }
2412     GrAtlasSubRun* subRunPtr = subRun.get();
2413     auto [drawingClip, op] = subRunPtr->makeAtlasTextOp(
2414             fClip, fViewMatrix, fGlyphRunList, fPaint, fSDC, std::move(subRun));
2415     if (op != nullptr) {
2416         fSDC->addDrawOp(drawingClip, std::move(op));
2417     }
2418 }
2419 
2420