Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / src / text / gpu / TextBlob.cpp
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 #include "src/text/gpu/TextBlob.h"
8
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkScalar.h"
11 #if SK_SUPPORT_GPU
12 #include "include/gpu/GrRecordingContext.h"
13 #endif
14 #include "include/private/SkTemplates.h"
15 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
16 #include "include/private/chromium/Slug.h"
17 #include "src/core/SkEnumerate.h"
18 #include "src/core/SkFontPriv.h"
19 #include "src/core/SkGlyph.h"
20 #include "src/core/SkMaskFilterBase.h"
21 #include "src/core/SkMatrixProvider.h"
22 #include "src/core/SkPaintPriv.h"
23 #include "src/core/SkReadBuffer.h"
24 #include "src/core/SkStrikeCache.h"
25 #include "src/core/SkStrikeSpec.h"
26 #if SK_SUPPORT_GPU
27 #include "src/gpu/ganesh/GrClip.h"
28 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
29 #include "src/gpu/ganesh/GrStyle.h"
30 #include "src/gpu/ganesh/SkGr.h"
31 #include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h"
32 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
33 #endif
34
35 #include "src/text/gpu/Glyph.h"
36 #include "src/text/gpu/GlyphVector.h"
37 #include "src/text/gpu/SDFTControl.h"
38 #include "src/text/gpu/StrikeCache.h"
39 #include "src/text/gpu/SubRunAllocator.h"
40
41 #if SK_SUPPORT_GPU
42 #include "src/gpu/ganesh/GrBlurUtils.h"
43 #include "src/gpu/ganesh/ops/AtlasTextOp.h"
44 #include "src/gpu/ganesh/text/GrAtlasManager.h"
45 #include "src/gpu/ganesh/v1/Device_v1.h"
46 #include "src/gpu/ganesh/v1/SurfaceDrawContext_v1.h"
47
48 using AtlasTextOp = skgpu::v1::AtlasTextOp;
49 #endif
50
51 #include <cinttypes>
52
53 using MaskFormat = skgpu::MaskFormat;
54
55 namespace sktext::gpu {
56 // -- GPU Text -------------------------------------------------------------------------------------
57 // There are three broad types of SubRun implementations for drawing text using the GPU.
58 // TextBlob (runs with no postfix) - these runs support drawing for TextBlobs.
59 // sktext::gpu::Slug (Slug postfix) - these runs support drawing of Slugs.
60 //
61 // Naming conventions
62 //  * drawMatrix - the CTM from the canvas.
63 //  * drawOrigin - the x, y location of the drawTextBlob call.
64 //  * positionMatrix - this is the combination of the drawMatrix and the drawOrigin:
65 //        positionMatrix = drawMatrix * TranslationMatrix(drawOrigin.x, drawOrigin.y);
66 //
67 // Note:
68 //   In order to use Slugs, you need to set the fSupportBilerpFromGlyphAtlas on GrContextOptions.
69
70 enum SubRun::SubRunType : int {
71     kBad = 0,  // Make this 0 to line up with errors from readInt.
72     kDirectMask,
73     kSDFT,
74     kTransformMask,
75     kPath,
76     kDrawable,
77     kSubRunTypeCount,
78 };
79
80 // -- SubRun -------------------------------------------------------------------------------------
81 SubRun::~SubRun() = default;
82 const BlobSubRun* SubRun::blobCast() const {
83     SK_ABORT("This is not a subclass of GrBlobSubRun.");
84 }
85 }  // namespace sktext::gpu
86
87 using namespace sktext::gpu;
88 namespace {
89
90 template <typename T>
91 bool pun_read(SkReadBuffer& buffer, T* dst) {
92     return buffer.readPad32(dst, sizeof(T));
93 }
94
95 template <typename T>
96 void pun_write(SkWriteBuffer& buffer, const T& src) {
97     buffer.writePad32(&src, sizeof(T));
98 }
99
100 // -- TransformedMaskVertexFiller ------------------------------------------------------------------
101 class TransformedMaskVertexFiller {
102     struct PositionAndExtent;
103 public:
104     TransformedMaskVertexFiller(MaskFormat maskFormat,
105                                 int dstPadding,
106                                 SkScalar strikeToSourceScale,
107                                 SkRect sourceBounds,
108                                 SkSpan<const PositionAndExtent> positionAndExtent);
109
110     static TransformedMaskVertexFiller Make(MaskFormat maskType,
111                 int dstPadding,
112                 SkScalar strikeToSourceScale,
113                 const SkZip<SkGlyphVariant, SkPoint>& accepted,
114                 SubRunAllocator* alloc);
115
116     static std::optional<TransformedMaskVertexFiller> MakeFromBuffer(
117             SkReadBuffer& buffer, SubRunAllocator* alloc);
118     int unflattenSize() const;
119     void flatten(SkWriteBuffer& buffer) const;
120
121 #if SK_SUPPORT_GPU
122     size_t vertexStride(const SkMatrix& matrix) const {
123         if (fMaskType != MaskFormat::kARGB) {
124             // For formats MaskFormat::kA565 and MaskFormat::kA8 where A8 include SDF.
125             return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
126         } else {
127             // For format MaskFormat::kARGB
128             return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
129         }
130     }
131
132     void fillVertexData(int offset, int count,
133                         SkSpan<const Glyph*> glyphs,
134                         GrColor color,
135                         const SkMatrix& positionMatrix,
136                         SkIRect clip,
137                         void* vertexBuffer) const;
138
139     AtlasTextOp::MaskType opMaskType() const;
140 #endif
141     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
142     MaskFormat grMaskType() const {return fMaskType;}
143     int count() const { return SkCount(fPositionAndExtent); }
144
145 private:
146     struct PositionAndExtent {
147         const SkPoint pos;
148         // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
149         // device space rect.
150         skgpu::IRect16 rect;
151     };
152
153     struct AtlasPt {
154         uint16_t u;
155         uint16_t v;
156     };
157
158 #if SK_SUPPORT_GPU
159     // Normal text mask, SDFT, or color.
160     struct Mask2DVertex {
161         SkPoint devicePos;
162         GrColor color;
163         AtlasPt atlasPos;
164     };
165
166     struct ARGB2DVertex {
167         ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
168
169         SkPoint devicePos;
170         AtlasPt atlasPos;
171     };
172
173     // Perspective SDFT or SDFT forced to 3D or perspective color.
174     struct Mask3DVertex {
175         SkPoint3 devicePos;
176         GrColor color;
177         AtlasPt atlasPos;
178     };
179
180     struct ARGB3DVertex {
181         ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
182
183         SkPoint3 devicePos;
184         AtlasPt atlasPos;
185     };
186 #endif
187
188     std::array<SkScalar, 4> sourceRect(PositionAndExtent positionAndExtent) const;
189
190 #if SK_SUPPORT_GPU
191     template<typename Quad, typename VertexData>
192     void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
193                 GrColor color,
194                 const SkMatrix& matrix) const;
195
196     template<typename Quad, typename VertexData>
197     void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
198                 GrColor color,
199                 const SkMatrix& matrix) const;
200 #endif
201
202     const MaskFormat fMaskType;
203     const SkPoint fPaddingInset;
204     const SkScalar fStrikeToSourceScale;
205     const SkRect fSourceBounds;
206     const SkSpan<const PositionAndExtent> fPositionAndExtent;
207 };
208
209 TransformedMaskVertexFiller::TransformedMaskVertexFiller(
210     MaskFormat maskFormat,
211     int dstPadding,
212     SkScalar strikeToSourceScale,
213     SkRect sourceBounds,
214     SkSpan<const PositionAndExtent> positionAndExtent)
215         : fMaskType{maskFormat}
216         , fPaddingInset{SkPoint::Make(dstPadding, dstPadding)}
217         , fStrikeToSourceScale{strikeToSourceScale}
218         , fSourceBounds{sourceBounds}
219         , fPositionAndExtent{positionAndExtent} {}
220
221 TransformedMaskVertexFiller TransformedMaskVertexFiller::Make(
222         MaskFormat maskType,
223         int dstPadding,
224         SkScalar strikeToSourceScale,
225         const SkZip<SkGlyphVariant, SkPoint>& accepted,
226         SubRunAllocator* alloc) {
227     SkRect sourceBounds = SkRectPriv::MakeLargestInverted();
228     SkSpan<PositionAndExtent> positionAndExtent = alloc->makePODArray<PositionAndExtent>(
229             accepted,
230             [&](auto e) {
231                 auto [variant, pos] = e;
232                 const SkGlyph* skGlyph = variant;
233                 int16_t l = skGlyph->left(),
234                         t = skGlyph->top(),
235                         r = l + skGlyph->width(),
236                         b = t + skGlyph->height();
237                 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
238                         rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
239
240                 sourceBounds.joinPossiblyEmptyRect(
241                         SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
242                 return PositionAndExtent{pos, {l, t, r, b}};
243             });
244     return TransformedMaskVertexFiller{
245             maskType, dstPadding, strikeToSourceScale, sourceBounds, positionAndExtent};
246 }
247
248 static bool check_glyph_count(SkReadBuffer& buffer, int glyphCount) {
249     return 0 < glyphCount && static_cast<size_t>(glyphCount) < (buffer.available() / 4);
250 }
251
252 std::optional<TransformedMaskVertexFiller> TransformedMaskVertexFiller::MakeFromBuffer(
253         SkReadBuffer& buffer, SubRunAllocator* alloc) {
254     int checkingMaskType = buffer.readInt();
255     if (!buffer.validate(0 <= checkingMaskType && checkingMaskType < skgpu::kMaskFormatCount)) {
256         return {};
257     }
258     MaskFormat maskType = (MaskFormat)checkingMaskType;
259     int dstPadding = buffer.readInt();
260     if (!buffer.validate(0 <= dstPadding && dstPadding <= 2)) { return {}; }
261     SkScalar strikeToSourceScale = buffer.readScalar();
262     if (!buffer.validate(0 < strikeToSourceScale)) { return {}; }
263     SkRect sourceBounds = buffer.readRect();
264
265     int glyphCount = buffer.readInt();
266     if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return {}; }
267     PositionAndExtent* positionAndExtentStorage =
268             alloc->makePODArray<PositionAndExtent>(glyphCount);
269     for (int i = 0; i < glyphCount; ++i) {
270         pun_read(buffer, &positionAndExtentStorage[i]);
271     }
272     SkSpan<PositionAndExtent> positionAndExtent(positionAndExtentStorage, glyphCount);
273
274     return {TransformedMaskVertexFiller{
275             maskType, dstPadding, strikeToSourceScale, sourceBounds, positionAndExtent}};
276 }
277
278 SkRect TransformedMaskVertexFiller::deviceRect(
279         const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
280     SkRect outBounds = fSourceBounds;
281     outBounds.offset(drawOrigin);
282     return drawMatrix.mapRect(outBounds);
283 }
284
285 int TransformedMaskVertexFiller::unflattenSize() const {
286     return fPositionAndExtent.size_bytes();
287 }
288
289 void TransformedMaskVertexFiller::flatten(SkWriteBuffer& buffer) const {
290     buffer.writeInt(static_cast<int>(fMaskType));
291     buffer.writeInt(SkScalarRoundToInt(fPaddingInset.x()));
292     buffer.writeScalar(fStrikeToSourceScale);
293     buffer.writeRect(fSourceBounds);
294     buffer.writeInt(SkCount(fPositionAndExtent));
295     for (auto posAndExt : fPositionAndExtent) {
296         pun_write(buffer, posAndExt);
297     }
298 }
299
300 std::array<SkScalar, 4>
301 TransformedMaskVertexFiller::sourceRect(PositionAndExtent positionAndExtent) const {
302     auto[pos, rect] = positionAndExtent;
303     auto[l, t, r, b] = rect;
304     SkPoint LT = (SkPoint::Make(l, t) + fPaddingInset) * fStrikeToSourceScale + pos,
305             RB = (SkPoint::Make(r, b) - fPaddingInset) * fStrikeToSourceScale + pos;
306     return {LT.x(), LT.y(), RB.x(), RB.y()};
307 }
308
309 #if SK_SUPPORT_GPU
310 void TransformedMaskVertexFiller::fillVertexData(int offset, int count,
311                                                  SkSpan<const Glyph*> glyphs,
312                                                  GrColor color,
313                                                  const SkMatrix& positionMatrix,
314                                                  SkIRect clip,
315                                                  void* vertexBuffer) const {
316     auto quadData = [&](auto dst) {
317         return SkMakeZip(dst,
318                          glyphs.subspan(offset, count),
319                          fPositionAndExtent.subspan(offset, count));
320     };
321
322     if (!positionMatrix.hasPerspective()) {
323         if (fMaskType == MaskFormat::kARGB) {
324             using Quad = ARGB2DVertex[4];
325             SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
326             this->fill2D(quadData((Quad*) vertexBuffer), color, positionMatrix);
327         } else {
328             using Quad = Mask2DVertex[4];
329             SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
330             this->fill2D(quadData((Quad*) vertexBuffer), color, positionMatrix);
331         }
332     } else {
333         if (fMaskType == MaskFormat::kARGB) {
334             using Quad = ARGB3DVertex[4];
335             SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
336             this->fill3D(quadData((Quad*) vertexBuffer), color, positionMatrix);
337         } else {
338             using Quad = Mask3DVertex[4];
339             SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
340             this->fill3D(quadData((Quad*) vertexBuffer), color, positionMatrix);
341         }
342     }
343 }
344
345 template<typename Quad, typename VertexData>
346 void TransformedMaskVertexFiller::fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
347                                          GrColor color,
348                                          const SkMatrix& positionMatrix) const {
349     for (auto[quad, glyph, positionAndExtent] : quadData) {
350         auto [l, t, r, b] = this->sourceRect(positionAndExtent);
351         SkPoint lt = positionMatrix.mapXY(l, t),
352                 lb = positionMatrix.mapXY(l, b),
353                 rt = positionMatrix.mapXY(r, t),
354                 rb = positionMatrix.mapXY(r, b);
355         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
356         quad[0] = {lt, color, {al, at}};  // L,T
357         quad[1] = {lb, color, {al, ab}};  // L,B
358         quad[2] = {rt, color, {ar, at}};  // R,T
359         quad[3] = {rb, color, {ar, ab}};  // R,B
360     }
361 }
362
363 template<typename Quad, typename VertexData>
364 void TransformedMaskVertexFiller::fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
365                                          GrColor color,
366                                          const SkMatrix& positionMatrix) const {
367     auto mapXYZ = [&](SkScalar x, SkScalar y) {
368         SkPoint pt{x, y};
369         SkPoint3 result;
370         positionMatrix.mapHomogeneousPoints(&result, &pt, 1);
371         return result;
372     };
373     for (auto[quad, glyph, positionAndExtent] : quadData) {
374         auto [l, t, r, b] = this->sourceRect(positionAndExtent);
375         SkPoint3 lt = mapXYZ(l, t),
376                  lb = mapXYZ(l, b),
377                  rt = mapXYZ(r, t),
378                  rb = mapXYZ(r, b);
379         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
380         quad[0] = {lt, color, {al, at}};  // L,T
381         quad[1] = {lb, color, {al, ab}};  // L,B
382         quad[2] = {rt, color, {ar, at}};  // R,T
383         quad[3] = {rb, color, {ar, ab}};  // R,B
384     }
385 }
386
387 AtlasTextOp::MaskType TransformedMaskVertexFiller::opMaskType() const {
388     switch (fMaskType) {
389         case MaskFormat::kA8:   return AtlasTextOp::MaskType::kGrayscaleCoverage;
390         case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
391         case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
392     }
393     SkUNREACHABLE;
394 }
395 #endif
396
397 struct AtlasPt {
398     uint16_t u;
399     uint16_t v;
400 };
401
402 #if SK_SUPPORT_GPU
403 // Normal text mask, SDFT, or color.
404 struct Mask2DVertex {
405     SkPoint devicePos;
406     GrColor color;
407     AtlasPt atlasPos;
408 };
409
410 struct ARGB2DVertex {
411     ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
412
413     SkPoint devicePos;
414     AtlasPt atlasPos;
415 };
416
417 // Perspective SDFT or SDFT forced to 3D or perspective color.
418 struct Mask3DVertex {
419     SkPoint3 devicePos;
420     GrColor color;
421     AtlasPt atlasPos;
422 };
423
424 struct ARGB3DVertex {
425     ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
426
427     SkPoint3 devicePos;
428     AtlasPt atlasPos;
429 };
430
431 AtlasTextOp::MaskType op_mask_type(MaskFormat maskFormat) {
432     switch (maskFormat) {
433         case MaskFormat::kA8:   return AtlasTextOp::MaskType::kGrayscaleCoverage;
434         case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
435         case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
436     }
437     SkUNREACHABLE;
438 }
439
440 SkPMColor4f calculate_colors(skgpu::SurfaceContext* sc,
441                              const SkPaint& paint,
442                              const SkMatrixProvider& matrix,
443                              MaskFormat maskFormat,
444                              GrPaint* grPaint) {
445     GrRecordingContext* rContext = sc->recordingContext();
446     const GrColorInfo& colorInfo = sc->colorInfo();
447     if (maskFormat == MaskFormat::kARGB) {
448         SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, grPaint);
449         float a = grPaint->getColor4f().fA;
450         return {a, a, a, a};
451     }
452     SkPaintToGrPaint(rContext, colorInfo, paint, matrix, grPaint);
453     return grPaint->getColor4f();
454 }
455 #endif
456
457 SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) {
458     SkMatrix position_matrix = drawMatrix;
459     return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
460 }
461
462 // Check for integer translate with the same 2x2 matrix.
463 // Returns the translation, and true if the change from initial matrix to the position matrix
464 // support using direct glyph masks.
465 std::tuple<bool, SkVector> can_use_direct(
466         const SkMatrix& initialPositionMatrix, const SkMatrix& positionMatrix) {
467     // The existing direct glyph info can be used if the initialPositionMatrix, and the
468     // positionMatrix have the same 2x2, and the translation between them is integer.
469     // Calculate the translation in source space to a translation in device space by mapping
470     // (0, 0) through both the initial position matrix and the position matrix; take the difference.
471     SkVector translation = positionMatrix.mapOrigin() - initialPositionMatrix.mapOrigin();
472     return {initialPositionMatrix.getScaleX() == positionMatrix.getScaleX() &&
473             initialPositionMatrix.getScaleY() == positionMatrix.getScaleY() &&
474             initialPositionMatrix.getSkewX()  == positionMatrix.getSkewX()  &&
475             initialPositionMatrix.getSkewY()  == positionMatrix.getSkewY()  &&
476             SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
477             translation};
478 }
479
480 // -- PathOpSubmitter ------------------------------------------------------------------------------
481 // Shared code for submitting GPU ops for drawing glyphs as paths.
482 class PathOpSubmitter {
483     union Variant;
484
485 public:
486     PathOpSubmitter(bool isAntiAliased,
487                     SkScalar strikeToSourceScale,
488                     SkSpan<SkPoint> positions,
489                     SkSpan<SkGlyphID> glyphIDs,
490                     std::unique_ptr<SkPath[], SubRunAllocator::ArrayDestroyer> paths,
491                     const SkDescriptor& descriptor);
492
493     static PathOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
494                                 bool isAntiAliased,
495                                 SkScalar strikeToSourceScale,
496                                 const SkDescriptor& descriptor,
497                                 SubRunAllocator* alloc);
498
499     int unflattenSize() const;
500     void flatten(SkWriteBuffer& buffer) const;
501     static std::optional<PathOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
502                                                          SubRunAllocator* alloc,
503                                                          const SkStrikeClient* client);
504
505 #if SK_SUPPORT_GPU
506     void submitOps(SkCanvas*,
507                    const GrClip* clip,
508                    const SkMatrixProvider& viewMatrix,
509                    SkPoint drawOrigin,
510                    const SkPaint& paint,
511                    skgpu::v1::SurfaceDrawContext* sdc) const;
512 #endif
513
514 private:
515     const bool fIsAntiAliased;
516     const SkScalar fStrikeToSourceScale;
517     const SkSpan<const SkPoint> fPositions;
518     const SkSpan<const SkGlyphID> fGlyphIDs;
519     std::unique_ptr<SkPath[], SubRunAllocator::ArrayDestroyer> fPaths;
520     const SkAutoDescriptor fDescriptor;
521 };
522
523 int PathOpSubmitter::unflattenSize() const {
524     return fPositions.size_bytes() + fGlyphIDs.size_bytes() + SkCount(fGlyphIDs) * sizeof(SkPath);
525 }
526
527 void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const {
528     buffer.writeInt(fIsAntiAliased);
529     buffer.writeScalar(fStrikeToSourceScale);
530     buffer.writeInt(SkCount(fPositions));
531     for (auto pos : fPositions) {
532         buffer.writePoint(pos);
533     }
534     for (SkGlyphID glyphID : fGlyphIDs) {
535         buffer.writeInt(glyphID);
536     }
537     fDescriptor.getDesc()->flatten(buffer);
538 }
539
540 std::optional<PathOpSubmitter> PathOpSubmitter::MakeFromBuffer(SkReadBuffer& buffer,
541                                                                SubRunAllocator* alloc,
542                                                                const SkStrikeClient* client) {
543     bool isAntiAlias = buffer.readInt();
544     SkScalar strikeToSourceScale = buffer.readScalar();
545
546     int glyphCount = buffer.readInt();
547     if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return {}; }
548     if (!buffer.validateCanReadN<SkPoint>(glyphCount)) { return {}; }
549     SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
550     for (int i = 0; i < glyphCount; ++i) {
551         positions[i] = buffer.readPoint();
552     }
553
554     // Remember, we stored an int for glyph id.
555     if (!buffer.validateCanReadN<int>(glyphCount)) { return {}; }
556     SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
557     for (int i = 0; i < glyphCount; ++i) {
558         glyphIDs[i] = SkTo<SkGlyphID>(buffer.readInt());
559     }
560
561     auto descriptor = SkAutoDescriptor::MakeFromBuffer(buffer);
562     if (!buffer.validate(descriptor.has_value())) { return {}; }
563
564     // Translate the TypefaceID if this was transferred from the GPU process.
565     if (client != nullptr) {
566         if (!client->translateTypefaceID(&descriptor.value())) { return {}; }
567     }
568
569     auto strike = SkStrikeCache::GlobalStrikeCache()->findStrike(*descriptor->getDesc());
570     if (!buffer.validate(strike != nullptr)) { return {}; }
571
572     auto paths = alloc->makeUniqueArray<SkPath>(glyphCount);
573     SkBulkGlyphMetricsAndPaths pathGetter{std::move(strike)};
574
575     for (auto [i, glyphID] : SkMakeEnumerate(SkMakeSpan(glyphIDs, glyphCount))) {
576         const SkPath* path = pathGetter.glyph(glyphID)->path();
577         // There should never be missing paths in a sub run.
578         if (path == nullptr) { return {}; }
579         paths[i] = *path;
580     }
581
582     SkASSERT(buffer.isValid());
583     return {PathOpSubmitter{isAntiAlias,
584                             strikeToSourceScale,
585                             SkMakeSpan(positions, glyphCount),
586                             SkMakeSpan(glyphIDs, glyphCount),
587                             std::move(paths),
588                             *descriptor->getDesc()}};
589 }
590
591 PathOpSubmitter::PathOpSubmitter(
592     bool isAntiAliased,
593     SkScalar strikeToSourceScale,
594     SkSpan<SkPoint> positions,
595     SkSpan<SkGlyphID> glyphIDs,
596     std::unique_ptr<SkPath[], SubRunAllocator::ArrayDestroyer> paths,
597     const SkDescriptor& descriptor)
598         : fIsAntiAliased{isAntiAliased}
599         , fStrikeToSourceScale{strikeToSourceScale}
600         , fPositions{positions}
601         , fGlyphIDs{glyphIDs}
602         , fPaths{std::move(paths)}
603         , fDescriptor{descriptor} {
604     SkASSERT(!fPositions.empty());
605 }
606
607 PathOpSubmitter PathOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
608                                       bool isAntiAliased,
609                                       SkScalar strikeToSourceScale,
610                                       const SkDescriptor& descriptor,
611                                       SubRunAllocator* alloc) {
612     int glyphCount = SkCount(accepted);
613     SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
614     SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
615     auto paths = alloc->makeUniqueArray<SkPath>(glyphCount);
616
617     for (auto [i, variant, pos] : SkMakeEnumerate(accepted)) {
618         positions[i] = pos;
619         glyphIDs[i] = variant.glyph()->getGlyphID();
620         paths[i] = *variant.glyph()->path();
621     }
622
623     return PathOpSubmitter{isAntiAliased,
624                            strikeToSourceScale,
625                            SkMakeSpan(positions, glyphCount),
626                            SkMakeSpan(glyphIDs, glyphCount),
627                            std::move(paths),
628                            descriptor};
629 }
630
631 #if SK_SUPPORT_GPU
632 void PathOpSubmitter::submitOps(SkCanvas* canvas,
633                                 const GrClip* clip,
634                                 const SkMatrixProvider& viewMatrix,
635                                 SkPoint drawOrigin,
636                                 const SkPaint& paint,
637                                 skgpu::v1::SurfaceDrawContext* sdc) const {
638     SkPaint runPaint{paint};
639     runPaint.setAntiAlias(fIsAntiAliased);
640
641
642     SkMaskFilterBase* maskFilter = as_MFB(runPaint.getMaskFilter());
643
644     // Calculate the matrix that maps the path glyphs from their size in the strike to
645     // the graphics source space.
646     SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
647     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
648
649     // If there are shaders, non-blur mask filters or styles, the path must be scaled into source
650     // space independently of the CTM. This allows the CTM to be correct for the different effects.
651     GrStyle style(runPaint);
652     bool needsExactCTM = runPaint.getShader()
653                          || style.applies()
654                          || (maskFilter != nullptr && !maskFilter->asABlur(nullptr));
655     if (!needsExactCTM) {
656         SkMaskFilterBase::BlurRec blurRec;
657
658         // If there is a blur mask filter, then sigma needs to be adjusted to account for the
659         // scaling of fStrikeToSourceScale.
660         if (maskFilter != nullptr && maskFilter->asABlur(&blurRec)) {
661             runPaint.setMaskFilter(
662                     SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma / fStrikeToSourceScale));
663         }
664         for (auto [path, pos] : SkMakeZip(fPaths.get(), fPositions)) {
665             // Transform the glyph to source space.
666             SkMatrix pathMatrix = strikeToSource;
667             pathMatrix.postTranslate(pos.x(), pos.y());
668
669             SkAutoCanvasRestore acr(canvas, true);
670             canvas->concat(pathMatrix);
671             canvas->drawPath(path, runPaint);
672         }
673     } else {
674         // Transform the path to device because the deviceMatrix must be unchanged to
675         // draw effect, filter or shader paths.
676         for (auto [path, pos] : SkMakeZip(fPaths.get(), fPositions)) {
677             // Transform the glyph to source space.
678             SkMatrix pathMatrix = strikeToSource;
679             pathMatrix.postTranslate(pos.x(), pos.y());
680
681             SkPath deviceOutline;
682             path.transform(pathMatrix, &deviceOutline);
683             deviceOutline.setIsVolatile(true);
684             canvas->drawPath(deviceOutline, runPaint);
685         }
686     }
687 }
688 #endif
689
690 // -- PathSubRun -----------------------------------------------------------------------------------
691 class PathSubRun final : public SubRun {
692 public:
693     PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {}
694
695     static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
696                             bool isAntiAliased,
697                             SkScalar strikeToSourceScale,
698                             const SkDescriptor& descriptor,
699                             SubRunAllocator* alloc) {
700         return alloc->makeUnique<PathSubRun>(
701                 PathOpSubmitter::Make(
702                         accepted, isAntiAliased, strikeToSourceScale, descriptor, alloc));
703     }
704
705 #if SK_SUPPORT_GPU
706     void draw(SkCanvas* canvas,
707               const GrClip* clip,
708               const SkMatrixProvider& viewMatrix,
709               SkPoint drawOrigin,
710               const SkPaint& paint,
711               sk_sp<SkRefCnt> subRunStorage,
712               skgpu::v1::SurfaceDrawContext* sdc) const override {
713         fPathDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
714     }
715 #endif
716
717     int unflattenSize() const override;
718
719     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
720         return true;
721     }
722     const AtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
723     static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
724                                       SkReadBuffer& buffer,
725                                       SubRunAllocator* alloc,
726                                       const SkStrikeClient* client);
727
728 protected:
729     SubRunType subRunType() const override { return kPath; }
730     void doFlatten(SkWriteBuffer& buffer) const override;
731
732 private:
733     PathOpSubmitter fPathDrawing;
734 };
735
736 int PathSubRun::unflattenSize() const {
737     return sizeof(PathSubRun) + fPathDrawing.unflattenSize();
738 }
739
740 void PathSubRun::doFlatten(SkWriteBuffer& buffer) const {
741     fPathDrawing.flatten(buffer);
742 }
743
744 SubRunOwner PathSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
745                                        SkReadBuffer& buffer,
746                                        SubRunAllocator* alloc,
747                                        const SkStrikeClient* client) {
748     auto pathOpSubmitter = PathOpSubmitter::MakeFromBuffer(buffer, alloc, client);
749     if (!buffer.validate(pathOpSubmitter.has_value())) { return nullptr; }
750     return alloc->makeUnique<PathSubRun>(std::move(*pathOpSubmitter));
751 }
752
753 // -- DrawableOpSubmitter --------------------------------------------------------------------------
754 // Shared code for submitting GPU ops for drawing glyphs as drawables.
755 class DrawableOpSubmitter {
756 public:
757     DrawableOpSubmitter(SkScalar strikeToSourceScale,
758                         SkSpan<SkPoint> positions,
759                         SkSpan<SkGlyphID> glyphIDs,
760                         SkSpan<SkDrawable*> drawableData,
761                         sk_sp<SkStrike>&& strike,
762                         const SkDescriptor& descriptor);
763
764     static DrawableOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
765                                     sk_sp<SkStrike>&& strike,
766                                     SkScalar strikeToSourceScale,
767                                     const SkDescriptor& descriptor,
768                                     SubRunAllocator* alloc);
769
770     int unflattenSize() const;
771     void flatten(SkWriteBuffer& buffer) const;
772     static std::optional<DrawableOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
773                                                              SubRunAllocator* alloc,
774                                                              const SkStrikeClient* client);
775 #if SK_SUPPORT_GPU
776     void submitOps(SkCanvas*,
777                    const GrClip* clip,
778                    const SkMatrixProvider& viewMatrix,
779                    SkPoint drawOrigin,
780                    const SkPaint& paint,
781                    skgpu::v1::SurfaceDrawContext* sdc) const;
782 #endif
783
784 private:
785     const SkScalar fStrikeToSourceScale;
786     const SkSpan<SkPoint> fPositions;
787     const SkSpan<SkGlyphID> fGlyphIDs;
788     const SkSpan<SkDrawable*> fDrawables;
789     sk_sp<SkStrike> fStrike;  // Owns the fDrawables.
790     const SkAutoDescriptor fDescriptor;
791 };
792
793 int DrawableOpSubmitter::unflattenSize() const {
794     return fPositions.size_bytes() +
795            fGlyphIDs.size_bytes() +
796            SkCount(fPositions) * sizeof(sk_sp<SkDrawable>);
797 }
798
799 void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const {
800     buffer.writeScalar(fStrikeToSourceScale);
801     buffer.writeInt(SkCount(fPositions));
802     for (auto pos : fPositions) {
803         buffer.writePoint(pos);
804     }
805     for (SkGlyphID glyphID : fGlyphIDs) {
806         buffer.writeInt(glyphID);
807     }
808     fDescriptor.getDesc()->flatten(buffer);
809 }
810
811 std::optional<DrawableOpSubmitter> DrawableOpSubmitter::MakeFromBuffer(
812         SkReadBuffer& buffer, SubRunAllocator* alloc, const SkStrikeClient* client) {
813     SkScalar strikeToSourceScale = buffer.readScalar();
814
815     int glyphCount = buffer.readInt();
816     if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return {}; }
817     if (!buffer.validateCanReadN<SkPoint>(glyphCount)) { return {}; }
818     SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
819     for (int i = 0; i < glyphCount; ++i) {
820         positions[i] = buffer.readPoint();
821     }
822
823     // Remember, we stored an int for glyph id.
824     if (!buffer.validateCanReadN<int>(glyphCount)) { return {}; }
825     SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
826     for (int i = 0; i < glyphCount; ++i) {
827         glyphIDs[i] = SkTo<SkGlyphID>(buffer.readInt());
828     }
829
830     auto descriptor = SkAutoDescriptor::MakeFromBuffer(buffer);
831     if (!buffer.validate(descriptor.has_value())) { return {}; }
832
833     // Translate the TypefaceID if this was transferred from the GPU process.
834     if (client != nullptr) {
835         if (!client->translateTypefaceID(&descriptor.value())) { return {}; }
836     }
837
838     auto strike = SkStrikeCache::GlobalStrikeCache()->findStrike(*descriptor->getDesc());
839     if (!buffer.validate(strike != nullptr)) { return {}; }
840
841     auto drawables = alloc->makePODArray<SkDrawable*>(glyphCount);
842     SkBulkGlyphMetricsAndDrawables drawableGetter(sk_sp<SkStrike>{strike});
843     auto glyphs = drawableGetter.glyphs(SkMakeSpan(glyphIDs, glyphCount));
844
845     for (auto [i, glyph] : SkMakeEnumerate(glyphs)) {
846         drawables[i] = glyph->drawable();
847     }
848
849     SkASSERT(buffer.isValid());
850     return {DrawableOpSubmitter{strikeToSourceScale,
851                                 SkMakeSpan(positions, glyphCount),
852                                 SkMakeSpan(glyphIDs, glyphCount),
853                                 SkMakeSpan(drawables, glyphCount),
854                                 std::move(strike),
855                                 *descriptor->getDesc()}};
856 }
857
858 DrawableOpSubmitter::DrawableOpSubmitter(
859         SkScalar strikeToSourceScale,
860         SkSpan<SkPoint> positions,
861         SkSpan<SkGlyphID> glyphIDs,
862         SkSpan<SkDrawable*> drawables,
863         sk_sp<SkStrike>&& strike,
864         const SkDescriptor& descriptor)
865             : fStrikeToSourceScale{strikeToSourceScale}
866             , fPositions{positions}
867             , fGlyphIDs{glyphIDs}
868             , fDrawables{std::move(drawables)}
869             , fStrike(std::move(strike))
870             , fDescriptor{descriptor} {
871     SkASSERT(!fPositions.empty());
872 }
873
874 DrawableOpSubmitter DrawableOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
875                                               sk_sp<SkStrike>&& strike,
876                                               SkScalar strikeToSourceScale,
877                                               const SkDescriptor& descriptor,
878                                               SubRunAllocator* alloc) {
879     int glyphCount = SkCount(accepted);
880     SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
881     SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
882     SkDrawable** drawables = alloc->makePODArray<SkDrawable*>(glyphCount);
883     for (auto [i, variant, pos] : SkMakeEnumerate(accepted)) {
884         positions[i] = pos;
885         glyphIDs[i] = variant.glyph()->getGlyphID();
886         drawables[i] = variant.glyph()->drawable();
887     }
888
889     return DrawableOpSubmitter{strikeToSourceScale,
890                                SkMakeSpan(positions, glyphCount),
891                                SkMakeSpan(glyphIDs, glyphCount),
892                                SkMakeSpan(drawables, glyphCount),
893                                std::move(strike),
894                                descriptor};
895 }
896
897 #if SK_SUPPORT_GPU
898 void DrawableOpSubmitter::submitOps(SkCanvas* canvas,
899                                     const GrClip* clip,
900                                     const SkMatrixProvider& viewMatrix,
901                                     SkPoint drawOrigin,
902                                     const SkPaint& paint,
903                                     skgpu::v1::SurfaceDrawContext* sdc) const {
904     // Calculate the matrix that maps the path glyphs from their size in the strike to
905     // the graphics source space.
906     SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
907     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
908
909     // Transform the path to device because the deviceMatrix must be unchanged to
910     // draw effect, filter or shader paths.
911     for (auto [i, position] : SkMakeEnumerate(fPositions)) {
912         SkDrawable* drawable = fDrawables[i];
913         // Transform the glyph to source space.
914         SkMatrix pathMatrix = strikeToSource;
915         pathMatrix.postTranslate(position.x(), position.y());
916
917         SkAutoCanvasRestore acr(canvas, false);
918         SkRect drawableBounds = drawable->getBounds();
919         pathMatrix.mapRect(&drawableBounds);
920         canvas->saveLayer(&drawableBounds, &paint);
921         drawable->draw(canvas, &pathMatrix);
922     }
923 }
924 #endif
925
926 template <typename SubRunT>
927 SubRunOwner make_drawable_sub_run(const SkZip<SkGlyphVariant, SkPoint>& drawables,
928                                   sk_sp<SkStrike>&& strike,
929                                   SkScalar strikeToSourceScale,
930                                   const SkDescriptor& descriptor,
931                                   SubRunAllocator* alloc) {
932     return alloc->makeUnique<SubRunT>(
933             DrawableOpSubmitter::Make(drawables, std::move(strike),
934                                       strikeToSourceScale, descriptor, alloc));
935 }
936
937 // -- DrawableSubRun -------------------------------------------------------------------------------
938 class DrawableSubRun : public SubRun {
939 public:
940     DrawableSubRun(DrawableOpSubmitter&& drawingDrawing)
941             : fDrawingDrawing(std::move(drawingDrawing)) {}
942
943     static SubRunOwner MakeFromBuffer(const SkMatrix&,
944                                       SkReadBuffer& buffer,
945                                       SubRunAllocator* alloc,
946                                       const SkStrikeClient* client);
947 #if SK_SUPPORT_GPU
948     void draw(SkCanvas* canvas,
949               const GrClip* clip,
950               const SkMatrixProvider& viewMatrix,
951               SkPoint drawOrigin,
952               const SkPaint& paint,
953               sk_sp<SkRefCnt> subRunStorage,
954               skgpu::v1::SurfaceDrawContext* sdc) const override {
955         fDrawingDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
956     }
957 #endif
958
959     int unflattenSize() const override;
960
961     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
962
963     const AtlasSubRun* testingOnly_atlasSubRun() const override;
964
965 protected:
966     SubRunType subRunType() const override { return kDrawable; }
967     void doFlatten(SkWriteBuffer& buffer) const override;
968
969 private:
970     DrawableOpSubmitter fDrawingDrawing;
971 };
972
973 int DrawableSubRun::unflattenSize() const {
974     return sizeof(DrawableSubRun) + fDrawingDrawing.unflattenSize();
975 }
976
977 void DrawableSubRun::doFlatten(SkWriteBuffer& buffer) const {
978     fDrawingDrawing.flatten(buffer);
979 }
980
981 SubRunOwner DrawableSubRun::MakeFromBuffer(const SkMatrix&,
982                                            SkReadBuffer& buffer,
983                                            SubRunAllocator* alloc,
984                                            const SkStrikeClient* client) {
985     auto drawableOpSubmitter = DrawableOpSubmitter::MakeFromBuffer(buffer, alloc, client);
986     if (!buffer.validate(drawableOpSubmitter.has_value())) { return nullptr; }
987     return alloc->makeUnique<DrawableSubRun>(std::move(*drawableOpSubmitter));
988 }
989
990 bool DrawableSubRun::canReuse( const SkPaint& paint, const SkMatrix& positionMatrix) const {
991     return true;
992 }
993
994 const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const {
995     return nullptr;
996 }
997
998 namespace {
999 using DevicePosition = skvx::Vec<2, int16_t>;
1000
1001 #if SK_SUPPORT_GPU
1002 enum ClipMethod {
1003     kClippedOut,
1004     kUnclipped,
1005     kGPUClipped,
1006     kGeometryClipped
1007 };
1008
1009 std::tuple<ClipMethod, SkIRect>
1010 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
1011     if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
1012         return {kClippedOut, SkIRect::MakeEmpty()};
1013     } else if (clip != nullptr) {
1014         switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
1015             case GrClip::Effect::kClippedOut:
1016                 return {kClippedOut, SkIRect::MakeEmpty()};
1017             case GrClip::Effect::kUnclipped:
1018                 return {kUnclipped, SkIRect::MakeEmpty()};
1019             case GrClip::Effect::kClipped: {
1020                 if (result.fIsRRect && result.fRRect.isRect()) {
1021                     SkRect r = result.fRRect.rect();
1022                     if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
1023                         SkIRect clipRect = SkIRect::MakeEmpty();
1024                         // Clip geometrically during onPrepare using clipRect.
1025                         r.round(&clipRect);
1026                         if (clipRect.contains(glyphBounds)) {
1027                             // If fully within the clip, signal no clipping using the empty rect.
1028                             return {kUnclipped, SkIRect::MakeEmpty()};
1029                         }
1030                         // Use the clipRect to clip the geometry.
1031                         return {kGeometryClipped, clipRect};
1032                     }
1033                     // Partial pixel clipped at this point. Have the GPU handle it.
1034                 }
1035             }
1036             break;
1037         }
1038     }
1039     return {kGPUClipped, SkIRect::MakeEmpty()};
1040 }
1041
1042 // The 99% case. No clip. Non-color only.
1043 void direct_2D(SkZip<Mask2DVertex[4],
1044                      const Glyph*,
1045                      const DevicePosition> quadData,
1046                GrColor color,
1047                SkPoint originOffset) {
1048     for (auto[quad, glyph, leftTop] : quadData) {
1049         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1050         SkScalar dl = leftTop[0] + originOffset.x(),
1051                  dt = leftTop[1] + originOffset.y(),
1052                  dr = dl + (ar - al),
1053                  db = dt + (ab - at);
1054
1055         quad[0] = {{dl, dt}, color, {al, at}};  // L,T
1056         quad[1] = {{dl, db}, color, {al, ab}};  // L,B
1057         quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
1058         quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
1059     }
1060 }
1061 }  // namespace
1062
1063 template <typename Rect>
1064 auto ltbr(const Rect& r) {
1065     return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
1066 }
1067
1068 // Handle any combination of BW or color and clip or no clip.
1069 template<typename Quad, typename VertexData>
1070 void generalized_direct_2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1071                            GrColor color,
1072                            SkPoint originOffset,
1073                            SkIRect* clip = nullptr) {
1074     for (auto[quad, glyph, leftTop] : quadData) {
1075         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1076         uint16_t w = ar - al,
1077                  h = ab - at;
1078         SkScalar l = (SkScalar)leftTop[0] + originOffset.x(),
1079                  t = (SkScalar)leftTop[1] + originOffset.y();
1080         if (clip == nullptr) {
1081             auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
1082             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
1083             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
1084             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
1085             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
1086         } else {
1087             SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
1088             SkScalar dl, dt, dr, db;
1089             if (!clip->containsNoEmptyCheck(devIRect)) {
1090                 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
1091                     al += clipped.left()   - devIRect.left();
1092                     at += clipped.top()    - devIRect.top();
1093                     ar += clipped.right()  - devIRect.right();
1094                     ab += clipped.bottom() - devIRect.bottom();
1095                     std::tie(dl, dt, dr, db) = ltbr(clipped);
1096                 } else {
1097                     // TODO: omit generating any vertex data for fully clipped glyphs ?
1098                     std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
1099                     std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
1100                 }
1101             } else {
1102                 std::tie(dl, dt, dr, db) = ltbr(devIRect);
1103             }
1104             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
1105             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
1106             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
1107             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
1108         }
1109     }
1110 #endif  // SK_SUPPORT_GPU
1111 }
1112
1113 // -- DirectMaskSubRun -------------------------------------------------------------------------
1114 class DirectMaskSubRun final : public SubRun, public AtlasSubRun {
1115 public:
1116     DirectMaskSubRun(MaskFormat format,
1117                      const SkMatrix& initialPositionMatrix,
1118                      SkGlyphRect deviceBounds,
1119                      SkSpan<const DevicePosition> devicePositions,
1120                      GlyphVector&& glyphs,
1121                      bool glyphsOutOfBounds);
1122
1123     static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1124                             const SkMatrix& initialPositionMatrix,
1125                             sk_sp<SkStrike>&& strike,
1126                             MaskFormat format,
1127                             SubRunAllocator* alloc);
1128
1129     static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1130                                       SkReadBuffer& buffer,
1131                                       SubRunAllocator* alloc,
1132                                       const SkStrikeClient* client);
1133 #if SK_SUPPORT_GPU
1134     void draw(SkCanvas*,
1135               const GrClip* clip,
1136               const SkMatrixProvider& viewMatrix,
1137               SkPoint drawOrigin,
1138               const SkPaint& paint,
1139               sk_sp<SkRefCnt> subRunOwner,
1140               skgpu::v1::SurfaceDrawContext* sdc) const override;
1141 #endif
1142
1143     int unflattenSize() const override;
1144
1145     int glyphCount() const override;
1146
1147     void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override;
1148
1149 #if SK_SUPPORT_GPU
1150     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1151
1152     std::tuple<const GrClip*, GrOp::Owner>
1153     makeAtlasTextOp(const GrClip*,
1154                     const SkMatrixProvider& viewMatrix,
1155                     SkPoint,
1156                     const SkPaint&,
1157                     sk_sp<SkRefCnt>&& subRunStorage,
1158                     skgpu::v1::SurfaceDrawContext*) const override;
1159
1160     std::tuple<bool, int>
1161     regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1162
1163     void fillVertexData(void* vertexDst, int offset, int count,
1164                         GrColor color,
1165                         const SkMatrix& drawMatrix, SkPoint drawOrigin,
1166                         SkIRect clip) const override;
1167 #endif
1168
1169     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1170
1171     const AtlasSubRun* testingOnly_atlasSubRun() const override;
1172
1173 protected:
1174     SubRunType subRunType() const override { return kDirectMask; }
1175     void doFlatten(SkWriteBuffer& buffer) const override;
1176
1177 private:
1178     // Return true if the positionMatrix represents an integer translation. Return the device
1179     // bounding box of all the glyphs. If the bounding box is empty, then something went singular
1180     // and this operation should be dropped.
1181     std::tuple<bool, SkRect> deviceRectAndCheckTransform(const SkMatrix& positionMatrix) const;
1182
1183     const MaskFormat fMaskFormat;
1184     const SkMatrix& fInitialPositionMatrix;
1185
1186     // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1187     const SkGlyphRect fGlyphDeviceBounds;
1188     const SkSpan<const DevicePosition> fLeftTopDevicePos;
1189     const bool fSomeGlyphsExcluded;
1190
1191     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1192     // be single threaded.
1193     mutable GlyphVector fGlyphs;
1194 };
1195
1196 DirectMaskSubRun::DirectMaskSubRun(MaskFormat format,
1197                                    const SkMatrix& initialPositionMatrix,
1198                                    SkGlyphRect deviceBounds,
1199                                    SkSpan<const DevicePosition> devicePositions,
1200                                    GlyphVector&& glyphs,
1201                                    bool glyphsOutOfBounds)
1202         : fMaskFormat{format}
1203         , fInitialPositionMatrix{initialPositionMatrix}
1204         , fGlyphDeviceBounds{deviceBounds}
1205         , fLeftTopDevicePos{devicePositions}
1206         , fSomeGlyphsExcluded{glyphsOutOfBounds}
1207         , fGlyphs{std::move(glyphs)} { }
1208
1209 SubRunOwner DirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1210                                    const SkMatrix& initialPositionMatrix,
1211                                    sk_sp<SkStrike>&& strike,
1212                                    MaskFormat format,
1213                                    SubRunAllocator* alloc) {
1214     auto glyphLeftTop = alloc->makePODArray<DevicePosition>(accepted.size());
1215     auto glyphIDs = alloc->makePODArray<GlyphVector::Variant>(accepted.size());
1216
1217     // Because this is the direct case, the maximum width or height is the size that fits in the
1218     // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
1219     // not overflow.
1220     constexpr SkScalar kMaxPos =
1221             std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
1222     SkGlyphRect runBounds = skglyph::empty_rect();
1223     size_t goodPosCount = 0;
1224     for (auto [variant, pos] : accepted) {
1225         auto [x, y] = pos;
1226         // Ensure that the .offset() call below does not overflow. And, at this point none of the
1227         // rectangles are empty because they were culled before the run was created. Basically,
1228         // cull all the glyphs that can't appear on the screen.
1229         if (-kMaxPos < x && x < kMaxPos && -kMaxPos  < y && y < kMaxPos) {
1230             const SkGlyph* const skGlyph = variant;
1231             const SkGlyphRect deviceBounds =
1232                     skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
1233             runBounds = skglyph::rect_union(runBounds, deviceBounds);
1234             glyphLeftTop[goodPosCount] = deviceBounds.leftTop();
1235             glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
1236             goodPosCount += 1;
1237         }
1238     }
1239
1240     // Wow! no glyphs are in bounds and had non-empty bounds.
1241     if (goodPosCount == 0) {
1242         return nullptr;
1243     }
1244
1245     // If some glyphs were excluded by the bounds, then this subrun can't be generally be used
1246     // for other draws. Mark the subrun as not general.
1247     bool glyphsExcluded = goodPosCount != accepted.size();
1248     SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
1249     return alloc->makeUnique<DirectMaskSubRun>(
1250             format, initialPositionMatrix, runBounds, leftTop,
1251             GlyphVector{std::move(strike), {glyphIDs, goodPosCount}},
1252             glyphsExcluded);
1253 }
1254
1255 bool DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1256     auto [reuse, translation] =
1257             can_use_direct(fInitialPositionMatrix, positionMatrix);
1258
1259     // If glyphs were excluded because of position bounds, then this subrun can only be reused if
1260     // there is no change in position.
1261     if (fSomeGlyphsExcluded) {
1262         return translation.x() == 0 && translation.y() == 0;
1263     }
1264
1265     return reuse;
1266 }
1267
1268 SubRunOwner DirectMaskSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1269                                              SkReadBuffer& buffer,
1270                                              SubRunAllocator* alloc,
1271                                              const SkStrikeClient* client) {
1272     MaskFormat maskType = (MaskFormat)buffer.readInt();
1273     SkGlyphRect runBounds;
1274     pun_read(buffer, &runBounds);
1275
1276     int glyphCount = buffer.readInt();
1277     if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return nullptr; }
1278     if (!buffer.validateCanReadN<DevicePosition>(glyphCount)) { return nullptr; }
1279     DevicePosition* positionsData = alloc->makePODArray<DevicePosition>(glyphCount);
1280     for (int i = 0; i < glyphCount; ++i) {
1281         pun_read(buffer, &positionsData[i]);
1282     }
1283     SkSpan<DevicePosition> positions(positionsData, glyphCount);
1284
1285     auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1286     if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1287     if (!buffer.validate(SkCount(glyphVector->glyphs()) == glyphCount)) { return nullptr; }
1288     SkASSERT(buffer.isValid());
1289     return alloc->makeUnique<DirectMaskSubRun>(
1290             maskType, initialPositionMatrix, runBounds, positions,
1291             std::move(glyphVector.value()), false);
1292 }
1293
1294 void DirectMaskSubRun::doFlatten(SkWriteBuffer& buffer) const {
1295     buffer.writeInt(static_cast<int>(fMaskFormat));
1296     pun_write(buffer, fGlyphDeviceBounds);
1297     int glyphCount = SkTo<int>(fLeftTopDevicePos.size());
1298     buffer.writeInt(glyphCount);
1299     for (auto pos : fLeftTopDevicePos) {
1300         pun_write(buffer, pos);
1301     }
1302     fGlyphs.flatten(buffer);
1303 }
1304
1305 int DirectMaskSubRun::unflattenSize() const {
1306     return sizeof(DirectMaskSubRun) +
1307            fGlyphs.unflattenSize() +
1308            sizeof(DevicePosition) * fGlyphs.glyphs().size();
1309 }
1310
1311 const AtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() const {
1312     return this;
1313 }
1314
1315 int DirectMaskSubRun::glyphCount() const {
1316     return SkCount(fGlyphs.glyphs());
1317 }
1318
1319 #if SK_SUPPORT_GPU
1320 size_t DirectMaskSubRun::vertexStride(const SkMatrix& positionMatrix) const {
1321     if (!positionMatrix.hasPerspective()) {
1322         if (fMaskFormat != MaskFormat::kARGB) {
1323             return sizeof(Mask2DVertex);
1324         } else {
1325             return sizeof(ARGB2DVertex);
1326         }
1327     } else {
1328         if (fMaskFormat != MaskFormat::kARGB) {
1329             return sizeof(Mask3DVertex);
1330         } else {
1331             return sizeof(ARGB3DVertex);
1332         }
1333     }
1334 }
1335
1336 void DirectMaskSubRun::draw(SkCanvas*,
1337                             const GrClip* clip,
1338                             const SkMatrixProvider& viewMatrix,
1339                             SkPoint drawOrigin,
1340                             const SkPaint& paint,
1341                             sk_sp<SkRefCnt> subRunStorage,
1342                             skgpu::v1::SurfaceDrawContext* sdc) const {
1343     auto[drawingClip, op] = this->makeAtlasTextOp(
1344             clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1345     if (op != nullptr) {
1346         sdc->addDrawOp(drawingClip, std::move(op));
1347     }
1348 }
1349
1350 std::tuple<const GrClip*, GrOp::Owner> DirectMaskSubRun::makeAtlasTextOp(
1351         const GrClip* clip,
1352         const SkMatrixProvider& viewMatrix,
1353         SkPoint drawOrigin,
1354         const SkPaint& paint,
1355         sk_sp<SkRefCnt>&& subRunStorage,
1356         skgpu::v1::SurfaceDrawContext* sdc) const {
1357     SkASSERT(this->glyphCount() != 0);
1358     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1359     const SkMatrix& positionMatrix = position_matrix(drawMatrix, drawOrigin);
1360
1361     auto [integerTranslate, subRunDeviceBounds] = this->deviceRectAndCheckTransform(positionMatrix);
1362     if (subRunDeviceBounds.isEmpty()) {
1363         return {nullptr, nullptr};
1364     }
1365     // Rect for optimized bounds clipping when doing an integer translate.
1366     SkIRect geometricClipRect = SkIRect::MakeEmpty();
1367     if (integerTranslate) {
1368         // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
1369         // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
1370         // needed.
1371         const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
1372         auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds);
1373
1374         switch (clipMethod) {
1375             case kClippedOut:
1376                 // Returning nullptr as op means skip this op.
1377                 return {nullptr, nullptr};
1378             case kUnclipped:
1379             case kGeometryClipped:
1380                 // GPU clip is not needed.
1381                 clip = nullptr;
1382                 break;
1383             case kGPUClipped:
1384                 // Use th GPU clip; clipRect is ignored.
1385                 break;
1386         }
1387         geometricClipRect = clipRect;
1388
1389         if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); }
1390     }
1391
1392     GrPaint grPaint;
1393     const SkPMColor4f drawingColor =
1394             calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1395
1396     auto geometry = AtlasTextOp::Geometry::Make(*this,
1397                                                 drawMatrix,
1398                                                 drawOrigin,
1399                                                 geometricClipRect,
1400                                                 std::move(subRunStorage),
1401                                                 drawingColor,
1402                                                 sdc->arenaAlloc());
1403
1404     GrRecordingContext* const rContext = sdc->recordingContext();
1405     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1406                                              op_mask_type(fMaskFormat),
1407                                              !integerTranslate,
1408                                              this->glyphCount(),
1409                                              subRunDeviceBounds,
1410                                              geometry,
1411                                              std::move(grPaint));
1412     return {clip, std::move(op)};
1413 }
1414 #endif
1415
1416 void DirectMaskSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
1417     fGlyphs.packedGlyphIDToGlyph(cache);
1418 }
1419
1420 #if SK_SUPPORT_GPU
1421 std::tuple<bool, int> DirectMaskSubRun::regenerateAtlas(int begin, int end,
1422                                                         GrMeshDrawTarget* target) const {
1423     return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
1424 }
1425
1426 template<typename Quad, typename VertexData>
1427 void transformed_direct_2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1428                            GrColor color,
1429                            const SkMatrix& matrix) {
1430     for (auto[quad, glyph, leftTop] : quadData) {
1431         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1432         SkScalar dl = leftTop[0],
1433                  dt = leftTop[1],
1434                  dr = dl + (ar - al),
1435                  db = dt + (ab - at);
1436         SkPoint lt = matrix.mapXY(dl, dt),
1437                 lb = matrix.mapXY(dl, db),
1438                 rt = matrix.mapXY(dr, dt),
1439                 rb = matrix.mapXY(dr, db);
1440         quad[0] = {lt, color, {al, at}};  // L,T
1441         quad[1] = {lb, color, {al, ab}};  // L,B
1442         quad[2] = {rt, color, {ar, at}};  // R,T
1443         quad[3] = {rb, color, {ar, ab}};  // R,B
1444     }
1445 }
1446
1447 template<typename Quad, typename VertexData>
1448 void transformed_direct_3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1449                            GrColor color,
1450                            const SkMatrix& matrix) {
1451     auto mapXYZ = [&](SkScalar x, SkScalar y) {
1452         SkPoint pt{x, y};
1453         SkPoint3 result;
1454         matrix.mapHomogeneousPoints(&result, &pt, 1);
1455         return result;
1456     };
1457     for (auto[quad, glyph, leftTop] : quadData) {
1458         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1459         SkScalar dl = leftTop[0],
1460                  dt = leftTop[1],
1461                  dr = dl + (ar - al),
1462                  db = dt + (ab - at);
1463         SkPoint3 lt = mapXYZ(dl, dt),
1464                  lb = mapXYZ(dl, db),
1465                  rt = mapXYZ(dr, dt),
1466                  rb = mapXYZ(dr, db);
1467         quad[0] = {lt, color, {al, at}};  // L,T
1468         quad[1] = {lb, color, {al, ab}};  // L,B
1469         quad[2] = {rt, color, {ar, at}};  // R,T
1470         quad[3] = {rb, color, {ar, ab}};  // R,B
1471     }
1472 }
1473
1474 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1475                                           GrColor color,
1476                                           const SkMatrix& drawMatrix, SkPoint drawOrigin,
1477                                           SkIRect clip) const {
1478     auto quadData = [&](auto dst) {
1479         return SkMakeZip(dst,
1480                          fGlyphs.glyphs().subspan(offset, count),
1481                          fLeftTopDevicePos.subspan(offset, count));
1482     };
1483
1484     const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1485     auto [noTransformNeeded, originOffset] =
1486             can_use_direct(fInitialPositionMatrix, positionMatrix);
1487
1488     if (noTransformNeeded) {
1489         if (clip.isEmpty()) {
1490             if (fMaskFormat != MaskFormat::kARGB) {
1491                 using Quad = Mask2DVertex[4];
1492                 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
1493                 direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1494             } else {
1495                 using Quad = ARGB2DVertex[4];
1496                 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
1497                 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1498             }
1499         } else {
1500             if (fMaskFormat != MaskFormat::kARGB) {
1501                 using Quad = Mask2DVertex[4];
1502                 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
1503                 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1504             } else {
1505                 using Quad = ARGB2DVertex[4];
1506                 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
1507                 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1508             }
1509         }
1510     } else if (SkMatrix inverse; fInitialPositionMatrix.invert(&inverse)) {
1511         SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
1512         if (!viewDifference.hasPerspective()) {
1513             if (fMaskFormat != MaskFormat::kARGB) {
1514                 using Quad = Mask2DVertex[4];
1515                 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
1516                 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
1517             } else {
1518                 using Quad = ARGB2DVertex[4];
1519                 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
1520                 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
1521             }
1522         } else {
1523             if (fMaskFormat != MaskFormat::kARGB) {
1524                 using Quad = Mask3DVertex[4];
1525                 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
1526                 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
1527             } else {
1528                 using Quad = ARGB3DVertex[4];
1529                 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
1530                 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
1531             }
1532         }
1533     }
1534 }
1535 #endif
1536
1537 // true if only need to translate by integer amount, device rect.
1538 std::tuple<bool, SkRect> DirectMaskSubRun::deviceRectAndCheckTransform(
1539             const SkMatrix& positionMatrix) const {
1540     const SkMatrix& initialMatrix = fInitialPositionMatrix;
1541     const SkPoint offset = positionMatrix.mapOrigin() - initialMatrix.mapOrigin();
1542
1543     const bool compatibleMatrix = positionMatrix[0] == initialMatrix[0] &&
1544                                   positionMatrix[1] == initialMatrix[1] &&
1545                                   positionMatrix[3] == initialMatrix[3] &&
1546                                   positionMatrix[4] == initialMatrix[4] &&
1547                                   !positionMatrix.hasPerspective() &&
1548                                   !initialMatrix.hasPerspective();
1549
1550     if (compatibleMatrix && SkScalarIsInt(offset.x()) && SkScalarIsInt(offset.y())) {
1551         // Handle the integer offset case.
1552         // The offset should be integer, but make sure.
1553         SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
1554
1555         SkIRect outBounds = fGlyphDeviceBounds.iRect();
1556         return {true, SkRect::Make(outBounds.makeOffset(iOffset))};
1557     } else if (SkMatrix inverse; fInitialPositionMatrix.invert(&inverse)) {
1558         SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
1559         return {false, viewDifference.mapRect(fGlyphDeviceBounds.rect())};
1560     }
1561
1562     // initialPositionMatrix is singular. Do nothing.
1563     return {false, SkRect::MakeEmpty()};
1564 }
1565
1566 // -- TransformedMaskSubRun ------------------------------------------------------------------------
1567 class TransformedMaskSubRun final : public SubRun, public AtlasSubRun {
1568 public:
1569     TransformedMaskSubRun(const SkMatrix& initialPositionMatrix,
1570                           TransformedMaskVertexFiller&& vertexFiller,
1571                           GlyphVector&& glyphs);
1572
1573     static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1574                             const SkMatrix& initialPositionMatrix,
1575                             sk_sp<SkStrike>&& strike,
1576                             SkScalar strikeToSourceScale,
1577                             MaskFormat maskType,
1578                             SubRunAllocator* alloc);
1579
1580     static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1581                                       SkReadBuffer& buffer,
1582                                       SubRunAllocator* alloc,
1583                                       const SkStrikeClient* client);
1584 #if SK_SUPPORT_GPU
1585     void draw(SkCanvas*,
1586               const GrClip*,
1587               const SkMatrixProvider& viewMatrix,
1588               SkPoint drawOrigin,
1589               const SkPaint& paint,
1590               sk_sp<SkRefCnt> subRunStorage,
1591               skgpu::v1::SurfaceDrawContext*) const override;
1592
1593     std::tuple<const GrClip*, GrOp::Owner>
1594     makeAtlasTextOp(const GrClip*,
1595                     const SkMatrixProvider& viewMatrix,
1596                     SkPoint drawOrigin,
1597                     const SkPaint&,
1598                     sk_sp<SkRefCnt>&& subRunStorage,
1599                     skgpu::v1::SurfaceDrawContext*) const override;
1600 #endif
1601
1602     int unflattenSize() const override;
1603
1604     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1605
1606     const AtlasSubRun* testingOnly_atlasSubRun() const override;
1607
1608     void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override;
1609
1610 #if SK_SUPPORT_GPU
1611     std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1612
1613     void fillVertexData(
1614             void* vertexDst, int offset, int count,
1615             GrColor color,
1616             const SkMatrix& drawMatrix, SkPoint drawOrigin,
1617             SkIRect clip) const override;
1618
1619     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1620 #endif
1621
1622     int glyphCount() const override;
1623
1624 protected:
1625     SubRunType subRunType() const override { return kTransformMask; }
1626     void doFlatten(SkWriteBuffer& buffer) const override;
1627
1628 private:
1629     // The rectangle that surrounds all the glyph bounding boxes in device space.
1630     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1631
1632     const SkMatrix& fInitialPositionMatrix;
1633
1634     const TransformedMaskVertexFiller fVertexFiller;
1635
1636     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1637     // be single threaded.
1638     mutable GlyphVector fGlyphs;
1639 };
1640
1641 TransformedMaskSubRun::TransformedMaskSubRun(const SkMatrix& initialPositionMatrix,
1642                                              TransformedMaskVertexFiller&& vertexFiller,
1643                                              GlyphVector&& glyphs)
1644         : fInitialPositionMatrix{initialPositionMatrix}
1645         , fVertexFiller{std::move(vertexFiller)}
1646         , fGlyphs{std::move(glyphs)} { }
1647
1648 SubRunOwner TransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1649                                         const SkMatrix& initialPositionMatrix,
1650                                         sk_sp<SkStrike>&& strike,
1651                                         SkScalar strikeToSourceScale,
1652                                         MaskFormat maskType,
1653                                         SubRunAllocator* alloc) {
1654     auto vertexFiller = TransformedMaskVertexFiller::Make(
1655             maskType, 0, strikeToSourceScale, accepted, alloc);
1656
1657     auto glyphVector = GlyphVector::Make(std::move(strike), accepted.get<0>(), alloc);
1658
1659     return alloc->makeUnique<TransformedMaskSubRun>(
1660             initialPositionMatrix, std::move(vertexFiller), std::move(glyphVector));
1661 }
1662
1663 SubRunOwner TransformedMaskSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1664                                                   SkReadBuffer& buffer,
1665                                                   SubRunAllocator* alloc,
1666                                                   const SkStrikeClient* client) {
1667     auto vertexFiller = TransformedMaskVertexFiller::MakeFromBuffer(buffer, alloc);
1668     if (!buffer.validate(vertexFiller.has_value())) { return {}; }
1669
1670     auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1671     if (!buffer.validate(glyphVector.has_value())) { return {}; }
1672     if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { return {}; }
1673     return alloc->makeUnique<TransformedMaskSubRun>(
1674             initialPositionMatrix, std::move(*vertexFiller), std::move(*glyphVector));
1675 }
1676
1677 int TransformedMaskSubRun::unflattenSize() const {
1678     return sizeof(TransformedMaskSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
1679 }
1680
1681 void TransformedMaskSubRun::doFlatten(SkWriteBuffer& buffer) const {
1682     fVertexFiller.flatten(buffer);
1683     fGlyphs.flatten(buffer);
1684 }
1685
1686 #if SK_SUPPORT_GPU
1687 void TransformedMaskSubRun::draw(SkCanvas*,
1688                                  const GrClip* clip,
1689                                  const SkMatrixProvider& viewMatrix,
1690                                  SkPoint drawOrigin,
1691                                  const SkPaint& paint,
1692                                  sk_sp<SkRefCnt> subRunStorage,
1693                                  skgpu::v1::SurfaceDrawContext* sdc) const {
1694     auto[drawingClip, op] = this->makeAtlasTextOp(
1695             clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1696     if (op != nullptr) {
1697         sdc->addDrawOp(drawingClip, std::move(op));
1698     }
1699 }
1700
1701 std::tuple<const GrClip*, GrOp::Owner>
1702 TransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
1703                                        const SkMatrixProvider& viewMatrix,
1704                                        SkPoint drawOrigin,
1705                                        const SkPaint& paint,
1706                                        sk_sp<SkRefCnt>&& subRunStorage,
1707                                        skgpu::v1::SurfaceDrawContext* sdc) const {
1708     SkASSERT(this->glyphCount() != 0);
1709
1710     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1711
1712     GrPaint grPaint;
1713     SkPMColor4f drawingColor = calculate_colors(
1714             sdc, paint, viewMatrix, fVertexFiller.grMaskType(), &grPaint);
1715
1716     auto geometry = AtlasTextOp::Geometry::Make(*this,
1717                                                 drawMatrix,
1718                                                 drawOrigin,
1719                                                 SkIRect::MakeEmpty(),
1720                                                 std::move(subRunStorage),
1721                                                 drawingColor,
1722                                                 sdc->arenaAlloc());
1723
1724     GrRecordingContext* const rContext = sdc->recordingContext();
1725     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1726                                              fVertexFiller.opMaskType(),
1727                                              true,
1728                                              this->glyphCount(),
1729                                              this->deviceRect(drawMatrix, drawOrigin),
1730                                              geometry,
1731                                              std::move(grPaint));
1732     return {clip, std::move(op)};
1733 }
1734 #endif
1735
1736 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may be
1737 // better.
1738 bool TransformedMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1739     if (fInitialPositionMatrix.getMaxScale() < 1) {
1740         return false;
1741     }
1742     return true;
1743 }
1744
1745 void TransformedMaskSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
1746     fGlyphs.packedGlyphIDToGlyph(cache);
1747 }
1748
1749 #if SK_SUPPORT_GPU
1750 std::tuple<bool, int> TransformedMaskSubRun::regenerateAtlas(int begin, int end,
1751                                                              GrMeshDrawTarget* target) const {
1752     return fGlyphs.regenerateAtlas(begin, end, fVertexFiller.grMaskType(), 1, target);
1753 }
1754
1755 void TransformedMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1756                                            GrColor color,
1757                                            const SkMatrix& drawMatrix, SkPoint drawOrigin,
1758                                            SkIRect clip) const {
1759     const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1760     fVertexFiller.fillVertexData(offset, count,
1761                                  fGlyphs.glyphs(),
1762                                  color,
1763                                  positionMatrix,
1764                                  clip,
1765                                  vertexDst);
1766 }
1767
1768 size_t TransformedMaskSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1769     return fVertexFiller.vertexStride(drawMatrix);
1770 }
1771 #endif
1772
1773 int TransformedMaskSubRun::glyphCount() const {
1774     return SkCount(fGlyphs.glyphs());
1775 }
1776
1777 SkRect TransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1778     return fVertexFiller.deviceRect(drawMatrix, drawOrigin);
1779 }
1780
1781 const AtlasSubRun* TransformedMaskSubRun::testingOnly_atlasSubRun() const {
1782     return this;
1783 }
1784
1785 // -- SDFTSubRun -----------------------------------------------------------------------------------
1786 class SDFTSubRun final : public SubRun, public AtlasSubRun {
1787 public:
1788     SDFTSubRun(bool useLCDText,
1789                bool antiAliased,
1790                const SDFTMatrixRange& matrixRange,
1791                TransformedMaskVertexFiller&& vertexFiller,
1792                GlyphVector&& glyphs);
1793
1794     static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1795                             const SkFont& runFont,
1796                             sk_sp<SkStrike>&& strike,
1797                             SkScalar strikeToSourceScale,
1798                             const SDFTMatrixRange& matrixRange,
1799                             SubRunAllocator* alloc);
1800
1801     static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1802                                       SkReadBuffer& buffer,
1803                                       SubRunAllocator* alloc,
1804                                       const SkStrikeClient* client);
1805 #if SK_SUPPORT_GPU
1806     void draw(SkCanvas*,
1807               const GrClip*,
1808               const SkMatrixProvider& viewMatrix,
1809               SkPoint drawOrigin,
1810               const SkPaint&,
1811               sk_sp<SkRefCnt> subRunStorage,
1812               skgpu::v1::SurfaceDrawContext*) const override;
1813
1814     std::tuple<const GrClip*, GrOp::Owner>
1815     makeAtlasTextOp(const GrClip*,
1816                     const SkMatrixProvider& viewMatrix,
1817                     SkPoint drawOrigin,
1818                     const SkPaint&,
1819                     sk_sp<SkRefCnt>&& subRunStorage,
1820                     skgpu::v1::SurfaceDrawContext*) const override;
1821 #endif
1822
1823     int unflattenSize() const override;
1824
1825     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1826
1827     const AtlasSubRun* testingOnly_atlasSubRun() const override;
1828
1829     void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override;
1830
1831 #if SK_SUPPORT_GPU
1832     std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1833
1834     void fillVertexData(
1835             void* vertexDst, int offset, int count,
1836             GrColor color,
1837             const SkMatrix& drawMatrix, SkPoint drawOrigin,
1838             SkIRect clip) const override;
1839
1840     size_t vertexStride(const SkMatrix& drawMatrix) const override;
1841 #endif
1842
1843     int glyphCount() const override;
1844
1845 protected:
1846     SubRunType subRunType() const override { return kSDFT; }
1847     void doFlatten(SkWriteBuffer& buffer) const override;
1848
1849 private:
1850     // The rectangle that surrounds all the glyph bounding boxes in device space.
1851     SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1852
1853     const bool fUseLCDText;
1854     const bool fAntiAliased;
1855     const sktext::gpu::SDFTMatrixRange fMatrixRange;
1856
1857     const TransformedMaskVertexFiller fVertexFiller;
1858
1859     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1860     // be single threaded.
1861     mutable GlyphVector fGlyphs;
1862 };
1863
1864 SDFTSubRun::SDFTSubRun(bool useLCDText,
1865                        bool antiAliased,
1866                        const SDFTMatrixRange& matrixRange,
1867                        TransformedMaskVertexFiller&& vertexFiller,
1868                        GlyphVector&& glyphs)
1869         : fUseLCDText{useLCDText}
1870         , fAntiAliased{antiAliased}
1871         , fMatrixRange{matrixRange}
1872         , fVertexFiller{std::move(vertexFiller)}
1873         , fGlyphs{std::move(glyphs)} { }
1874
1875 bool has_some_antialiasing(const SkFont& font ) {
1876     SkFont::Edging edging = font.getEdging();
1877     return edging == SkFont::Edging::kAntiAlias
1878            || edging == SkFont::Edging::kSubpixelAntiAlias;
1879 }
1880
1881 SubRunOwner SDFTSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1882                                const SkFont& runFont,
1883                                sk_sp<SkStrike>&& strike,
1884                                SkScalar strikeToSourceScale,
1885                                const SDFTMatrixRange& matrixRange,
1886                                SubRunAllocator* alloc) {
1887     auto vertexFiller = TransformedMaskVertexFiller::Make(
1888             MaskFormat::kA8,
1889             SK_DistanceFieldInset,
1890             strikeToSourceScale,
1891             accepted,
1892             alloc);
1893
1894     auto glyphVector = GlyphVector::Make(std::move(strike), accepted.get<0>(), alloc);
1895
1896     return alloc->makeUnique<SDFTSubRun>(
1897             runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1898             has_some_antialiasing(runFont),
1899             matrixRange,
1900             std::move(vertexFiller),
1901             std::move(glyphVector));
1902 }
1903
1904 SubRunOwner SDFTSubRun::MakeFromBuffer(const SkMatrix&,
1905                                        SkReadBuffer& buffer,
1906                                        SubRunAllocator* alloc,
1907                                        const SkStrikeClient* client) {
1908     int useLCD = buffer.readInt();
1909     int isAntiAliased = buffer.readInt();
1910     SDFTMatrixRange matrixRange = SDFTMatrixRange::MakeFromBuffer(buffer);
1911     auto vertexFiller = TransformedMaskVertexFiller::MakeFromBuffer(buffer, alloc);
1912     if (!buffer.validate(vertexFiller.has_value())) { return {}; }
1913     auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1914     if (!buffer.validate(glyphVector.has_value())) { return {}; }
1915     if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { return {}; }
1916     return alloc->makeUnique<SDFTSubRun>(useLCD,
1917                                          isAntiAliased,
1918                                          matrixRange,
1919                                          std::move(*vertexFiller),
1920                                          std::move(*glyphVector));
1921 }
1922
1923 int SDFTSubRun::unflattenSize() const {
1924     return sizeof(SDFTSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
1925 }
1926
1927 void SDFTSubRun::doFlatten(SkWriteBuffer& buffer) const {
1928     buffer.writeInt(fUseLCDText);
1929     buffer.writeInt(fAntiAliased);
1930     fMatrixRange.flatten(buffer);
1931     fVertexFiller.flatten(buffer);
1932     fGlyphs.flatten(buffer);
1933 }
1934
1935 #if SK_SUPPORT_GPU
1936 void SDFTSubRun::draw(SkCanvas*,
1937                       const GrClip* clip,
1938                       const SkMatrixProvider& viewMatrix,
1939                       SkPoint drawOrigin,
1940                       const SkPaint& paint,
1941                       sk_sp<SkRefCnt> subRunStorage,
1942                       skgpu::v1::SurfaceDrawContext* sdc) const {
1943     auto[drawingClip, op] = this->makeAtlasTextOp(
1944             clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1945     if (op != nullptr) {
1946         sdc->addDrawOp(drawingClip, std::move(op));
1947     }
1948 }
1949
1950 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1951         const skgpu::v1::SurfaceDrawContext& sdc,
1952         const SkMatrix& drawMatrix,
1953         bool useLCDText,
1954         bool isAntiAliased) {
1955     const GrColorInfo& colorInfo = sdc.colorInfo();
1956     const SkSurfaceProps& props = sdc.surfaceProps();
1957     bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1958     bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1959     using MT = AtlasTextOp::MaskType;
1960     MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1961                                  : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1962                                                   : MT::kLCDDistanceField)
1963                                          : MT::kGrayscaleDistanceField;
1964
1965     bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1966     uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1967     DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1968     DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1969     DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1970
1971     if (isLCD) {
1972         DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1973         DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1974     }
1975     return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1976 }
1977
1978 std::tuple<const GrClip*, GrOp::Owner >
1979 SDFTSubRun::makeAtlasTextOp(const GrClip* clip,
1980                             const SkMatrixProvider& viewMatrix,
1981                             SkPoint drawOrigin,
1982                             const SkPaint& paint,
1983                             sk_sp<SkRefCnt>&& subRunStorage,
1984                             skgpu::v1::SurfaceDrawContext* sdc) const {
1985     SkASSERT(this->glyphCount() != 0);
1986
1987     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1988
1989     GrPaint grPaint;
1990     SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, MaskFormat::kA8,
1991                                                 &grPaint);
1992
1993     auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1994         calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
1995
1996     auto geometry = AtlasTextOp::Geometry::Make(*this,
1997                                                 drawMatrix,
1998                                                 drawOrigin,
1999                                                 SkIRect::MakeEmpty(),
2000                                                 std::move(subRunStorage),
2001                                                 drawingColor,
2002                                                 sdc->arenaAlloc());
2003
2004     GrRecordingContext* const rContext = sdc->recordingContext();
2005     GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2006                                              maskType,
2007                                              true,
2008                                              this->glyphCount(),
2009                                              this->deviceRect(drawMatrix, drawOrigin),
2010                                              SkPaintPriv::ComputeLuminanceColor(paint),
2011                                              useGammaCorrectDistanceTable,
2012                                              DFGPFlags,
2013                                              geometry,
2014                                              std::move(grPaint));
2015
2016     return {clip, std::move(op)};
2017 }
2018 #endif
2019
2020 bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
2021     return fMatrixRange.matrixInRange(positionMatrix);
2022 }
2023
2024 void SDFTSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
2025     fGlyphs.packedGlyphIDToGlyph(cache);
2026 }
2027
2028 #if SK_SUPPORT_GPU
2029 std::tuple<bool, int>
2030 SDFTSubRun::regenerateAtlas(int begin, int end, GrMeshDrawTarget *target) const {
2031     return fGlyphs.regenerateAtlas(begin, end, MaskFormat::kA8, SK_DistanceFieldInset,
2032                                    target);
2033 }
2034
2035 size_t SDFTSubRun::vertexStride(const SkMatrix& drawMatrix) const {
2036     return sizeof(Mask2DVertex);
2037 }
2038
2039 void SDFTSubRun::fillVertexData(
2040         void *vertexDst, int offset, int count,
2041         GrColor color,
2042         const SkMatrix& drawMatrix, SkPoint drawOrigin,
2043         SkIRect clip) const {
2044     const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
2045
2046     fVertexFiller.fillVertexData(offset, count,
2047                                  fGlyphs.glyphs(),
2048                                  color,
2049                                  positionMatrix,
2050                                  clip,
2051                                  vertexDst);
2052 }
2053 #endif
2054
2055 int SDFTSubRun::glyphCount() const {
2056     return fVertexFiller.count();
2057 }
2058
2059 SkRect SDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2060     return fVertexFiller.deviceRect(drawMatrix, drawOrigin);
2061 }
2062
2063 const AtlasSubRun* SDFTSubRun::testingOnly_atlasSubRun() const {
2064     return this;
2065 }
2066
2067 template<typename AddSingleMaskFormat>
2068 void add_multi_mask_format(
2069         AddSingleMaskFormat addSingleMaskFormat,
2070         const SkZip<SkGlyphVariant, SkPoint>& accepted,
2071         sk_sp<SkStrike>&& strike) {
2072     if (accepted.empty()) { return; }
2073
2074     auto glyphSpan = accepted.get<0>();
2075     const SkGlyph* glyph = glyphSpan[0];
2076     MaskFormat format = Glyph::FormatFromSkGlyph(glyph->maskFormat());
2077     size_t startIndex = 0;
2078     for (size_t i = 1; i < accepted.size(); i++) {
2079         glyph = glyphSpan[i];
2080         MaskFormat nextFormat = Glyph::FormatFromSkGlyph(glyph->maskFormat());
2081         if (format != nextFormat) {
2082             auto glyphsWithSameFormat = accepted.subspan(startIndex, i - startIndex);
2083             // Take a ref on the strike. This should rarely happen.
2084             addSingleMaskFormat(glyphsWithSameFormat, format, sk_sp<SkStrike>(strike));
2085             format = nextFormat;
2086             startIndex = i;
2087         }
2088     }
2089     auto glyphsWithSameFormat = accepted.last(accepted.size() - startIndex);
2090     addSingleMaskFormat(glyphsWithSameFormat, format, std::move(strike));
2091 }
2092
2093 }  // namespace
2094
2095 // -- TextBlob::Key ------------------------------------------------------------------------------
2096
2097 static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
2098     SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint);
2099     if (lcd) {
2100         // This is the correct computation for canonicalColor, but there are tons of cases where LCD
2101         // can be modified. For now we just regenerate if any run in a textblob has LCD.
2102         // TODO figure out where all of these modifications are and see if we can incorporate that
2103         //      logic at a higher level *OR* use sRGB
2104         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
2105
2106         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
2107         // see the note above.  We pick a placeholder value for LCD text to ensure we always match
2108         // the same key
2109         return SK_ColorTRANSPARENT;
2110     } else {
2111         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
2112         // gamma corrected masks anyways, nor color
2113         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
2114                                        SkColorGetG(canonicalColor),
2115                                        SkColorGetB(canonicalColor));
2116         // reduce to our finite number of bits
2117         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
2118     }
2119     return canonicalColor;
2120 }
2121
2122 auto TextBlob::Key::Make(const SkGlyphRunList& glyphRunList,
2123                          const SkPaint& paint,
2124                          const SkMatrix& drawMatrix,
2125                          const SkStrikeDeviceInfo& strikeDevice) -> std::tuple<bool, Key> {
2126     SkASSERT(strikeDevice.fSDFTControl != nullptr);
2127     SkMaskFilterBase::BlurRec blurRec;
2128     // It might be worth caching these things, but its not clear at this time
2129     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
2130     const SkMaskFilter* maskFilter = paint.getMaskFilter();
2131     bool canCache = glyphRunList.canCache() &&
2132                     !(paint.getPathEffect() ||
2133                         (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
2134
2135     TextBlob::Key key;
2136     if (canCache) {
2137         bool hasLCD = glyphRunList.anyRunsLCD();
2138
2139         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
2140         SkPixelGeometry pixelGeometry = hasLCD ? strikeDevice.fSurfaceProps.pixelGeometry()
2141                                                : kUnknown_SkPixelGeometry;
2142
2143         SkColor canonicalColor = compute_canonical_color(paint, hasLCD);
2144
2145         key.fPixelGeometry = pixelGeometry;
2146         key.fUniqueID = glyphRunList.uniqueID();
2147         key.fStyle = paint.getStyle();
2148         if (key.fStyle != SkPaint::kFill_Style) {
2149             key.fFrameWidth = paint.getStrokeWidth();
2150             key.fMiterLimit = paint.getStrokeMiter();
2151             key.fJoin = paint.getStrokeJoin();
2152         }
2153         key.fHasBlur = maskFilter != nullptr;
2154         if (key.fHasBlur) {
2155             key.fBlurRec = blurRec;
2156         }
2157         key.fCanonicalColor = canonicalColor;
2158         key.fScalerContextFlags = SkTo<uint32_t>(strikeDevice.fScalerContextFlags);
2159
2160         // Do any runs use direct drawing types?.
2161         key.fHasSomeDirectSubRuns = false;
2162         for (auto& run : glyphRunList) {
2163             SkScalar approximateDeviceTextSize =
2164                     SkFontPriv::ApproximateTransformedTextSize(run.font(), drawMatrix);
2165             key.fHasSomeDirectSubRuns |=
2166                     strikeDevice.fSDFTControl->isDirect(approximateDeviceTextSize, paint);
2167         }
2168
2169         if (key.fHasSomeDirectSubRuns) {
2170             // Store the fractional offset of the position. We know that the matrix can't be
2171             // perspective at this point.
2172             SkPoint mappedOrigin = drawMatrix.mapOrigin();
2173             key.fPositionMatrix = drawMatrix;
2174             key.fPositionMatrix.setTranslateX(
2175                     mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
2176             key.fPositionMatrix.setTranslateY(
2177                     mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
2178         } else {
2179             // For path and SDFT, the matrix doesn't matter.
2180             key.fPositionMatrix = SkMatrix::I();
2181         }
2182     }
2183
2184     return {canCache, key};
2185 }
2186
2187 bool TextBlob::Key::operator==(const TextBlob::Key& that) const {
2188     if (fUniqueID != that.fUniqueID) { return false; }
2189     if (fCanonicalColor != that.fCanonicalColor) { return false; }
2190     if (fStyle != that.fStyle) { return false; }
2191     if (fStyle != SkPaint::kFill_Style) {
2192         if (fFrameWidth != that.fFrameWidth ||
2193             fMiterLimit != that.fMiterLimit ||
2194             fJoin != that.fJoin) {
2195             return false;
2196         }
2197     }
2198     if (fPixelGeometry != that.fPixelGeometry) { return false; }
2199     if (fHasBlur != that.fHasBlur) { return false; }
2200     if (fHasBlur) {
2201         if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
2202             return false;
2203         }
2204     }
2205     if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
2206
2207     if (fPositionMatrix.hasPerspective()) {
2208         if (fPositionMatrix[SkMatrix::kMPersp0] != that.fPositionMatrix[SkMatrix::kMPersp0] ||
2209             fPositionMatrix[SkMatrix::kMPersp1] != that.fPositionMatrix[SkMatrix::kMPersp1] ||
2210             fPositionMatrix[SkMatrix::kMPersp2] != that.fPositionMatrix[SkMatrix::kMPersp2]) {
2211                 return false;
2212         }
2213     }
2214
2215     if (fHasSomeDirectSubRuns != that.fHasSomeDirectSubRuns) {
2216         return false;
2217     }
2218
2219     if (fHasSomeDirectSubRuns) {
2220         auto [compatible, _] = can_use_direct(fPositionMatrix, that.fPositionMatrix);
2221         return compatible;
2222     }
2223
2224     return true;
2225 }
2226
2227 // -- TextBlob -----------------------------------------------------------------------------------
2228 void TextBlob::operator delete(void* p) { ::operator delete(p); }
2229 void* TextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
2230 void* TextBlob::operator new(size_t, void* p) { return p; }
2231
2232 TextBlob::~TextBlob() = default;
2233
2234 sk_sp<TextBlob> TextBlob::Make(const SkGlyphRunList& glyphRunList,
2235                                const SkPaint& paint,
2236                                const SkMatrix& positionMatrix,
2237                                SkStrikeDeviceInfo strikeDeviceInfo,
2238                                SkStrikeForGPUCacheInterface* strikeCache) {
2239     // The difference in alignment from the per-glyph data to the SubRun;
2240     constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(DevicePosition);
2241     constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
2242     size_t totalGlyphCount = glyphRunList.totalGlyphCount();
2243
2244     // The neededForSubRun is optimized for DirectMaskSubRun which is by far the most common case.
2245     size_t subRunSizeHint =
2246             totalGlyphCount * sizeof(DevicePosition)
2247             + GlyphVector::GlyphVectorSize(totalGlyphCount)
2248             + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding);
2249
2250     auto [initializer, totalMemoryAllocated, alloc] =
2251             SubRunAllocator::AllocateClassMemoryAndArena<TextBlob>(subRunSizeHint);
2252     SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(paint);
2253     sk_sp<TextBlob> blob = sk_sp<TextBlob>(initializer.initialize(
2254             std::move(alloc), totalMemoryAllocated, positionMatrix, initialLuminance));
2255
2256     // Be sure to pass the ref to the matrix that the SubRuns will capture.
2257     blob->fSomeGlyphsExcluded = SkGlyphRunListPainter::CategorizeGlyphRunList(
2258             glyphRunList, blob->fInitialPositionMatrix, paint,
2259             strikeDeviceInfo, strikeCache, &blob->fSubRunList, &blob->fAlloc, "TextBlob");
2260
2261     return blob;
2262 }
2263
2264 void TextBlob::addKey(const Key& key) {
2265     fKey = key;
2266 }
2267
2268 bool TextBlob::hasPerspective() const { return fInitialPositionMatrix.hasPerspective(); }
2269
2270 bool TextBlob::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
2271     // A singular matrix will create a TextBlob with no SubRuns, but unknown glyphs can
2272     // also cause empty runs. If there are no subRuns or some glyphs were excluded or perspective,
2273     // then regenerate when the matrices don't match.
2274     if ((fSubRunList.isEmpty() || fSomeGlyphsExcluded || hasPerspective()) &&
2275         fInitialPositionMatrix != positionMatrix)
2276     {
2277         return false;
2278     }
2279
2280     // If we have LCD text then our canonical color will be set to transparent, in this case we have
2281     // to regenerate the blob on any color change
2282     // We use the grPaint to get any color filter effects
2283     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
2284         fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
2285         return false;
2286     }
2287
2288     for (const SubRun& subRun : fSubRunList) {
2289         if (!subRun.canReuse(paint, positionMatrix)) {
2290             return false;
2291         }
2292     }
2293
2294     return true;
2295 }
2296
2297 const TextBlob::Key& TextBlob::key() const { return fKey; }
2298
2299 #if SK_SUPPORT_GPU
2300 void TextBlob::draw(SkCanvas* canvas,
2301                       const GrClip* clip,
2302                       const SkMatrixProvider& viewMatrix,
2303                       SkPoint drawOrigin,
2304                       const SkPaint& paint,
2305                       skgpu::v1::SurfaceDrawContext* sdc) {
2306     for (const SubRun& subRun : fSubRunList) {
2307         subRun.draw(canvas, clip, viewMatrix, drawOrigin, paint, sk_ref_sp(this), sdc);
2308     }
2309 }
2310 #endif
2311
2312 const AtlasSubRun* TextBlob::testingOnlyFirstSubRun() const {
2313     if (fSubRunList.isEmpty()) {
2314         return nullptr;
2315     }
2316
2317     return fSubRunList.front().testingOnly_atlasSubRun();
2318 }
2319
2320 TextBlob::TextBlob(SubRunAllocator&& alloc,
2321                    int totalMemorySize,
2322                    const SkMatrix& positionMatrix,
2323                    SkColor initialLuminance)
2324         : fAlloc{std::move(alloc)}
2325         , fSize(totalMemorySize)
2326         , fInitialPositionMatrix{positionMatrix}
2327         , fInitialLuminance{initialLuminance} { }
2328
2329 namespace {
2330 // -- SlugImpl -------------------------------------------------------------------------------------
2331 class SlugImpl final : public Slug {
2332 public:
2333     SlugImpl(SubRunAllocator&& alloc,
2334              SkRect sourceBounds,
2335              const SkPaint& paint,
2336              const SkMatrix& positionMatrix,
2337              SkPoint origin);
2338     ~SlugImpl() override = default;
2339
2340     static sk_sp<SlugImpl> Make(const SkMatrixProvider& viewMatrix,
2341                                 const SkGlyphRunList& glyphRunList,
2342                                 const SkPaint& initialPaint,
2343                                 const SkPaint& drawingPaint,
2344                                 SkStrikeDeviceInfo strikeDeviceInfo,
2345                                 SkStrikeForGPUCacheInterface* strikeCache);
2346     static sk_sp<Slug> MakeFromBuffer(SkReadBuffer& buffer,
2347                                       const SkStrikeClient* client);
2348
2349 #if SK_SUPPORT_GPU
2350     void surfaceDraw(SkCanvas*,
2351                      const GrClip* clip,
2352                      const SkMatrixProvider& viewMatrix,
2353                      const SkPaint& paint,
2354                      skgpu::v1::SurfaceDrawContext* sdc) const;
2355 #endif
2356
2357     void doFlatten(SkWriteBuffer& buffer) const override;
2358     SkRect sourceBounds() const override { return fSourceBounds; }
2359     const SkPaint& initialPaint() const override { return fInitialPaint; }
2360
2361     const SkMatrix& initialPositionMatrix() const { return fInitialPositionMatrix; }
2362     SkPoint origin() const { return fOrigin; }
2363
2364     // Change memory management to handle the data after Slug, but in the same allocation
2365     // of memory. Only allow placement new.
2366     void operator delete(void* p) { ::operator delete(p); }
2367     void* operator new(size_t) { SK_ABORT("All slugs are created by placement new."); }
2368     void* operator new(size_t, void* p) { return p; }
2369
2370     std::tuple<int, int> subRunCountAndUnflattenSizeHint() const {
2371         int unflattenSizeHint = 0;
2372         int subRunCount = 0;
2373         for (auto& subrun : fSubRuns) {
2374             subRunCount += 1;
2375             unflattenSizeHint += subrun.unflattenSize();
2376         }
2377         return {subRunCount, unflattenSizeHint};
2378     }
2379
2380 private:
2381     // The allocator must come first because it needs to be destroyed last. Other fields of this
2382     // structure may have pointers into it.
2383     SubRunAllocator fAlloc;
2384     const SkRect fSourceBounds;
2385     const SkPaint fInitialPaint;
2386     const SkMatrix fInitialPositionMatrix;
2387     const SkPoint fOrigin;
2388     SubRunList fSubRuns;
2389 };
2390
2391 SlugImpl::SlugImpl(SubRunAllocator&& alloc,
2392                    SkRect sourceBounds,
2393                    const SkPaint& initialPaint,
2394                    const SkMatrix& positionMatrix,
2395                    SkPoint origin)
2396     : fAlloc {std::move(alloc)}
2397     , fSourceBounds{sourceBounds}
2398     , fInitialPaint{initialPaint}
2399     , fInitialPositionMatrix{positionMatrix}
2400     , fOrigin{origin} {}
2401
2402 #if SK_SUPPORT_GPU
2403 void SlugImpl::surfaceDraw(SkCanvas* canvas, const GrClip* clip, const SkMatrixProvider& viewMatrix,
2404                            const SkPaint& drawingPaint, skgpu::v1::SurfaceDrawContext* sdc) const {
2405     for (const SubRun& subRun : fSubRuns) {
2406         subRun.draw(canvas, clip, viewMatrix, fOrigin, drawingPaint, sk_ref_sp(this), sdc);
2407     }
2408 }
2409 #endif
2410
2411 void SlugImpl::doFlatten(SkWriteBuffer& buffer) const {
2412     buffer.writeRect(fSourceBounds);
2413     SkPaintPriv::Flatten(fInitialPaint, buffer);
2414     buffer.writeMatrix(fInitialPositionMatrix);
2415     buffer.writePoint(fOrigin);
2416     auto [subRunCount, subRunsUnflattenSizeHint] = this->subRunCountAndUnflattenSizeHint();
2417     buffer.writeInt(subRunCount);
2418     buffer.writeInt(subRunsUnflattenSizeHint);
2419     for (auto& subRun : fSubRuns) {
2420         subRun.flatten(buffer);
2421     }
2422 }
2423
2424 sk_sp<Slug> SlugImpl::MakeFromBuffer(SkReadBuffer& buffer, const SkStrikeClient* client) {
2425     SkRect sourceBounds = buffer.readRect();
2426     SkASSERT(!sourceBounds.isEmpty());
2427     if (!buffer.validate(!sourceBounds.isEmpty())) { return nullptr; }
2428
2429     SkPaint paint = buffer.readPaint();
2430     SkMatrix positionMatrix;
2431     buffer.readMatrix(&positionMatrix);
2432     SkPoint origin = buffer.readPoint();
2433     int subRunCount = buffer.readInt();
2434     SkASSERT(subRunCount > 0);
2435     if (!buffer.validate(subRunCount > 0)) { return nullptr; }
2436     int subRunsSizeHint = buffer.readInt();
2437
2438     // Since the hint doesn't affect performance, then if it looks fishy just pick a reasonable
2439     // value.
2440     if (subRunsSizeHint < 0 || (1 << 16) < subRunsSizeHint) {
2441         subRunsSizeHint = 128;
2442     }
2443
2444     auto [initializer, _, alloc] =
2445             SubRunAllocator::AllocateClassMemoryAndArena<SlugImpl>(subRunsSizeHint);
2446
2447     sk_sp<SlugImpl> slug = sk_sp<SlugImpl>(
2448             initializer.initialize(std::move(alloc), sourceBounds, paint, positionMatrix, origin));
2449
2450     for (int i = 0; i < subRunCount; ++i) {
2451         auto subRun = SubRun::MakeFromBuffer(
2452                 slug->initialPositionMatrix(), buffer, &slug->fAlloc, client);
2453         if (!buffer.validate(subRun != nullptr)) { return nullptr; }
2454         if (subRun != nullptr) {
2455             slug->fSubRuns.append(std::move(subRun));
2456         }
2457     }
2458
2459     // Something went wrong while reading.
2460     SkASSERT(buffer.isValid());
2461     if (!buffer.isValid()) { return nullptr;}
2462
2463     return std::move(slug);
2464 }
2465
2466 sk_sp<SlugImpl> SlugImpl::Make(const SkMatrixProvider& viewMatrix,
2467                                const SkGlyphRunList& glyphRunList,
2468                                const SkPaint& initialPaint,
2469                                const SkPaint& drawingPaint,
2470                                SkStrikeDeviceInfo strikeDeviceInfo,
2471                                SkStrikeForGPUCacheInterface* strikeCache) {
2472     // The difference in alignment from the per-glyph data to the SubRun;
2473     constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(DevicePosition);
2474     constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
2475     size_t totalGlyphCount = glyphRunList.totalGlyphCount();
2476     // The bytesNeededForSubRun is optimized for DirectMaskSubRun which is by far the most
2477     // common case.
2478     size_t subRunSizeHint =
2479             totalGlyphCount * sizeof(DevicePosition)
2480             + GlyphVector::GlyphVectorSize(totalGlyphCount)
2481             + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding);
2482
2483     auto [initializer, _, alloc] =
2484             SubRunAllocator::AllocateClassMemoryAndArena<SlugImpl>(subRunSizeHint);
2485
2486     const SkMatrix positionMatrix =
2487             position_matrix(viewMatrix.localToDevice(), glyphRunList.origin());
2488
2489     sk_sp<SlugImpl> slug = sk_sp<SlugImpl>(initializer.initialize(
2490             std::move(alloc), glyphRunList.sourceBounds(), initialPaint, positionMatrix,
2491             glyphRunList.origin()));
2492
2493     // Be sure to pass the ref to the initialPositionMatrix that the SubRuns will capture.
2494     SkGlyphRunListPainter::CategorizeGlyphRunList(
2495             glyphRunList, slug->fInitialPositionMatrix, drawingPaint,
2496             strikeDeviceInfo, strikeCache, &slug->fSubRuns, &slug->fAlloc, "Make Slug");
2497
2498     // There is nothing to draw here. This is particularly a problem with RSX form blobs where a
2499     // single space becomes a run with no glyphs.
2500     if (slug->fSubRuns.isEmpty()) { return nullptr; }
2501
2502     return slug;
2503 }
2504 }  // namespace
2505
2506 #if SK_SUPPORT_GPU
2507 namespace skgpu::v1 {
2508 sk_sp<Slug>
2509 Device::convertGlyphRunListToSlug(const SkGlyphRunList& glyphRunList,
2510                                   const SkPaint& initialPaint,
2511                                   const SkPaint& drawingPaint) {
2512     return SlugImpl::Make(this->asMatrixProvider(),
2513                           glyphRunList,
2514                           initialPaint,
2515                           drawingPaint,
2516                           this->strikeDeviceInfo(),
2517                           SkStrikeCache::GlobalStrikeCache());
2518 }
2519
2520 void Device::drawSlug(SkCanvas* canvas, const Slug* slug, const SkPaint& drawingPaint) {
2521     const SlugImpl* slugImpl = static_cast<const SlugImpl*>(slug);
2522     auto matrixProvider = this->asMatrixProvider();
2523 #if defined(SK_DEBUG)
2524     if (!fContext->priv().options().fSupportBilerpFromGlyphAtlas) {
2525         // We can draw a slug if the atlas has padding or if the creation matrix and the
2526         // drawing matrix are the same. If they are the same, then the Slug will use the direct
2527         // drawing code and not use bi-lerp.
2528         SkMatrix slugMatrix = slugImpl->initialPositionMatrix();
2529         SkMatrix positionMatrix = matrixProvider.localToDevice();
2530         positionMatrix.preTranslate(slugImpl->origin().x(), slugImpl->origin().y());
2531         SkASSERT(slugMatrix == positionMatrix);
2532     }
2533 #endif
2534     slugImpl->surfaceDraw(
2535             canvas, this->clip(), matrixProvider, drawingPaint, fSurfaceDrawContext.get());
2536 }
2537
2538 sk_sp<Slug> MakeSlug(const SkMatrixProvider& drawMatrix,
2539                      const SkGlyphRunList& glyphRunList,
2540                      const SkPaint& initialPaint,
2541                      const SkPaint& drawingPaint,
2542                      SkStrikeDeviceInfo strikeDeviceInfo,
2543                      SkStrikeForGPUCacheInterface* strikeCache) {
2544     return SlugImpl::Make(
2545             drawMatrix, glyphRunList, initialPaint, drawingPaint, strikeDeviceInfo, strikeCache);
2546 }
2547 }  // namespace skgpu::v1
2548 #endif
2549
2550 namespace sktext::gpu {
2551
2552 // -- SubRun -------------------------------------------------------------------------------------
2553 void SubRun::flatten(SkWriteBuffer& buffer) const {
2554     buffer.writeInt(this->subRunType());
2555     this->doFlatten(buffer);
2556 }
2557
2558 SubRunOwner SubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
2559                                    SkReadBuffer& buffer,
2560                                    SubRunAllocator* alloc,
2561                                    const SkStrikeClient* client) {
2562     using Maker = SubRunOwner (*)(const SkMatrix&,
2563                                   SkReadBuffer&,
2564                                   SubRunAllocator*,
2565                                   const SkStrikeClient*);
2566
2567     static Maker makers[kSubRunTypeCount] = {
2568             nullptr,                                             // 0 index is bad.
2569             DirectMaskSubRun::MakeFromBuffer,
2570             SDFTSubRun::MakeFromBuffer,
2571             TransformedMaskSubRun::MakeFromBuffer,
2572             PathSubRun::MakeFromBuffer,
2573             DrawableSubRun::MakeFromBuffer,
2574     };
2575     int subRunTypeInt = buffer.readInt();
2576     SkASSERT(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount);
2577     if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount)) {
2578         return nullptr;
2579     }
2580     auto maker = makers[subRunTypeInt];
2581     if (!buffer.validate(maker != nullptr)) { return nullptr; }
2582     return maker(initialPositionMatrix, buffer, alloc, client);
2583 }
2584
2585 sk_sp<Slug> SkMakeSlugFromBuffer(SkReadBuffer& buffer, const SkStrikeClient* client) {
2586     return SlugImpl::MakeFromBuffer(buffer, client);
2587 }
2588
2589 }  // namespace sktext::gpu
2590
2591 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
2592 // Use the following in your args.gn to dump telemetry for diagnosing chrome Renderer/GPU
2593 // differences.
2594 // extra_cflags = ["-D", "SK_TRACE_GLYPH_RUN_PROCESS"]
2595 namespace {
2596 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
2597     static const constexpr bool kTrace = true;
2598 #else
2599     static const constexpr bool kTrace = false;
2600 #endif
2601 }
2602
2603 bool SkGlyphRunListPainter::CategorizeGlyphRunList(const SkGlyphRunList& glyphRunList,
2604                                                    const SkMatrix& positionMatrix,
2605                                                    const SkPaint& runPaint,
2606                                                    SkStrikeDeviceInfo strikeDeviceInfo,
2607                                                    SkStrikeForGPUCacheInterface* strikeCache,
2608                                                    sktext::gpu::SubRunList* subRunList,
2609                                                    sktext::gpu::SubRunAllocator* alloc,
2610                                                    const char* tag) {
2611     [[maybe_unused]] SkString msg;
2612     if constexpr (kTrace) {
2613         const uint64_t uniqueID = glyphRunList.uniqueID();
2614         msg.appendf("\nStart glyph run processing");
2615         if (tag != nullptr) {
2616             msg.appendf(" for %s ", tag);
2617             if (uniqueID != SK_InvalidUniqueID) {
2618                 msg.appendf(" uniqueID: %" PRIu64, uniqueID);
2619             }
2620         }
2621         msg.appendf("\n   matrix\n");
2622         msg.appendf("   %7.3g %7.3g %7.3g\n   %7.3g %7.3g %7.3g\n",
2623                     positionMatrix[0], positionMatrix[1], positionMatrix[2],
2624                     positionMatrix[3], positionMatrix[4], positionMatrix[5]);
2625     }
2626
2627     SkASSERT(strikeDeviceInfo.fSDFTControl != nullptr);
2628     if (strikeDeviceInfo.fSDFTControl == nullptr) {
2629         return true;
2630     }
2631
2632     const SkSurfaceProps deviceProps = strikeDeviceInfo.fSurfaceProps;
2633     const SkScalerContextFlags scalerContextFlags = strikeDeviceInfo.fScalerContextFlags;
2634     const sktext::gpu::SDFTControl SDFTControl = *strikeDeviceInfo.fSDFTControl;
2635
2636     auto bufferScope = SkSubRunBuffers::EnsureBuffers(glyphRunList);
2637     auto [accepted, rejected] = bufferScope.buffers();
2638     bool someGlyphExcluded = false;
2639     for (auto& glyphRun : glyphRunList) {
2640         rejected->setSource(glyphRun.source());
2641         const SkFont& runFont = glyphRun.font();
2642
2643         // Only consider using direct or SDFT drawing if not drawing hairlines and not perspective.
2644         if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0)
2645             && !positionMatrix.hasPerspective()) {
2646             SkScalar approximateDeviceTextSize =
2647                     SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix);
2648
2649             if (SDFTControl.isSDFT(approximateDeviceTextSize, runPaint)) {
2650                 // Process SDFT - This should be the .009% case.
2651                 const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
2652                         SkStrikeSpec::MakeSDFT(
2653                                 runFont, runPaint, deviceProps, positionMatrix, SDFTControl);
2654
2655                 if constexpr(kTrace) {
2656                     msg.appendf("  SDFT case:\n%s", strikeSpec.dump().c_str());
2657                 }
2658
2659                 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2660                     SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2661
2662                     accepted->startSource(rejected->source());
2663                     if constexpr (kTrace) {
2664                         msg.appendf("    glyphs:(x,y):\n      %s\n", accepted->dumpInput().c_str());
2665                     }
2666                     strike->prepareForSDFTDrawing(accepted, rejected);
2667                     rejected->flipRejectsToSource();
2668
2669                     if (subRunList && !accepted->empty()) {
2670                         subRunList->append(SDFTSubRun::Make(
2671                                 accepted->accepted(),
2672                                 runFont,
2673                                 strike->getUnderlyingStrike(),
2674                                 strikeToSourceScale,
2675                                 matrixRange, alloc));
2676                     }
2677                 }
2678             }
2679
2680             if (!rejected->source().empty()) {
2681                 // Process masks including ARGB - this should be the 99.99% case.
2682                 // This will handle medium size emoji that are sharing the run with SDFT drawn text.
2683                 // If things are too big they will be passed along to the drawing of last resort
2684                 // below.
2685                 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
2686                         runFont, runPaint, deviceProps, scalerContextFlags, positionMatrix);
2687
2688                 if constexpr (kTrace) {
2689                     msg.appendf("  Mask case:\n%s", strikeSpec.dump().c_str());
2690                 }
2691
2692                 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2693
2694                 accepted->startDevicePositioning(
2695                         rejected->source(), positionMatrix, strike->roundingSpec());
2696                 if constexpr (kTrace) {
2697                     msg.appendf("    glyphs:(x,y):\n      %s\n", accepted->dumpInput().c_str());
2698                 }
2699                 strike->prepareForMaskDrawing(accepted, rejected);
2700                 rejected->flipRejectsToSource();
2701
2702                 if (subRunList && !accepted->empty()) {
2703                     auto addGlyphsWithSameFormat = [&] (
2704                             const SkZip<SkGlyphVariant, SkPoint>& acceptedGlyphsAndLocations,
2705                             MaskFormat format,
2706                             sk_sp<SkStrike>&& runStrike) {
2707                         SubRunOwner subRun = DirectMaskSubRun::Make(
2708                                 acceptedGlyphsAndLocations,
2709                                 positionMatrix,
2710                                 std::move(runStrike),
2711                                 format,
2712                                 alloc);
2713                         if (subRun != nullptr) {
2714                             subRunList->append(std::move(subRun));
2715                         } else {
2716                             someGlyphExcluded |= true;
2717                         }
2718                     };
2719                     add_multi_mask_format(addGlyphsWithSameFormat,
2720                                           accepted->accepted(),
2721                                           strike->getUnderlyingStrike());
2722                 }
2723             }
2724         }
2725
2726         // Glyphs are generated in different scales relative to the source space. Masks are drawn
2727         // in device space, and SDFT and Paths are draw in a fixed constant space. The
2728         // maxDimensionInSourceSpace is used to calculate the factor from strike space to source
2729         // space.
2730         SkScalar maxDimensionInSourceSpace = 0.0;
2731         if (!rejected->source().empty()) {
2732             // Drawable case - handle big things with that have a drawable.
2733             auto [strikeSpec, strikeToSourceScale] =
2734                     SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
2735
2736             if constexpr (kTrace) {
2737                 msg.appendf("  Drawable case:\n%s", strikeSpec.dump().c_str());
2738             }
2739
2740             if (!SkScalarNearlyZero(strikeToSourceScale)) {
2741                 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2742
2743                 accepted->startSource(rejected->source());
2744                 if constexpr (kTrace) {
2745                     msg.appendf("    glyphs:(x,y):\n      %s\n", accepted->dumpInput().c_str());
2746                 }
2747                 strike->prepareForDrawableDrawing(accepted, rejected);
2748                 rejected->flipRejectsToSource();
2749                 auto [minHint, maxHint] = rejected->maxDimensionHint();
2750                 maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
2751
2752                 if (subRunList && !accepted->empty()) {
2753                     subRunList->append(make_drawable_sub_run<DrawableSubRun>(
2754                             accepted->accepted(),
2755                             strike->getUnderlyingStrike(),
2756                             strikeToSourceScale,
2757                             strikeSpec.descriptor(),
2758                             alloc));
2759                 }
2760             }
2761         }
2762         if (!rejected->source().empty()) {
2763             // Path case - handle big things without color and that have a path.
2764             auto [strikeSpec, strikeToSourceScale] =
2765                     SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
2766
2767             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
2768                 msg.appendf("  Path case:\n%s", strikeSpec.dump().c_str());
2769             #endif
2770
2771             if (!SkScalarNearlyZero(strikeToSourceScale)) {
2772                 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2773
2774                 accepted->startSource(rejected->source());
2775                 if constexpr (kTrace) {
2776                     msg.appendf("    glyphs:(x,y):\n      %s\n", accepted->dumpInput().c_str());
2777                 }
2778                 strike->prepareForPathDrawing(accepted, rejected);
2779                 rejected->flipRejectsToSource();
2780                 auto [minHint, maxHint] = rejected->maxDimensionHint();
2781                 maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
2782
2783                 if (subRunList && !accepted->empty()) {
2784                     subRunList->append(PathSubRun::Make(accepted->accepted(),
2785                                                         has_some_antialiasing(runFont),
2786                                                         strikeToSourceScale,
2787                                                         strikeSpec.descriptor(),
2788                                                         alloc));
2789                 }
2790             }
2791         }
2792
2793         if (!rejected->source().empty() && maxDimensionInSourceSpace != 0) {
2794             // Draw of last resort. Scale the bitmap to the screen.
2795             auto [strikeSpec, strikeToSourceScaleTemp] = SkStrikeSpec::MakeSourceFallback(
2796                     runFont, runPaint, deviceProps,
2797                     scalerContextFlags, maxDimensionInSourceSpace);
2798
2799             if constexpr (kTrace) {
2800                 msg.appendf("Transformed case:\n%s", strikeSpec.dump().c_str());
2801             }
2802             // Get around fake binding from structural decomposition. Bad c++.
2803             auto strikeToSourceScale = strikeToSourceScaleTemp;
2804
2805             if (!SkScalarNearlyZero(strikeToSourceScale)) {
2806                 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2807
2808                 accepted->startSource(rejected->source());
2809                 if constexpr (kTrace) {
2810                     msg.appendf("glyphs:(x,y):\n      %s\n", accepted->dumpInput().c_str());
2811                 }
2812                 strike->prepareForMaskDrawing(accepted, rejected);
2813                 rejected->flipRejectsToSource();
2814                 SkASSERT(rejected->source().empty());
2815
2816                 if (subRunList && !accepted->empty()) {
2817                     auto addGlyphsWithSameFormat = [&] (
2818                             const SkZip<SkGlyphVariant, SkPoint>& acceptedGlyphsAndLocations,
2819                             MaskFormat format,
2820                             sk_sp<SkStrike>&& runStrike) {
2821                         SubRunOwner subRun = TransformedMaskSubRun::Make(
2822                                 acceptedGlyphsAndLocations, positionMatrix, std::move(runStrike),
2823                                 strikeToSourceScale, format, alloc);
2824                         if (subRun != nullptr) {
2825                             subRunList->append(std::move(subRun));
2826                         } else {
2827                             someGlyphExcluded |= true;
2828                         }
2829                     };
2830                     add_multi_mask_format(
2831                             addGlyphsWithSameFormat,
2832                             accepted->accepted(),
2833                             strike->getUnderlyingStrike());
2834                 }
2835             }
2836         }
2837     }
2838     if constexpr (kTrace) {
2839         msg.appendf("End glyph run processing");
2840         if (tag != nullptr) {
2841             msg.appendf(" for %s ", tag);
2842         }
2843         SkDebugf("%s\n", msg.c_str());
2844     }
2845     return someGlyphExcluded;
2846 }
2847
2848