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