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