• 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__anon3f11a1ec0111::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__anon3f11a1ec0111::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 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE)
408                 atlasManager->incAtlasMissCount();
409 #endif
410                 const SkGlyph& skGlyph = *metricsAndImages.glyph(grGlyph->fPackedID);
411                 auto code = atlasManager->addGlyphToAtlas(
412                         skGlyph, grGlyph, srcPadding, target->resourceProvider(),
413                         uploadTarget, bilerpPadding);
414                 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
415                     success = code != GrDrawOpAtlas::ErrorCode::kError;
416                     break;
417                 }
418             }
419 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE)
420             else {
421                 atlasManager->incAtlasHitCount();
422             }
423 #endif
424             atlasManager->addGlyphToBulkAndSetUseToken(
425                     &fBulkUseToken, maskFormat, grGlyph,
426                     tokenTracker->nextDrawToken());
427             glyphsPlacedInAtlas++;
428         }
429 
430         // Update atlas generation if there are no more glyphs to put in the atlas.
431         if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
432             // Need to get the freshest value of the atlas' generation because
433             // updateTextureCoordinates may have changed it.
434             fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
435         }
436 
437         return {success, glyphsPlacedInAtlas};
438     } else {
439         // The atlas hasn't changed, so our texture coordinates are still valid.
440         if (end == SkCount(fGlyphs)) {
441             // The atlas hasn't changed and the texture coordinates are all still valid. Update
442             // all the plots used to the new use token.
443             atlasManager->setUseTokenBulk(fBulkUseToken,
444                                           uploadTarget->tokenTracker()->nextDrawToken(),
445                                           maskFormat);
446         }
447         return {true, end - begin};
448     }
449 }
450 
451 // -- DirectMaskSubRun -----------------------------------------------------------------------------
452 class DirectMaskSubRun final : public GrSubRun, public GrAtlasSubRun {
453 public:
454     using DevicePosition = skvx::Vec<2, int16_t>;
455 
456     DirectMaskSubRun(GrMaskFormat format,
457                      GrTextBlob* blob,
458                      const SkGlyphRect& deviceBounds,
459                      SkSpan<const DevicePosition> devicePositions,
460                      GlyphVector&& glyphs,
461                      bool glyphsOutOfBounds);
462 
463     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
464                               sk_sp<SkStrike>&& strike,
465                               SkScalar,
466                               GrMaskFormat format,
467                               GrTextBlob* blob,
468                               GrSubRunAllocator* alloc);
469 
470     void draw(const GrClip*,
471               const SkMatrixProvider& viewMatrix,
472               const SkGlyphRunList&,
473               const SkPaint&,
474               skgpu::v1::SurfaceDrawContext*) const override;
475 
476     std::tuple<const GrClip*, GrOp::Owner>
477     makeAtlasTextOp(const GrClip* clip,
478                     const SkMatrixProvider& viewMatrix,
479                     SkPoint drawOrigin,
480                     const SkPaint& paint,
481                     skgpu::v1::SurfaceDrawContext* sdc,
482                     GrAtlasSubRunOwner) const override;
483 
484     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
485 
486     GrAtlasSubRun* testingOnly_atlasSubRun() override;
487 
488     size_t vertexStride(const SkMatrix& drawMatrix) const override;
489 
490     int glyphCount() const override;
491 
492     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
493 
494     std::tuple<bool, int>
495     regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
496 
497     void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
498                         const SkMatrix& positionMatrix, SkIRect clip) const override;
499 private:
500     // The rectangle that surrounds all the glyph bounding boxes in device space.
501     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
502 
503     const GrMaskFormat fMaskFormat;
504     GrTextBlob* const fBlob;
505 
506     // The union of all the glyph bounds in device space.
507     const SkGlyphRect fGlyphDeviceBounds;
508     const SkSpan<const DevicePosition> fLeftTopDevicePos;
509     const bool fSomeGlyphsExcluded;
510 
511     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
512     // be single threaded.
513     mutable GlyphVector fGlyphs;
514 };
515 
DirectMaskSubRun(GrMaskFormat format,GrTextBlob * blob,const SkGlyphRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GlyphVector && glyphs,bool glyphsOutOfBounds)516 DirectMaskSubRun::DirectMaskSubRun(GrMaskFormat format,
517                                    GrTextBlob* blob,
518                                    const SkGlyphRect& deviceBounds,
519                                    SkSpan<const DevicePosition> devicePositions,
520                                    GlyphVector&& glyphs,
521                                    bool glyphsOutOfBounds)
522         : fMaskFormat{format}
523         , fBlob{blob}
524         , fGlyphDeviceBounds{deviceBounds}
525         , fLeftTopDevicePos{devicePositions}
526         , fSomeGlyphsExcluded{glyphsOutOfBounds}
527         , fGlyphs{std::move(glyphs)} {}
528 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar,GrMaskFormat format,GrTextBlob * blob,GrSubRunAllocator * alloc)529 GrSubRunOwner DirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
530                                      sk_sp<SkStrike>&& strike,
531                                      SkScalar,
532                                      GrMaskFormat format,
533                                      GrTextBlob* blob,
534                                      GrSubRunAllocator* alloc) {
535     DevicePosition* glyphLeftTop = alloc->makePODArray<DevicePosition>(drawables.size());
536     GlyphVector::Variant* glyphIDs = alloc->makePODArray<GlyphVector::Variant>(drawables.size());
537 
538     // Because this is the direct case, the maximum width or height is the size that fits in the
539     // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
540     // not overflow.
541     constexpr SkScalar kMaxPos =
542             std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
543     SkGlyphRect runBounds = skglyph::empty_rect();
544     size_t goodPosCount = 0;
545     for (auto [variant, pos] : drawables) {
546         auto [x, y] = pos;
547         // Ensure that the .offset() call below does not overflow. And, at this point none of the
548         // rectangles are empty because they were culled before the run was created. Basically,
549         // cull all the glyphs that can't appear on the screen.
550         if (-kMaxPos < x && x < kMaxPos && -kMaxPos  < y && y < kMaxPos) {
551             const SkGlyph* const skGlyph = variant;
552             const SkGlyphRect deviceBounds =
553                     skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
554             runBounds = skglyph::rect_union(runBounds, deviceBounds);
555             glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
556             glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
557             goodPosCount += 1;
558         }
559     }
560 
561     // Wow! no glyphs are in bounds and had non-empty bounds.
562     if (goodPosCount == 0) {
563         return nullptr;
564     }
565 
566     // If some glyphs were excluded by the bounds, then this subrun can't be generally be used
567     // for other draws. Mark the subrun as not general.
568     bool glyphsExcluded = goodPosCount != drawables.size();
569     SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
570     return alloc->makeUnique<DirectMaskSubRun>(
571             format, blob, runBounds, leftTop,
572             GlyphVector{std::move(strike), {glyphIDs, goodPosCount}}, glyphsExcluded);
573 }
574 
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const575 bool DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
576     auto [reuse, translation] = check_integer_translate(fBlob->initialMatrix(), drawMatrix);
577 
578     // If glyphs were excluded because of position bounds, then this subrun can only be reused if
579     // there is no change in position.
580     if (fSomeGlyphsExcluded) {
581         return translation.x() == 0 && translation.y() == 0;
582     }
583 
584     return reuse;
585 }
586 
vertexStride(const SkMatrix &) const587 size_t DirectMaskSubRun::vertexStride(const SkMatrix&) const {
588     if (fMaskFormat != kARGB_GrMaskFormat) {
589         return sizeof(Mask2DVertex);
590     } else {
591         return sizeof(ARGB2DVertex);
592     }
593 }
594 
glyphCount() const595 int DirectMaskSubRun::glyphCount() const {
596     return SkCount(fGlyphs.glyphs());
597 }
598 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const599 void DirectMaskSubRun::draw(const GrClip* clip,
600                             const SkMatrixProvider& viewMatrix,
601                             const SkGlyphRunList& glyphRunList,
602                             const SkPaint& paint,
603                             skgpu::v1::SurfaceDrawContext* sdc) const{
604     auto[drawingClip, op] = this->makeAtlasTextOp(
605             clip, viewMatrix, glyphRunList.origin(), paint, sdc, nullptr);
606     if (op != nullptr) {
607         sdc->addDrawOp(drawingClip, std::move(op));
608     }
609 }
610 
611 namespace {
612 enum ClipMethod {
613     kClippedOut,
614     kUnclipped,
615     kGPUClipped,
616     kGeometryClipped
617 };
618 
619 std::tuple<ClipMethod, SkIRect>
calculate_clip(const GrClip * clip,SkRect deviceBounds,SkRect glyphBounds)620 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
621     if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
622         return {kClippedOut, SkIRect::MakeEmpty()};
623     } else if (clip != nullptr) {
624         switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
625             case GrClip::Effect::kClippedOut:
626                 return {kClippedOut, SkIRect::MakeEmpty()};
627             case GrClip::Effect::kUnclipped:
628                 return {kUnclipped, SkIRect::MakeEmpty()};
629             case GrClip::Effect::kClipped: {
630                 if (result.fIsRRect && result.fRRect.isRect()) {
631                     SkRect r = result.fRRect.rect();
632                     if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
633                         SkIRect clipRect = SkIRect::MakeEmpty();
634                         // Clip geometrically during onPrepare using clipRect.
635                         r.round(&clipRect);
636                         if (clipRect.contains(glyphBounds)) {
637                             // If fully within the clip, signal no clipping using the empty rect.
638                             return {kUnclipped, SkIRect::MakeEmpty()};
639                         }
640                         // Use the clipRect to clip the geometry.
641                         return {kGeometryClipped, clipRect};
642                     }
643                     // Partial pixel clipped at this point. Have the GPU handle it.
644                 }
645             }
646             break;
647         }
648     }
649     return {kGPUClipped, SkIRect::MakeEmpty()};
650 }
651 }  // namespace
652 
653 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const654 DirectMaskSubRun::makeAtlasTextOp(const GrClip* clip,
655                                   const SkMatrixProvider& viewMatrix,
656                                   SkPoint drawOrigin,
657                                   const SkPaint& paint,
658                                   skgpu::v1::SurfaceDrawContext* sdc,
659                                   GrAtlasSubRunOwner) const {
660     SkASSERT(this->glyphCount() != 0);
661 
662     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
663 
664     // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
665     // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
666     // needed.
667     const SkRect subRunBounds = this->deviceRect(drawMatrix, drawOrigin);
668     const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
669     auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunBounds);
670 
671     switch (clipMethod) {
672         case kClippedOut:
673             // Returning nullptr as op means skip this op.
674             return {nullptr, nullptr};
675         case kUnclipped:
676         case kGeometryClipped:
677             // GPU clip is not needed.
678             clip = nullptr;
679             break;
680         case kGPUClipped:
681             // Use the the GPU clip; clipRect is ignored.
682             break;
683     }
684 
685     if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
686 
687     GrPaint grPaint;
688     const SkPMColor4f drawingColor =
689             calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
690 
691     auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
692                                                        drawMatrix,
693                                                        drawOrigin,
694                                                        clipRect,
695                                                        sk_ref_sp<GrTextBlob>(fBlob),
696                                                        drawingColor,
697                                                        sdc->arenaAlloc());
698 
699     GrRecordingContext* const rContext = sdc->recordingContext();
700     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
701                                              op_mask_type(fMaskFormat),
702                                              false,
703                                              this->glyphCount(),
704                                              subRunBounds,
705                                              geometry,
706                                              std::move(grPaint));
707 
708     return {clip, std::move(op)};
709 }
710 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)711 void DirectMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
712     fGlyphs.packedGlyphIDToGrGlyph(cache);
713 }
714 
715 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const716 DirectMaskSubRun::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
717     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
718 }
719 
720 // The 99% case. No clip. Non-color only.
direct_2D(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRun::DevicePosition> quadData,GrColor color,SkIPoint integralOriginOffset)721 void direct_2D(SkZip<Mask2DVertex[4],
722                const GrGlyph*,
723                const DirectMaskSubRun::DevicePosition> quadData,
724                GrColor color,
725                SkIPoint integralOriginOffset) {
726     for (auto[quad, glyph, leftTop] : quadData) {
727         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
728         SkScalar dl = leftTop[0] + integralOriginOffset.x(),
729                  dt = leftTop[1] + integralOriginOffset.y(),
730                  dr = dl + (ar - al),
731                  db = dt + (ab - at);
732 
733         quad[0] = {{dl, dt}, color, {al, at}};  // L,T
734         quad[1] = {{dl, db}, color, {al, ab}};  // L,B
735         quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
736         quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
737     }
738 }
739 
740 template <typename Rect>
ltbr(const Rect & r)741 auto ltbr(const Rect& r) {
742     return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
743 }
744 
745 // Handle any combination of BW or color and clip or no clip.
746 template<typename Quad, typename VertexData>
generalized_direct_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,SkIPoint integralOriginOffset,SkIRect * clip=nullptr)747 void generalized_direct_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
748                            GrColor color,
749                            SkIPoint integralOriginOffset,
750                            SkIRect* clip = nullptr) {
751     for (auto[quad, glyph, leftTop] : quadData) {
752         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
753         uint16_t w = ar - al,
754                  h = ab - at;
755         SkScalar l = (SkScalar)leftTop[0] + integralOriginOffset.x(),
756                  t = (SkScalar)leftTop[1] + integralOriginOffset.y();
757         if (clip == nullptr) {
758             auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
759             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
760             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
761             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
762             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
763         } else {
764             SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
765             SkScalar dl, dt, dr, db;
766             if (!clip->containsNoEmptyCheck(devIRect)) {
767                 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
768                     al += clipped.left()   - devIRect.left();
769                     at += clipped.top()    - devIRect.top();
770                     ar += clipped.right()  - devIRect.right();
771                     ab += clipped.bottom() - devIRect.bottom();
772                     std::tie(dl, dt, dr, db) = ltbr(clipped);
773                 } else {
774                     // TODO: omit generating any vertex data for fully clipped glyphs ?
775                     std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
776                     std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
777                 }
778             } else {
779                 std::tie(dl, dt, dr, db) = ltbr(devIRect);
780             }
781             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
782             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
783             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
784             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
785         }
786     }
787 }
788 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const789 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
790                                       const SkMatrix& positionMatrix, SkIRect clip) const {
791     auto quadData = [&](auto dst) {
792         return SkMakeZip(dst,
793                          fGlyphs.glyphs().subspan(offset, count),
794                          fLeftTopDevicePos.subspan(offset, count));
795     };
796 
797     SkPoint originOffset = positionMatrix.mapOrigin() - fBlob->initialMatrix().mapOrigin();
798     SkIPoint integralOriginOffset =
799             {SkScalarRoundToInt(originOffset.x()), SkScalarRoundToInt(originOffset.y())};
800 
801     if (clip.isEmpty()) {
802         if (fMaskFormat != kARGB_GrMaskFormat) {
803             using Quad = Mask2DVertex[4];
804             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
805             direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
806         } else {
807             using Quad = ARGB2DVertex[4];
808             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
809             generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
810         }
811     } else {
812         if (fMaskFormat != kARGB_GrMaskFormat) {
813             using Quad = Mask2DVertex[4];
814             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
815             generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
816         } else {
817             using Quad = ARGB2DVertex[4];
818             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
819             generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
820         }
821     }
822 }
823 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const824 SkRect DirectMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
825     SkIRect outBounds = fGlyphDeviceBounds.iRect();
826 
827     // Calculate the offset from the initial device origin to the current device origin.
828     SkVector offset = drawMatrix.mapPoint(drawOrigin) - fBlob->initialMatrix().mapOrigin();
829 
830     // The offset should be integer, but make sure.
831     SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
832 
833     return SkRect::Make(outBounds.makeOffset(iOffset));
834 }
835 
testingOnly_atlasSubRun()836 GrAtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() {
837     return this;
838 }
839 
840 // -- TransformedMaskSubRun ------------------------------------------------------------------------
841 class TransformedMaskSubRun final : public GrSubRun, public GrAtlasSubRun {
842 public:
843     struct VertexData {
844         const SkPoint pos;
845         // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
846         // device space rect.
847         GrIRect16 rect;
848     };
849 
850     TransformedMaskSubRun(GrMaskFormat format,
851                           GrTextBlob* blob,
852                           SkScalar strikeToSourceScale,
853                           const SkRect& bounds,
854                           SkSpan<const VertexData> vertexData,
855                           GlyphVector&& glyphs);
856 
857     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
858                               sk_sp<SkStrike>&& strike,
859                               SkScalar strikeToSourceScale,
860                               GrMaskFormat format,
861                               GrTextBlob* blob,
862                               GrSubRunAllocator* alloc);
863 
864     void draw(const GrClip*,
865               const SkMatrixProvider& viewMatrix,
866               const SkGlyphRunList&,
867               const SkPaint&,
868               skgpu::v1::SurfaceDrawContext*) const override;
869 
870     std::tuple<const GrClip*, GrOp::Owner>
871     makeAtlasTextOp(const GrClip*,
872                     const SkMatrixProvider& viewMatrix,
873                     SkPoint drawOrigin,
874                     const SkPaint&,
875                     skgpu::v1::SurfaceDrawContext*,
876                     GrAtlasSubRunOwner) const override;
877 
878     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
879 
880     GrAtlasSubRun* testingOnly_atlasSubRun() override;
881 
882     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
883 
884     std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
885 
886     void fillVertexData(
887             void* vertexDst, int offset, int count,
888             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
889 
890     size_t vertexStride(const SkMatrix& drawMatrix) const override;
891     int glyphCount() const override;
892 
893 private:
894     // The rectangle that surrounds all the glyph bounding boxes in device space.
895     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
896 
897     const GrMaskFormat fMaskFormat;
898     GrTextBlob* fBlob;
899 
900     // The scale factor between the strike size, and the source size.
901     const SkScalar fStrikeToSourceScale;
902 
903     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
904     const SkRect fVertexBounds;
905     const SkSpan<const VertexData> fVertexData;
906 
907     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
908     // be single threaded.
909     mutable GlyphVector fGlyphs;
910 };
911 
TransformedMaskSubRun(GrMaskFormat format,GrTextBlob * blob,SkScalar strikeToSourceScale,const SkRect & bounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs)912 TransformedMaskSubRun::TransformedMaskSubRun(GrMaskFormat format,
913                                              GrTextBlob* blob,
914                                              SkScalar strikeToSourceScale,
915                                              const SkRect& bounds,
916                                              SkSpan<const VertexData> vertexData,
917                                              GlyphVector&& glyphs)
918         : fMaskFormat{format}
919         , fBlob{blob}
920         , fStrikeToSourceScale{strikeToSourceScale}
921         , fVertexBounds{bounds}
922         , fVertexData{vertexData}
923         , fGlyphs{std::move(glyphs)} { }
924 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrMaskFormat format,GrTextBlob * blob,GrSubRunAllocator * alloc)925 GrSubRunOwner TransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
926                                           sk_sp<SkStrike>&& strike,
927                                           SkScalar strikeToSourceScale,
928                                           GrMaskFormat format,
929                                           GrTextBlob* blob,
930                                           GrSubRunAllocator* alloc) {
931     SkRect bounds = SkRectPriv::MakeLargestInverted();
932 
933     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(
934             drawables,
935             [&](auto e) {
936                 auto [variant, pos] = e;
937                 SkGlyph* skGlyph = variant;
938                 int16_t l = skGlyph->left(),
939                         t = skGlyph->top(),
940                         r = l + skGlyph->width(),
941                         b = t + skGlyph->height();
942                 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
943                         rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
944 
945                 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
946                 return VertexData{pos, {l, t, r, b}};
947             });
948 
949     return alloc->makeUnique<TransformedMaskSubRun>(
950             format, blob, strikeToSourceScale, bounds, vertexData,
951             GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc));
952 }
953 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const954 void TransformedMaskSubRun::draw(const GrClip* clip,
955                                  const SkMatrixProvider& viewMatrix,
956                                  const SkGlyphRunList& glyphRunList,
957                                  const SkPaint& paint,
958                                  skgpu::v1::SurfaceDrawContext* sdc) const {
959     auto[drawingClip, op] = this->makeAtlasTextOp(
960             clip, viewMatrix, glyphRunList.origin(), paint, sdc, nullptr);
961     if (op != nullptr) {
962         sdc->addDrawOp(drawingClip, std::move(op));
963     }
964 }
965 
966 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const967 TransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
968                                        const SkMatrixProvider& viewMatrix,
969                                        SkPoint drawOrigin,
970                                        const SkPaint& paint,
971                                        skgpu::v1::SurfaceDrawContext* sdc,
972                                        GrAtlasSubRunOwner) const {
973     SkASSERT(this->glyphCount() != 0);
974 
975     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
976 
977     GrPaint grPaint;
978     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
979 
980     auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
981                                                        drawMatrix,
982                                                        drawOrigin,
983                                                        SkIRect::MakeEmpty(),
984                                                        sk_ref_sp<GrTextBlob>(fBlob),
985                                                        drawingColor,
986                                                        sdc->arenaAlloc());
987 
988     GrRecordingContext* const rContext = sdc->recordingContext();
989     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
990                                              op_mask_type(fMaskFormat),
991                                              true,
992                                              this->glyphCount(),
993                                              this->deviceRect(drawMatrix, drawOrigin),
994                                              geometry,
995                                              std::move(grPaint));
996     return {clip, std::move(op)};
997 }
998 
999 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may be
1000 // better.
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1001 bool TransformedMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1002     if (fBlob->initialMatrix().getMaxScale() < 1) {
1003         return false;
1004     }
1005     return true;
1006 }
1007 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1008 void TransformedMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1009     fGlyphs.packedGlyphIDToGrGlyph(cache);
1010 }
1011 
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1012 std::tuple<bool, int> TransformedMaskSubRun::regenerateAtlas(int begin, int end,
1013                                                              GrMeshDrawTarget* target) const {
1014     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
1015 }
1016 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1017 void TransformedMaskSubRun::fillVertexData(
1018         void* vertexDst, int offset, int count, GrColor color, const SkMatrix& positionMatrix,
1019         SkIRect clip) const {
1020     constexpr SkScalar kDstPadding = 0.f;
1021 
1022     auto quadData = [&](auto dst) {
1023         return SkMakeZip(dst,
1024                          fGlyphs.glyphs().subspan(offset, count),
1025                          fVertexData.subspan(offset, count));
1026     };
1027 
1028     if (!positionMatrix.hasPerspective()) {
1029         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
1030             using Quad = ARGB2DVertex[4];
1031             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1032             fill_transformed_vertices_2D(
1033                     quadData((Quad*) vertexDst),
1034                     kDstPadding,
1035                     fStrikeToSourceScale,
1036                     color,
1037                     positionMatrix);
1038         } else {
1039             using Quad = Mask2DVertex[4];
1040             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1041             fill_transformed_vertices_2D(
1042                     quadData((Quad*) vertexDst),
1043                     kDstPadding,
1044                     fStrikeToSourceScale,
1045                     color,
1046                     positionMatrix);
1047         }
1048     } else {
1049         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
1050             using Quad = ARGB3DVertex[4];
1051             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1052             fill_transformed_vertices_3D(
1053                     quadData((Quad*) vertexDst),
1054                     kDstPadding,
1055                     fStrikeToSourceScale,
1056                     color,
1057                     positionMatrix);
1058         } else {
1059             using Quad = Mask3DVertex[4];
1060             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1061             fill_transformed_vertices_3D(
1062                     quadData((Quad*) vertexDst),
1063                     kDstPadding,
1064                     fStrikeToSourceScale,
1065                     color,
1066                     positionMatrix);
1067         }
1068     }
1069 }
1070 
vertexStride(const SkMatrix & drawMatrix) const1071 size_t TransformedMaskSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1072     switch (fMaskFormat) {
1073         case kA8_GrMaskFormat:
1074             return drawMatrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
1075         case kARGB_GrMaskFormat:
1076             return drawMatrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
1077         default:
1078             SkASSERT(!drawMatrix.hasPerspective());
1079             return sizeof(Mask2DVertex);
1080     }
1081     SkUNREACHABLE;
1082 }
1083 
glyphCount() const1084 int TransformedMaskSubRun::glyphCount() const {
1085     return SkCount(fVertexData);
1086 }
1087 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1088 SkRect TransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1089     SkRect outBounds = fVertexBounds;
1090     outBounds.offset(drawOrigin);
1091     return drawMatrix.mapRect(outBounds);
1092 }
1093 
testingOnly_atlasSubRun()1094 GrAtlasSubRun* TransformedMaskSubRun::testingOnly_atlasSubRun() {
1095     return this;
1096 }
1097 
1098 // -- SDFTSubRun -----------------------------------------------------------------------------------
1099 class SDFTSubRun final : public GrSubRun, public GrAtlasSubRun {
1100 public:
1101     struct VertexData {
1102         const SkPoint pos;
1103         // The rectangle of the glyphs in strike space.
1104         GrIRect16 rect;
1105     };
1106 
1107     SDFTSubRun(GrMaskFormat format,
1108                GrTextBlob* blob,
1109                SkScalar strikeToSource,
1110                SkRect vertexBounds,
1111                SkSpan<const VertexData> vertexData,
1112                GlyphVector&& glyphs,
1113                bool useLCDText,
1114                bool antiAliased);
1115 
1116     static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1117                               const SkFont& runFont,
1118                               sk_sp<SkStrike>&& strike,
1119                               SkScalar strikeToSourceScale,
1120                               GrTextBlob* blob,
1121                               GrSubRunAllocator* alloc);
1122 
1123     void draw(const GrClip*,
1124               const SkMatrixProvider& viewMatrix,
1125               const SkGlyphRunList&,
1126               const SkPaint&,
1127               skgpu::v1::SurfaceDrawContext*) const override;
1128 
1129     std::tuple<const GrClip*, GrOp::Owner>
1130     makeAtlasTextOp(const GrClip*,
1131                     const SkMatrixProvider& viewMatrix,
1132                     SkPoint drawOrigin,
1133                     const SkPaint&,
1134                     skgpu::v1::SurfaceDrawContext*,
1135                     GrAtlasSubRunOwner) const override;
1136 
1137     bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
1138 
1139     GrAtlasSubRun* testingOnly_atlasSubRun() override;
1140 
1141     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1142 
1143     std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1144 
1145     void fillVertexData(
1146             void* vertexDst, int offset, int count,
1147             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
1148 
1149     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1150     int glyphCount() const override;
1151 
1152 private:
1153     // The rectangle that surrounds all the glyph bounding boxes in device space.
1154     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1155 
1156     const GrMaskFormat fMaskFormat;
1157     GrTextBlob* fBlob;
1158 
1159     // The scale factor between the strike size, and the source size.
1160     const SkScalar fStrikeToSourceScale;
1161 
1162     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1163     const SkRect fVertexBounds;
1164     const SkSpan<const VertexData> fVertexData;
1165 
1166     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1167     // be single threaded.
1168     mutable GlyphVector fGlyphs;
1169 
1170     const bool fUseLCDText;
1171     const bool fAntiAliased;
1172 };
1173 
SDFTSubRun(GrMaskFormat format,GrTextBlob * textBlob,SkScalar strikeToSource,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs,bool useLCDText,bool antiAliased)1174 SDFTSubRun::SDFTSubRun(GrMaskFormat format,
1175                        GrTextBlob* textBlob,
1176                        SkScalar strikeToSource,
1177                        SkRect vertexBounds,
1178                        SkSpan<const VertexData> vertexData,
1179                        GlyphVector&& glyphs,
1180                        bool useLCDText,
1181                        bool antiAliased)
1182         : fMaskFormat{format}
1183         , fBlob{textBlob}
1184         , fStrikeToSourceScale{strikeToSource}
1185         , fVertexBounds{vertexBounds}
1186         , fVertexData{vertexData}
1187         , fGlyphs{std::move(glyphs)}
1188         , fUseLCDText{useLCDText}
1189         , fAntiAliased{antiAliased} {}
1190 
has_some_antialiasing(const SkFont & font)1191 bool has_some_antialiasing(const SkFont& font ) {
1192     SkFont::Edging edging = font.getEdging();
1193     return edging == SkFont::Edging::kAntiAlias
1194            || edging == SkFont::Edging::kSubpixelAntiAlias;
1195 }
1196 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrTextBlob * blob,GrSubRunAllocator * alloc)1197 GrSubRunOwner SDFTSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1198                                const SkFont& runFont,
1199                                sk_sp<SkStrike>&& strike,
1200                                SkScalar strikeToSourceScale,
1201                                GrTextBlob* blob,
1202                                GrSubRunAllocator* alloc) {
1203     SkRect bounds = SkRectPriv::MakeLargestInverted();
1204     auto mapper = [&](const auto& d) {
1205         auto& [variant, pos] = d;
1206         SkGlyph* skGlyph = variant;
1207         int16_t l = skGlyph->left(),
1208                 t = skGlyph->top(),
1209                 r = l + skGlyph->width(),
1210                 b = t + skGlyph->height();
1211         SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
1212                 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
1213 
1214         bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1215         return VertexData{pos, {l, t, r, b}};
1216     };
1217 
1218     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, mapper);
1219 
1220     return alloc->makeUnique<SDFTSubRun>(
1221             kA8_GrMaskFormat,
1222             blob,
1223             strikeToSourceScale,
1224             bounds,
1225             vertexData,
1226             GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc),
1227             runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1228             has_some_antialiasing(runFont));
1229 }
1230 
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const1231 void SDFTSubRun::draw(const GrClip* clip,
1232                       const SkMatrixProvider& viewMatrix,
1233                       const SkGlyphRunList& glyphRunList,
1234                       const SkPaint& paint,
1235                       skgpu::v1::SurfaceDrawContext* sdc) const {
1236     auto[drawingClip, op] = this->makeAtlasTextOp(
1237             clip, viewMatrix, glyphRunList.origin(), paint, sdc, nullptr);
1238     if (op != nullptr) {
1239         sdc->addDrawOp(drawingClip, std::move(op));
1240     }
1241 }
1242 
calculate_sdf_parameters(const skgpu::v1::SurfaceDrawContext & sdc,const SkMatrix & drawMatrix,bool useLCDText,bool isAntiAliased)1243 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1244         const skgpu::v1::SurfaceDrawContext& sdc,
1245         const SkMatrix& drawMatrix,
1246         bool useLCDText,
1247         bool isAntiAliased) {
1248     const GrColorInfo& colorInfo = sdc.colorInfo();
1249     const SkSurfaceProps& props = sdc.surfaceProps();
1250     bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1251     bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1252     using MT = AtlasTextOp::MaskType;
1253     MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1254                                  : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1255                                                   : MT::kLCDDistanceField)
1256                                          : MT::kGrayscaleDistanceField;
1257 
1258     bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1259     uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1260     DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1261     DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1262     DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1263 
1264     if (isLCD) {
1265         DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1266         DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1267     }
1268     return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1269 }
1270 
1271 std::tuple<const GrClip*, GrOp::Owner >
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const1272 SDFTSubRun::makeAtlasTextOp(const GrClip* clip,
1273                             const SkMatrixProvider& viewMatrix,
1274                             SkPoint drawOrigin,
1275                             const SkPaint& paint,
1276                             skgpu::v1::SurfaceDrawContext* sdc,
1277                             GrAtlasSubRunOwner) const {
1278     SkASSERT(this->glyphCount() != 0);
1279     SkASSERT(!viewMatrix.localToDevice().hasPerspective());
1280 
1281     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1282 
1283     GrPaint grPaint;
1284     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1285 
1286     auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1287         calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
1288 
1289     auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
1290                                                        drawMatrix,
1291                                                        drawOrigin,
1292                                                        SkIRect::MakeEmpty(),
1293                                                        sk_ref_sp<GrTextBlob>(fBlob),
1294                                                        drawingColor,
1295                                                        sdc->arenaAlloc());
1296 
1297     GrRecordingContext* const rContext = sdc->recordingContext();
1298     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1299                                              maskType,
1300                                              true,
1301                                              this->glyphCount(),
1302                                              this->deviceRect(drawMatrix, drawOrigin),
1303                                              SkPaintPriv::ComputeLuminanceColor(paint),
1304                                              useGammaCorrectDistanceTable,
1305                                              DFGPFlags,
1306                                              geometry,
1307                                              std::move(grPaint));
1308 
1309     return {clip, std::move(op)};
1310 }
1311 
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1312 bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1313     const SkMatrix& initialMatrix = fBlob->initialMatrix();
1314 
1315     // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
1316     // distance field being generated, so we have to regenerate in those cases
1317     SkScalar newMaxScale = drawMatrix.getMaxScale();
1318     SkScalar oldMaxScale = initialMatrix.getMaxScale();
1319     SkScalar scaleAdjust = newMaxScale / oldMaxScale;
1320     auto [maxMinScale, minMaxScale] = fBlob->scaleBounds();
1321     if (scaleAdjust < maxMinScale || scaleAdjust > minMaxScale) {
1322         return false;
1323     }
1324     return true;
1325 }
1326 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1327 void SDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1328     fGlyphs.packedGlyphIDToGrGlyph(cache);
1329 }
1330 
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1331 std::tuple<bool, int> SDFTSubRun::regenerateAtlas(
1332         int begin, int end, GrMeshDrawTarget *target) const {
1333 
1334     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
1335 }
1336 
vertexStride(const SkMatrix & drawMatrix) const1337 size_t SDFTSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1338     return sizeof(Mask2DVertex);
1339 }
1340 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1341 void SDFTSubRun::fillVertexData(
1342         void *vertexDst, int offset, int count,
1343         GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const {
1344 
1345     using Quad = Mask2DVertex[4];
1346     SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1347     fill_transformed_vertices_2D(
1348             SkMakeZip((Quad*)vertexDst,
1349                       fGlyphs.glyphs().subspan(offset, count),
1350                       fVertexData.subspan(offset, count)),
1351             SK_DistanceFieldInset,
1352             fStrikeToSourceScale,
1353             color,
1354             positionMatrix);
1355 }
1356 
glyphCount() const1357 int SDFTSubRun::glyphCount() const {
1358     return SkCount(fVertexData);
1359 }
1360 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1361 SkRect SDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1362     SkRect outBounds = fVertexBounds;
1363     outBounds.offset(drawOrigin);
1364     return drawMatrix.mapRect(outBounds);
1365 }
1366 
testingOnly_atlasSubRun()1367 GrAtlasSubRun* SDFTSubRun::testingOnly_atlasSubRun() {
1368     return this;
1369 }
1370 }  // namespace
1371 
1372 // -- GrTextBlob::Key ------------------------------------------------------------------------------
1373 
compute_canonical_color(const SkPaint & paint,bool lcd)1374 static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
1375     SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint);
1376     if (lcd) {
1377         // This is the correct computation for canonicalColor, but there are tons of cases where LCD
1378         // can be modified. For now we just regenerate if any run in a textblob has LCD.
1379         // TODO figure out where all of these modifications are and see if we can incorporate that
1380         //      logic at a higher level *OR* use sRGB
1381         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
1382 
1383         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
1384         // see the note above.  We pick a placeholder value for LCD text to ensure we always match
1385         // the same key
1386         return SK_ColorTRANSPARENT;
1387     } else {
1388         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
1389         // gamma corrected masks anyways, nor color
1390         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
1391                                        SkColorGetG(canonicalColor),
1392                                        SkColorGetB(canonicalColor));
1393         // reduce to our finite number of bits
1394         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
1395     }
1396     return canonicalColor;
1397 }
1398 
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const GrColorInfo & colorInfo,const SkMatrix & drawMatrix,const GrSDFTControl & control)1399 auto GrTextBlob::Key::Make(const SkGlyphRunList& glyphRunList,
1400                            const SkPaint& paint,
1401                            const SkSurfaceProps& surfaceProps,
1402                            const GrColorInfo& colorInfo,
1403                            const SkMatrix& drawMatrix,
1404                            const GrSDFTControl& control) -> std::tuple<bool, Key> {
1405     SkMaskFilterBase::BlurRec blurRec;
1406     // It might be worth caching these things, but its not clear at this time
1407     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
1408     const SkMaskFilter* maskFilter = paint.getMaskFilter();
1409     bool canCache = glyphRunList.canCache() &&
1410                     !(paint.getPathEffect() ||
1411                         (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
1412 
1413     // If we're doing linear blending, then we can disable the gamma hacks.
1414     // Otherwise, leave them on. In either case, we still want the contrast boost:
1415     // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
1416     SkScalerContextFlags scalerContextFlags = colorInfo.isLinearlyBlended()
1417                                               ? SkScalerContextFlags::kBoostContrast
1418                                               : SkScalerContextFlags::kFakeGammaAndBoostContrast;
1419 
1420     GrTextBlob::Key key;
1421     if (canCache) {
1422         bool hasLCD = glyphRunList.anyRunsLCD();
1423 
1424         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
1425         SkPixelGeometry pixelGeometry =
1426                 hasLCD ? surfaceProps.pixelGeometry() : kUnknown_SkPixelGeometry;
1427 
1428         GrColor canonicalColor = compute_canonical_color(paint, hasLCD);
1429 
1430         key.fPixelGeometry = pixelGeometry;
1431         key.fUniqueID = glyphRunList.uniqueID();
1432         key.fStyle = paint.getStyle();
1433         if (key.fStyle != SkPaint::kFill_Style) {
1434             key.fFrameWidth = paint.getStrokeWidth();
1435             key.fMiterLimit = paint.getStrokeMiter();
1436             key.fJoin = paint.getStrokeJoin();
1437         }
1438         key.fHasBlur = maskFilter != nullptr;
1439         if (key.fHasBlur) {
1440             key.fBlurRec = blurRec;
1441         }
1442         key.fCanonicalColor = canonicalColor;
1443         key.fScalerContextFlags = scalerContextFlags;
1444 
1445         // Calculate the set of drawing types.
1446         key.fSetOfDrawingTypes = 0;
1447         for (auto& run : glyphRunList) {
1448             key.fSetOfDrawingTypes |= control.drawingType(run.font(), paint, drawMatrix);
1449         }
1450 
1451         if (key.fSetOfDrawingTypes & GrSDFTControl::kDirect) {
1452             // Store the fractional offset of the position. We know that the matrix can't be
1453             // perspective at this point.
1454             SkPoint mappedOrigin = drawMatrix.mapOrigin();
1455             key.fDrawMatrix = drawMatrix;
1456             key.fDrawMatrix.setTranslateX(
1457                     mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
1458             key.fDrawMatrix.setTranslateY(
1459                     mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
1460         } else {
1461             // For path and SDFT, the matrix doesn't matter.
1462             key.fDrawMatrix = SkMatrix::I();
1463         }
1464     }
1465 
1466     return {canCache, key};
1467 }
1468 
operator ==(const GrTextBlob::Key & that) const1469 bool GrTextBlob::Key::operator==(const GrTextBlob::Key& that) const {
1470     if (fUniqueID != that.fUniqueID) { return false; }
1471     if (fCanonicalColor != that.fCanonicalColor) { return false; }
1472     if (fStyle != that.fStyle) { return false; }
1473     if (fStyle != SkPaint::kFill_Style) {
1474         if (fFrameWidth != that.fFrameWidth ||
1475             fMiterLimit != that.fMiterLimit ||
1476             fJoin != that.fJoin) {
1477             return false;
1478         }
1479     }
1480     if (fPixelGeometry != that.fPixelGeometry) { return false; }
1481     if (fHasBlur != that.fHasBlur) { return false; }
1482     if (fHasBlur) {
1483         if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
1484             return false;
1485         }
1486     }
1487     if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
1488 
1489     // Just punt on perspective.
1490     if (fDrawMatrix.hasPerspective()) {
1491         return false;
1492     }
1493 
1494     if (fSetOfDrawingTypes != that.fSetOfDrawingTypes) {
1495         return false;
1496     }
1497 
1498     if (fSetOfDrawingTypes & GrSDFTControl::kDirect) {
1499         auto [compatible, _] = check_integer_translate(fDrawMatrix, that.fDrawMatrix);
1500         return compatible;
1501     }
1502 
1503     return true;
1504 }
1505 
1506 // -- GrTextBlob -----------------------------------------------------------------------------------
operator delete(void * p)1507 void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
operator new(size_t)1508 void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
operator new(size_t,void * p)1509 void* GrTextBlob::operator new(size_t, void* p) { return p; }
1510 
1511 GrTextBlob::~GrTextBlob() = default;
1512 
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkMatrix & drawMatrix,const GrSDFTControl & control,SkGlyphRunListPainter * painter)1513 sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
1514                                    const SkPaint& paint,
1515                                    const SkMatrix& drawMatrix,
1516                                    const GrSDFTControl& control,
1517                                    SkGlyphRunListPainter* painter) {
1518     // The difference in alignment from the per-glyph data to the SubRun;
1519     constexpr size_t alignDiff =
1520             alignof(DirectMaskSubRun) - alignof(DirectMaskSubRun::DevicePosition);
1521     constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
1522     size_t totalGlyphCount = glyphRunList.totalGlyphCount();
1523 
1524     // The neededForSubRun is optimized for DirectMaskSubRun which is by far the most common case.
1525     size_t bytesNeededForSubRun = GrBagOfBytes::PlatformMinimumSizeWithOverhead(
1526             totalGlyphCount * sizeof(DirectMaskSubRun::DevicePosition)
1527             + GlyphVector::GlyphVectorSize(totalGlyphCount)
1528             + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding),
1529             alignof(GrTextBlob));
1530 
1531     size_t allocationSize = sizeof(GrTextBlob) + bytesNeededForSubRun;
1532 
1533     void* allocation = ::operator new (allocationSize);
1534 
1535     SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(paint);
1536     sk_sp<GrTextBlob> blob{new (allocation)
1537                             GrTextBlob(bytesNeededForSubRun, drawMatrix, initialLuminance)};
1538 
1539     for (auto& glyphRun : glyphRunList) {
1540         painter->processGlyphRun(glyphRun,
1541                                  drawMatrix,
1542                                  paint,
1543                                  control,
1544                                  blob.get(),
1545                                  "GrTextBlob");
1546     }
1547 
1548     return blob;
1549 }
1550 
addKey(const Key & key)1551 void GrTextBlob::addKey(const Key& key) {
1552     fKey = key;
1553 }
1554 
hasPerspective() const1555 bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
1556 
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1557 bool GrTextBlob::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1558     // A singular matrix will create a GrTextBlob with no SubRuns, but unknown glyphs can
1559     // also cause empty runs. If there are no subRuns or some glyphs were excluded or perspective,
1560     // then regenerate when the matrices don't match.
1561     if ((fSubRunList.isEmpty() || fSomeGlyphsExcluded || hasPerspective()) &&
1562          fInitialMatrix != drawMatrix)
1563     {
1564         return false;
1565     }
1566 
1567     // If we have LCD text then our canonical color will be set to transparent, in this case we have
1568     // to regenerate the blob on any color change
1569     // We use the grPaint to get any color filter effects
1570     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
1571         fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
1572         return false;
1573     }
1574 
1575     for (const GrSubRun& subRun : fSubRunList) {
1576         if (!subRun.canReuse(paint, drawMatrix)) {
1577             return false;
1578         }
1579     }
1580 
1581     return true;
1582 }
1583 
key() const1584 const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
size() const1585 size_t GrTextBlob::size() const { return fSize; }
1586 
1587 template<typename AddSingleMaskFormat>
addMultiMaskFormat(AddSingleMaskFormat addSingle,const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)1588 void GrTextBlob::addMultiMaskFormat(
1589         AddSingleMaskFormat addSingle,
1590         const SkZip<SkGlyphVariant, SkPoint>& drawables,
1591         sk_sp<SkStrike>&& strike,
1592         SkScalar strikeToSourceScale) {
1593     if (drawables.empty()) { return; }
1594 
1595     auto addSameFormat = [&](const SkZip<SkGlyphVariant, SkPoint>& drawable,
1596                              GrMaskFormat format,
1597                              sk_sp<SkStrike>&& runStrike) {
1598         GrSubRunOwner subRun = addSingle(
1599                 drawable, std::move(runStrike), strikeToSourceScale, format, this, &fAlloc);
1600         if (subRun != nullptr) {
1601             fSubRunList.append(std::move(subRun));
1602         } else {
1603             fSomeGlyphsExcluded = true;
1604         }
1605     };
1606 
1607     auto glyphSpan = drawables.get<0>();
1608     SkGlyph* glyph = glyphSpan[0];
1609     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1610     size_t startIndex = 0;
1611     for (size_t i = 1; i < drawables.size(); i++) {
1612         glyph = glyphSpan[i];
1613         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1614         if (format != nextFormat) {
1615             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
1616             // Take a ref on the strike. This should rarely happen.
1617             addSameFormat(sameFormat, format, sk_sp<SkStrike>(strike));
1618             format = nextFormat;
1619             startIndex = i;
1620         }
1621     }
1622     auto sameFormat = drawables.last(drawables.size() - startIndex);
1623     addSameFormat(sameFormat, format, std::move(strike));
1624 }
1625 
GrTextBlob(int allocSize,const SkMatrix & drawMatrix,SkColor initialLuminance)1626 GrTextBlob::GrTextBlob(int allocSize,
1627                        const SkMatrix& drawMatrix,
1628                        SkColor initialLuminance)
1629         : fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2}
1630         , fSize{allocSize}
1631         , fInitialMatrix{drawMatrix}
1632         , fInitialLuminance{initialLuminance} { }
1633 
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike)1634 void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1635                                     sk_sp<SkStrike>&& strike) {
1636     SkASSERT(strike != nullptr);
1637     this->addMultiMaskFormat(DirectMaskSubRun::Make, drawables, std::move(strike), 1);
1638 }
1639 
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,SkScalar strikeToSourceScale)1640 void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1641                                     const SkFont& runFont,
1642                                     SkScalar strikeToSourceScale) {
1643     fSubRunList.append(PathSubRun::Make(drawables,
1644                                         has_some_antialiasing(runFont),
1645                                         strikeToSourceScale,
1646                                         *this,
1647                                         &fAlloc));
1648 }
1649 
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)1650 void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1651                                    sk_sp<SkStrike>&& strike,
1652                                    SkScalar strikeToSourceScale,
1653                                    const SkFont& runFont,
1654                                    SkScalar minScale,
1655                                    SkScalar maxScale) {
1656 
1657     fMaxMinScale = std::max(minScale, fMaxMinScale);
1658     fMinMaxScale = std::min(maxScale, fMinMaxScale);
1659     fSubRunList.append(
1660         SDFTSubRun::Make(drawables, runFont, std::move(strike), strikeToSourceScale,this, &fAlloc));
1661 }
1662 
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)1663 void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1664                                     sk_sp<SkStrike>&& strike,
1665                                     SkScalar strikeToSourceScale) {
1666     this->addMultiMaskFormat(
1667             TransformedMaskSubRun::Make, drawables, std::move(strike), strikeToSourceScale);
1668 }
1669 
1670 // ----------------------------- Begin no cache implementation -------------------------------------
1671 namespace {
1672 // -- DirectMaskSubRunNoCache ----------------------------------------------------------------------
1673 class DirectMaskSubRunNoCache final : public GrAtlasSubRun {
1674 public:
1675     using DevicePosition = skvx::Vec<2, int16_t>;
1676 
1677     DirectMaskSubRunNoCache(GrMaskFormat format,
1678                             const SkRect& bounds,
1679                             SkSpan<const DevicePosition> devicePositions,
1680                             GlyphVector&& glyphs);
1681 
1682     static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1683                                    sk_sp<SkStrike>&& strike,
1684                                    GrMaskFormat format,
1685                                    GrSubRunAllocator* alloc);
1686 
1687     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1688 
1689     int glyphCount() const override;
1690 
1691     std::tuple<const GrClip*, GrOp::Owner>
1692     makeAtlasTextOp(const GrClip*,
1693                     const SkMatrixProvider& viewMatrix,
1694                     SkPoint,
1695                     const SkPaint&,
1696                     skgpu::v1::SurfaceDrawContext*,
1697                     GrAtlasSubRunOwner) const override;
1698 
1699     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1700 
1701     std::tuple<bool, int>
1702     regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1703 
1704     void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
1705                         const SkMatrix& positionMatrix, SkIRect clip) const override;
1706 
1707 private:
1708     const GrMaskFormat fMaskFormat;
1709 
1710     // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1711     const SkRect fGlyphDeviceBounds;
1712     const SkSpan<const DevicePosition> fLeftTopDevicePos;
1713 
1714     // Space for geometry
1715     alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
1716 
1717     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1718     // be single threaded.
1719     mutable GlyphVector fGlyphs;
1720 };
1721 
DirectMaskSubRunNoCache(GrMaskFormat format,const SkRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GlyphVector && glyphs)1722 DirectMaskSubRunNoCache::DirectMaskSubRunNoCache(GrMaskFormat format,
1723                                                  const SkRect& deviceBounds,
1724                                                  SkSpan<const DevicePosition> devicePositions,
1725                                                  GlyphVector&& glyphs)
1726         : fMaskFormat{format}
1727         , fGlyphDeviceBounds{deviceBounds}
1728         , fLeftTopDevicePos{devicePositions}
1729         , fGlyphs{std::move(glyphs)} { }
1730 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,GrMaskFormat format,GrSubRunAllocator * alloc)1731 GrAtlasSubRunOwner DirectMaskSubRunNoCache::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1732                                                  sk_sp<SkStrike>&& strike,
1733                                                  GrMaskFormat format,
1734                                                  GrSubRunAllocator* alloc) {
1735     DevicePosition* glyphLeftTop = alloc->makePODArray<DevicePosition>(drawables.size());
1736 
1737     GlyphVector::Variant* glyphIDs = static_cast<GlyphVector::Variant*>(
1738             alloc->alignedBytes(drawables.size() * sizeof(GlyphVector::Variant),
1739                                 alignof(GlyphVector::Variant)));
1740 
1741     // Because this is the direct case, the maximum width or height is the size that fits in the
1742     // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
1743     // not overflow.
1744     constexpr SkScalar kMaxPos =
1745             std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
1746     SkGlyphRect runBounds = skglyph::empty_rect();
1747     size_t goodPosCount = 0;
1748     for (auto [variant, pos] : drawables) {
1749         auto [x, y] = pos;
1750         // Ensure that the .offset() call below does not overflow. And, at this point none of the
1751         // rectangles are empty because they were culled before the run was created. Basically,
1752         // cull all the glyphs that can't appear on the screen.
1753         if (-kMaxPos < x && x < kMaxPos && -kMaxPos  < y && y < kMaxPos) {
1754             const SkGlyph* const skGlyph = variant;
1755             const SkGlyphRect deviceBounds =
1756                     skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
1757             runBounds = skglyph::rect_union(runBounds, deviceBounds);
1758             glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
1759             glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
1760             goodPosCount += 1;
1761         }
1762     }
1763 
1764     // Wow! no glyphs are in bounds and had non-empty bounds.
1765     if (goodPosCount == 0) {
1766         return nullptr;
1767     }
1768 
1769     SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
1770     return alloc->makeUnique<DirectMaskSubRunNoCache>(
1771             format, runBounds.rect(), leftTop,
1772             GlyphVector{std::move(strike), {glyphIDs, goodPosCount}});
1773 }
1774 
vertexStride(const SkMatrix &) const1775 size_t DirectMaskSubRunNoCache::vertexStride(const SkMatrix&) const {
1776     if (fMaskFormat != kARGB_GrMaskFormat) {
1777         return sizeof(Mask2DVertex);
1778     } else {
1779         return sizeof(ARGB2DVertex);
1780     }
1781 }
1782 
glyphCount() const1783 int DirectMaskSubRunNoCache::glyphCount() const {
1784     return SkCount(fGlyphs.glyphs());
1785 }
1786 
1787 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const1788 DirectMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
1789                                          const SkMatrixProvider& viewMatrix,
1790                                          SkPoint drawOrigin,
1791                                          const SkPaint& paint,
1792                                          skgpu::v1::SurfaceDrawContext* sdc,
1793                                          GrAtlasSubRunOwner subRunOwner) const {
1794     SkASSERT(this->glyphCount() != 0);
1795 
1796     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1797 
1798     // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
1799     // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
1800     // needed.
1801     const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
1802     auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, fGlyphDeviceBounds);
1803 
1804     switch (clipMethod) {
1805         case kClippedOut:
1806             // Returning nullptr as op means skip this op.
1807             return {nullptr, nullptr};
1808         case kUnclipped:
1809         case kGeometryClipped:
1810             // GPU clip is not needed.
1811             clip = nullptr;
1812             break;
1813         case kGPUClipped:
1814             // Use the the GPU clip; clipRect is ignored.
1815             break;
1816     }
1817 
1818     if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
1819 
1820     GrPaint grPaint;
1821     const SkPMColor4f drawingColor =
1822             calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1823 
1824     GrRecordingContext* const rContext = sdc->recordingContext();
1825 
1826     auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry{
1827             *this,
1828             drawMatrix,
1829             drawOrigin,
1830             clipRect,
1831             nullptr,
1832             std::move(subRunOwner),
1833             drawingColor
1834     };
1835 
1836     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1837                                              op_mask_type(fMaskFormat),
1838                                              false,
1839                                              this->glyphCount(),
1840                                              fGlyphDeviceBounds,
1841                                              geometry,
1842                                              std::move(grPaint));
1843 
1844     return {clip, std::move(op)};
1845 }
1846 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1847 void DirectMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1848     fGlyphs.packedGlyphIDToGrGlyph(cache);
1849 }
1850 
1851 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1852 DirectMaskSubRunNoCache::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
1853     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
1854 }
1855 
1856 // The 99% case. No clip. Non-color only.
direct_2D2(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRunNoCache::DevicePosition> quadData,GrColor color)1857 void direct_2D2(SkZip<Mask2DVertex[4],
1858         const GrGlyph*,
1859         const DirectMaskSubRunNoCache::DevicePosition> quadData,
1860         GrColor color) {
1861     for (auto[quad, glyph, leftTop] : quadData) {
1862         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1863         SkScalar dl = leftTop[0],
1864                  dt = leftTop[1],
1865                  dr = dl + (ar - al),
1866                  db = dt + (ab - at);
1867 
1868         quad[0] = {{dl, dt}, color, {al, at}};  // L,T
1869         quad[1] = {{dl, db}, color, {al, ab}};  // L,B
1870         quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
1871         quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
1872     }
1873 }
1874 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1875 void DirectMaskSubRunNoCache::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
1876                                              const SkMatrix& positionMatrix, SkIRect clip) const {
1877     auto quadData = [&](auto dst) {
1878         return SkMakeZip(dst,
1879                          fGlyphs.glyphs().subspan(offset, count),
1880                          fLeftTopDevicePos.subspan(offset, count));
1881     };
1882 
1883     if (clip.isEmpty()) {
1884         if (fMaskFormat != kARGB_GrMaskFormat) {
1885             using Quad = Mask2DVertex[4];
1886             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1887             direct_2D2(quadData((Quad*)vertexDst), color);
1888         } else {
1889             using Quad = ARGB2DVertex[4];
1890             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1891             generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0});
1892         }
1893     } else {
1894         if (fMaskFormat != kARGB_GrMaskFormat) {
1895             using Quad = Mask2DVertex[4];
1896             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1897             generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
1898         } else {
1899             using Quad = ARGB2DVertex[4];
1900             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1901             generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
1902         }
1903     }
1904 }
1905 
1906 // -- TransformedMaskSubRunNoCache -----------------------------------------------------------------
1907 class TransformedMaskSubRunNoCache final : public GrAtlasSubRun {
1908 public:
1909     struct VertexData {
1910         const SkPoint pos;
1911         // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
1912         // device space rect.
1913         GrIRect16 rect;
1914     };
1915 
1916     TransformedMaskSubRunNoCache(GrMaskFormat format,
1917                                  SkScalar strikeToSourceScale,
1918                                  const SkRect& bounds,
1919                                  SkSpan<const VertexData> vertexData,
1920                                  GlyphVector&& glyphs);
1921 
1922     static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1923                                    sk_sp<SkStrike>&& strike,
1924                                    SkScalar strikeToSourceScale,
1925                                    GrMaskFormat format,
1926                                    GrSubRunAllocator* alloc);
1927 
1928     std::tuple<const GrClip*, GrOp::Owner>
1929     makeAtlasTextOp(const GrClip*,
1930                     const SkMatrixProvider& viewMatrix,
1931                     SkPoint drawOrigin,
1932                     const SkPaint&,
1933                     skgpu::v1::SurfaceDrawContext*,
1934                     GrAtlasSubRunOwner) const override;
1935 
1936     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1937 
1938     std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1939 
1940     void fillVertexData(
1941             void* vertexDst, int offset, int count,
1942             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
1943 
1944     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1945     int glyphCount() const override;
1946 
1947 private:
1948     // The rectangle that surrounds all the glyph bounding boxes in device space.
1949     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1950 
1951     const GrMaskFormat fMaskFormat;
1952 
1953     // The scale factor between the strike size, and the source size.
1954     const SkScalar fStrikeToSourceScale;
1955 
1956     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1957     const SkRect fVertexBounds;
1958     const SkSpan<const VertexData> fVertexData;
1959 
1960     // Space for geometry
1961     alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
1962 
1963     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1964     // be single threaded.
1965     mutable GlyphVector fGlyphs;
1966 };
1967 
TransformedMaskSubRunNoCache(GrMaskFormat format,SkScalar strikeToSourceScale,const SkRect & bounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs)1968 TransformedMaskSubRunNoCache::TransformedMaskSubRunNoCache(GrMaskFormat format,
1969                                                            SkScalar strikeToSourceScale,
1970                                                            const SkRect& bounds,
1971                                                            SkSpan<const VertexData> vertexData,
1972                                                            GlyphVector&& glyphs)
1973         : fMaskFormat{format}
1974         , fStrikeToSourceScale{strikeToSourceScale}
1975         , fVertexBounds{bounds}
1976         , fVertexData{vertexData}
1977         , fGlyphs{std::move(glyphs)} {}
1978 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrMaskFormat format,GrSubRunAllocator * alloc)1979 GrAtlasSubRunOwner TransformedMaskSubRunNoCache::Make(
1980         const SkZip<SkGlyphVariant, SkPoint>& drawables,
1981         sk_sp<SkStrike>&& strike,
1982         SkScalar strikeToSourceScale,
1983         GrMaskFormat format,
1984         GrSubRunAllocator* alloc) {
1985     SkRect bounds = SkRectPriv::MakeLargestInverted();
1986     auto initializer = [&](auto drawable) {
1987         auto [variant, pos] = drawable;
1988         SkGlyph* skGlyph = variant;
1989         int16_t l = skGlyph->left(),
1990                 t = skGlyph->top(),
1991                 r = l + skGlyph->width(),
1992                 b = t + skGlyph->height();
1993         SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
1994                 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
1995 
1996         bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1997         return VertexData{pos, {l, t, r, b}};
1998     };
1999 
2000     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, initializer);
2001 
2002     return alloc->makeUnique<TransformedMaskSubRunNoCache>(
2003             format, strikeToSourceScale, bounds, vertexData,
2004             GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc));
2005 }
2006 
2007 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2008 TransformedMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2009                                               const SkMatrixProvider& viewMatrix,
2010                                               SkPoint drawOrigin,
2011                                               const SkPaint& paint,
2012                                               skgpu::v1::SurfaceDrawContext* sdc,
2013                                               GrAtlasSubRunOwner subRunOwner) const {
2014     SkASSERT(this->glyphCount() != 0);
2015 
2016     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2017 
2018     GrPaint grPaint;
2019     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2020 
2021     // We can clip geometrically using clipRect and ignore clip if we're not using SDFs or
2022     // transformed glyphs, and we have an axis-aligned rectangular non-AA clip.
2023     auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry{
2024             *this,
2025             drawMatrix,
2026             drawOrigin,
2027             SkIRect::MakeEmpty(),
2028             nullptr,
2029             std::move(subRunOwner),
2030             drawingColor
2031     };
2032 
2033     GrRecordingContext* rContext = sdc->recordingContext();
2034     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2035                                              op_mask_type(fMaskFormat),
2036                                              true,
2037                                              this->glyphCount(),
2038                                              this->deviceRect(drawMatrix, drawOrigin),
2039                                              geometry,
2040                                              std::move(grPaint));
2041     return {clip, std::move(op)};
2042 }
2043 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)2044 void TransformedMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
2045     fGlyphs.packedGlyphIDToGrGlyph(cache);
2046 }
2047 
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2048 std::tuple<bool, int> TransformedMaskSubRunNoCache::regenerateAtlas(
2049         int begin, int end, GrMeshDrawTarget* target) const {
2050     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
2051 }
2052 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const2053 void TransformedMaskSubRunNoCache::fillVertexData(
2054         void* vertexDst, int offset, int count, GrColor color,
2055         const SkMatrix& positionMatrix, SkIRect clip) const {
2056     constexpr SkScalar kDstPadding = 0.f;
2057 
2058     auto quadData = [&](auto dst) {
2059         return SkMakeZip(dst,
2060                          fGlyphs.glyphs().subspan(offset, count),
2061                          fVertexData.subspan(offset, count));
2062     };
2063 
2064     if (!positionMatrix.hasPerspective()) {
2065         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
2066             using Quad = ARGB2DVertex[4];
2067             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2068             fill_transformed_vertices_2D(
2069                     quadData((Quad*) vertexDst),
2070                     kDstPadding,
2071                     fStrikeToSourceScale,
2072                     color,
2073                     positionMatrix);
2074         } else {
2075             using Quad = Mask2DVertex[4];
2076             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2077             fill_transformed_vertices_2D(
2078                     quadData((Quad*) vertexDst),
2079                     kDstPadding,
2080                     fStrikeToSourceScale,
2081                     color,
2082                     positionMatrix);
2083         }
2084     } else {
2085         if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
2086             using Quad = ARGB3DVertex[4];
2087             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2088             fill_transformed_vertices_3D(
2089                     quadData((Quad*) vertexDst),
2090                     kDstPadding,
2091                     fStrikeToSourceScale,
2092                     color,
2093                     positionMatrix);
2094         } else {
2095             using Quad = Mask3DVertex[4];
2096             SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2097             fill_transformed_vertices_3D(
2098                     quadData((Quad*) vertexDst),
2099                     kDstPadding,
2100                     fStrikeToSourceScale,
2101                     color,
2102                     positionMatrix);
2103         }
2104     }
2105 }
2106 
vertexStride(const SkMatrix & drawMatrix) const2107 size_t TransformedMaskSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2108     switch (fMaskFormat) {
2109         case kA8_GrMaskFormat:
2110             return drawMatrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
2111         case kARGB_GrMaskFormat:
2112             return drawMatrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
2113         default:
2114             SkASSERT(!drawMatrix.hasPerspective());
2115             return sizeof(Mask2DVertex);
2116     }
2117     SkUNREACHABLE;
2118 }
2119 
glyphCount() const2120 int TransformedMaskSubRunNoCache::glyphCount() const {
2121     return SkCount(fVertexData);
2122 }
2123 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2124 SkRect TransformedMaskSubRunNoCache::deviceRect(
2125         const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2126     SkRect outBounds = fVertexBounds;
2127     outBounds.offset(drawOrigin);
2128     return drawMatrix.mapRect(outBounds);
2129 }
2130 
2131 // -- SDFTSubRunNoCache ----------------------------------------------------------------------------
2132 class SDFTSubRunNoCache final : public GrAtlasSubRun {
2133 public:
2134     struct VertexData {
2135         const SkPoint pos;
2136         // The rectangle of the glyphs in strike space.
2137         GrIRect16 rect;
2138     };
2139 
2140     SDFTSubRunNoCache(GrMaskFormat format,
2141                       SkScalar strikeToSourceScale,
2142                       SkRect vertexBounds,
2143                       SkSpan<const VertexData> vertexData,
2144                       GlyphVector&& glyphs,
2145                       bool useLCDText,
2146                       bool antiAliased);
2147 
2148     static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2149                                    const SkFont& runFont,
2150                                    sk_sp<SkStrike>&& strike,
2151                                    SkScalar strikeToSourceScale,
2152                                    GrSubRunAllocator* alloc);
2153 
2154     std::tuple<const GrClip*, GrOp::Owner>
2155     makeAtlasTextOp(const GrClip*,
2156                     const SkMatrixProvider& viewMatrix,
2157                     SkPoint drawOrigin,
2158                     const SkPaint&,
2159                     skgpu::v1::SurfaceDrawContext*,
2160                     GrAtlasSubRunOwner) const override;
2161 
2162     void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
2163 
2164     std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
2165 
2166     void fillVertexData(
2167             void* vertexDst, int offset, int count,
2168             GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
2169 
2170     size_t vertexStride(const SkMatrix& drawMatrix) const override;
2171     int glyphCount() const override;
2172 
2173 private:
2174     // The rectangle that surrounds all the glyph bounding boxes in device space.
2175     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
2176 
2177     const GrMaskFormat fMaskFormat;
2178 
2179     // The scale factor between the strike size, and the source size.
2180     const SkScalar fStrikeToSourceScale;
2181 
2182     // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
2183     const SkRect fVertexBounds;
2184     const SkSpan<const VertexData> fVertexData;
2185 
2186     // Space for geometry
2187     alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
2188 
2189     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2190     // be single threaded.
2191     mutable GlyphVector fGlyphs;
2192 
2193     const bool fUseLCDText;
2194     const bool fAntiAliased;
2195 };
2196 
SDFTSubRunNoCache(GrMaskFormat format,SkScalar strikeToSourceScale,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs,bool useLCDText,bool antiAliased)2197 SDFTSubRunNoCache::SDFTSubRunNoCache(GrMaskFormat format,
2198                                      SkScalar strikeToSourceScale,
2199                                      SkRect vertexBounds,
2200                                      SkSpan<const VertexData> vertexData,
2201                                      GlyphVector&& glyphs,
2202                                      bool useLCDText,
2203                                      bool antiAliased)
2204         : fMaskFormat{format}
2205         , fStrikeToSourceScale{strikeToSourceScale}
2206         , fVertexBounds{vertexBounds}
2207         , fVertexData{vertexData}
2208         , fGlyphs{std::move(glyphs)}
2209         , fUseLCDText{useLCDText}
2210         , fAntiAliased{antiAliased} {}
2211 
2212 
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)2213 GrAtlasSubRunOwner SDFTSubRunNoCache::Make(
2214         const SkZip<SkGlyphVariant, SkPoint>& drawables,
2215         const SkFont& runFont,
2216         sk_sp<SkStrike>&& strike,
2217         SkScalar strikeToSourceScale,
2218         GrSubRunAllocator* alloc) {
2219 
2220     SkRect bounds = SkRectPriv::MakeLargestInverted();
2221     auto initializer = [&](auto drawable) {
2222         auto [variant, pos] = drawable;
2223         SkGlyph* skGlyph = variant;
2224         int16_t l = skGlyph->left(),
2225                 t = skGlyph->top(),
2226                 r = l + skGlyph->width(),
2227                 b = t + skGlyph->height();
2228         SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
2229                 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
2230 
2231         bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
2232         return VertexData{pos, {l, t, r, b}};
2233     };
2234 
2235     SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, initializer);
2236 
2237     return alloc->makeUnique<SDFTSubRunNoCache>(
2238             kA8_GrMaskFormat,
2239             strikeToSourceScale,
2240             bounds,
2241             vertexData,
2242             GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc),
2243             runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
2244             has_some_antialiasing(runFont));
2245 }
2246 
2247 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2248 SDFTSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2249                                    const SkMatrixProvider& viewMatrix,
2250                                    SkPoint drawOrigin,
2251                                    const SkPaint& paint,
2252                                    skgpu::v1::SurfaceDrawContext* sdc,
2253                                    GrAtlasSubRunOwner subRunOwner) const {
2254     SkASSERT(this->glyphCount() != 0);
2255 
2256     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2257 
2258     GrPaint grPaint;
2259     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2260 
2261     auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
2262     calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
2263 
2264     auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry {
2265             *this,
2266             drawMatrix,
2267             drawOrigin,
2268             SkIRect::MakeEmpty(),
2269             nullptr,
2270             std::move(subRunOwner),
2271             drawingColor
2272     };
2273 
2274     GrRecordingContext* rContext = sdc->recordingContext();
2275     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2276                                              maskType,
2277                                              true,
2278                                              this->glyphCount(),
2279                                              this->deviceRect(drawMatrix, drawOrigin),
2280                                              SkPaintPriv::ComputeLuminanceColor(paint),
2281                                              useGammaCorrectDistanceTable,
2282                                              DFGPFlags,
2283                                              geometry,
2284                                              std::move(grPaint));
2285 
2286     return {clip, std::move(op)};
2287 }
2288 
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)2289 void SDFTSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
2290     fGlyphs.packedGlyphIDToGrGlyph(cache);
2291 }
2292 
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2293 std::tuple<bool, int> SDFTSubRunNoCache::regenerateAtlas(
2294         int begin, int end, GrMeshDrawTarget *target) const {
2295 
2296     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
2297 }
2298 
vertexStride(const SkMatrix & drawMatrix) const2299 size_t SDFTSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2300     return sizeof(Mask2DVertex);
2301 }
2302 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const2303 void SDFTSubRunNoCache::fillVertexData(
2304         void *vertexDst, int offset, int count,
2305         GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const {
2306     using Quad = Mask2DVertex[4];
2307     SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2308     fill_transformed_vertices_2D(
2309             SkMakeZip((Quad*)vertexDst,
2310                       fGlyphs.glyphs().subspan(offset, count),
2311                       fVertexData.subspan(offset, count)),
2312             SK_DistanceFieldInset,
2313             fStrikeToSourceScale,
2314             color,
2315             positionMatrix);
2316 }
2317 
glyphCount() const2318 int SDFTSubRunNoCache::glyphCount() const {
2319     return SkCount(fVertexData);
2320 }
2321 
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2322 SkRect SDFTSubRunNoCache::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2323     SkRect outBounds = fVertexBounds;
2324     outBounds.offset(drawOrigin);
2325     return drawMatrix.mapRect(outBounds);
2326 }
2327 }  // namespace
2328 
GrSubRunNoCachePainter(skgpu::v1::SurfaceDrawContext * sdc,GrSubRunAllocator * alloc,const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint)2329 GrSubRunNoCachePainter::GrSubRunNoCachePainter(skgpu::v1::SurfaceDrawContext* sdc,
2330                                                GrSubRunAllocator* alloc,
2331                                                const GrClip* clip,
2332                                                const SkMatrixProvider& viewMatrix,
2333                                                const SkGlyphRunList& glyphRunList,
2334                                                const SkPaint& paint)
2335             : fSDC{sdc}
2336             , fAlloc{alloc}
2337             , fClip{clip}
2338             , fViewMatrix{viewMatrix}
2339             , fGlyphRunList{glyphRunList}
2340             , fPaint {paint} {}
2341 
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike)2342 void GrSubRunNoCachePainter::processDeviceMasks(
2343         const SkZip<SkGlyphVariant, SkPoint>& drawables, sk_sp<SkStrike>&& strike) {
2344     if (drawables.empty()) { return; }
2345 
2346     auto glyphSpan = drawables.get<0>();
2347     SkGlyph* glyph = glyphSpan[0];
2348     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2349     size_t startIndex = 0;
2350     for (size_t i = 1; i < drawables.size(); i++) {
2351         glyph = glyphSpan[i];
2352         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2353         if (format != nextFormat) {
2354             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
2355             // Take an extra ref on the strike. This should rarely happen.
2356             this->draw(
2357                 DirectMaskSubRunNoCache::Make(sameFormat, sk_sp<SkStrike>(strike), format, fAlloc));
2358             format = nextFormat;
2359             startIndex = i;
2360         }
2361     }
2362     auto sameFormat = drawables.last(drawables.size() - startIndex);
2363     this->draw(DirectMaskSubRunNoCache::Make(sameFormat, std::move(strike), format, fAlloc));
2364 }
2365 
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)2366 void GrSubRunNoCachePainter::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2367                                                 sk_sp<SkStrike>&& strike,
2368                                                 SkScalar strikeToSourceScale) {
2369     if (drawables.empty()) {
2370         return;
2371     }
2372 
2373     auto glyphSpan = drawables.get<0>();
2374     SkGlyph* glyph = glyphSpan[0];
2375     GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2376     size_t startIndex = 0;
2377     for (size_t i = 1; i < drawables.size(); i++) {
2378         glyph = glyphSpan[i];
2379         GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2380         if (format != nextFormat) {
2381             auto sameFormat = drawables.subspan(startIndex, i - startIndex);
2382             this->draw(
2383                 // Add an extra ref to the strike. This should rarely happen.
2384                 TransformedMaskSubRunNoCache::Make(
2385                     sameFormat, sk_sp<SkStrike>(strike), strikeToSourceScale, format, fAlloc));
2386             format = nextFormat;
2387             startIndex = i;
2388         }
2389     }
2390     auto sameFormat = drawables.last(drawables.size() - startIndex);
2391     this->draw(
2392             TransformedMaskSubRunNoCache::Make(
2393                     sameFormat, std::move(strike), strikeToSourceScale, format, fAlloc));
2394 }
2395 
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,SkScalar strikeToSourceScale)2396 void GrSubRunNoCachePainter::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2397                                                 const SkFont& runFont,
2398                                                 SkScalar strikeToSourceScale) {
2399     SkASSERT(!drawables.empty());
2400     SkPoint drawOrigin = fGlyphRunList.origin();
2401     const SkPaint& drawPaint = fPaint;
2402     SkPaint runPaint{drawPaint};
2403     runPaint.setAntiAlias(has_some_antialiasing(runFont));
2404     // If there are shaders, blurs or styles, the path must be scaled into source
2405     // space independently of the CTM. This allows the CTM to be correct for the
2406     // different effects.
2407     GrStyle style(runPaint);
2408 
2409     bool needsExactCTM = runPaint.getShader()
2410                          || style.applies()
2411                          || runPaint.getMaskFilter();
2412 
2413     // Calculate the matrix that maps the path glyphs from their size in the strike to
2414     // the graphics source space.
2415     SkMatrix strikeToSource = SkMatrix::Scale(strikeToSourceScale, strikeToSourceScale);
2416     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
2417     if (!needsExactCTM) {
2418         for (auto [variant, pos] : drawables) {
2419             const SkPath& path = *variant.path();
2420             SkMatrix pathMatrix = strikeToSource;
2421             pathMatrix.postTranslate(pos.x(), pos.y());
2422             SkPreConcatMatrixProvider strikeToDevice(fViewMatrix, pathMatrix);
2423 
2424             GrStyledShape shape(path, drawPaint);
2425             GrBlurUtils::drawShapeWithMaskFilter(
2426                     fSDC->recordingContext(), fSDC, fClip, runPaint,
2427                     strikeToDevice, shape);
2428         }
2429     } else {
2430         // Transform the path to device space because the deviceMatrix must be unchanged to
2431         // draw effect, filter or shader paths.
2432         for (auto [variant, pos] : drawables) {
2433             const SkPath& path = *variant.path();
2434             // Transform the glyph to source space.
2435             SkMatrix pathMatrix = strikeToSource;
2436             pathMatrix.postTranslate(pos.x(), pos.y());
2437 
2438             SkPath sourceSpacePath;
2439             path.transform(pathMatrix, &sourceSpacePath);
2440             sourceSpacePath.setIsVolatile(true);
2441             GrStyledShape shape(sourceSpacePath, drawPaint);
2442             GrBlurUtils::drawShapeWithMaskFilter(
2443                     fSDC->recordingContext(), fSDC, fClip, runPaint, fViewMatrix, shape);
2444         }
2445     }
2446 }
2447 
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)2448 void GrSubRunNoCachePainter::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2449                                                sk_sp<SkStrike>&& strike,
2450                                                SkScalar strikeToSourceScale,
2451                                                const SkFont& runFont,
2452                                                SkScalar minScale, SkScalar maxScale) {
2453     if (drawables.empty()) {
2454         return;
2455     }
2456     this->draw(SDFTSubRunNoCache::Make(
2457             drawables, runFont, std::move(strike), strikeToSourceScale, fAlloc));
2458 }
2459 
draw(GrAtlasSubRunOwner subRun)2460 void GrSubRunNoCachePainter::draw(GrAtlasSubRunOwner subRun) {
2461     if (subRun == nullptr) {
2462         return;
2463     }
2464     GrAtlasSubRun* subRunPtr = subRun.get();
2465     auto [drawingClip, op] = subRunPtr->makeAtlasTextOp(
2466             fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC, std::move(subRun));
2467     if (op != nullptr) {
2468         fSDC->addDrawOp(drawingClip, std::move(op));
2469     }
2470 }
2471 
2472