xref: /aosp_15_r20/external/skia/src/text/gpu/SubRunContainer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/text/gpu/SubRunContainer.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkDrawable.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkMaskFilter.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPath.h"
17 #include "include/core/SkPathEffect.h"
18 #include "include/core/SkPoint.h"
19 #include "include/core/SkRect.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkStrokeRec.h"
22 #include "include/core/SkSurfaceProps.h"
23 #include "include/core/SkTypes.h"
24 #include "include/effects/SkDashPathEffect.h"
25 #include "include/private/base/SkFloatingPoint.h"
26 #include "include/private/base/SkOnce.h"
27 #include "include/private/base/SkTArray.h"
28 #include "include/private/base/SkTLogic.h"
29 #include "include/private/base/SkTo.h"
30 #include "src/base/SkZip.h"
31 #include "src/core/SkDevice.h"
32 #include "src/core/SkDistanceFieldGen.h"
33 #include "src/core/SkEnumerate.h"
34 #include "src/core/SkFontPriv.h"
35 #include "src/core/SkGlyph.h"
36 #include "src/core/SkMask.h"
37 #include "src/core/SkMaskFilterBase.h"
38 #include "src/core/SkMatrixPriv.h"
39 #include "src/core/SkPathEffectBase.h"
40 #include "src/core/SkReadBuffer.h"
41 #include "src/core/SkScalerContext.h"
42 #include "src/core/SkStrike.h"
43 #include "src/core/SkStrikeCache.h"
44 #include "src/core/SkStrikeSpec.h"
45 #include "src/core/SkWriteBuffer.h"
46 #include "src/gpu/AtlasTypes.h"
47 #include "src/text/GlyphRun.h"
48 #include "src/text/StrikeForGPU.h"
49 #include "src/text/gpu/Glyph.h"
50 #include "src/text/gpu/GlyphVector.h"
51 #include "src/text/gpu/SDFMaskFilter.h"
52 #include "src/text/gpu/SubRunAllocator.h"
53 #include "src/text/gpu/SubRunControl.h"
54 #include "src/text/gpu/VertexFiller.h"
55 
56 #include <algorithm>
57 #include <climits>
58 #include <cstdint>
59 #include <new>
60 #include <optional>
61 #include <vector>
62 
63 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
64 #include "include/core/SkRRect.h"
65 #include "include/private/SkColorData.h"
66 #include "include/private/gpu/ganesh/GrTypesPriv.h"
67 #include "src/core/SkPaintPriv.h"
68 #include "src/gpu/ganesh/GrClip.h"
69 #include "src/gpu/ganesh/GrColorInfo.h"
70 #include "src/gpu/ganesh/GrFragmentProcessor.h"
71 #include "src/gpu/ganesh/GrPaint.h"
72 #include "src/gpu/ganesh/SkGr.h"
73 #include "src/gpu/ganesh/SurfaceDrawContext.h"
74 #include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h"
75 #include "src/gpu/ganesh/ops/AtlasTextOp.h"
76 
77 class GrRecordingContext;
78 
79 using AtlasTextOp = skgpu::ganesh::AtlasTextOp;
80 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
81 
82 using namespace skia_private;
83 using namespace skglyph;
84 
85 // -- GPU Text -------------------------------------------------------------------------------------
86 // Naming conventions
87 //  * drawMatrix - the CTM from the canvas.
88 //  * drawOrigin - the x, y location of the drawTextBlob call.
89 //  * positionMatrix - this is the combination of the drawMatrix and the drawOrigin:
90 //        positionMatrix = drawMatrix * TranslationMatrix(drawOrigin.x, drawOrigin.y);
91 //
92 // Note:
93 //   In order to transform Slugs, you need to set the fSupportBilerpFromGlyphAtlas on
94 //   GrContextOptions.
95 
96 namespace sktext::gpu {
97 // -- SubRunStreamTag ------------------------------------------------------------------------------
98 enum SubRun::SubRunStreamTag : int {
99     kBad = 0,  // Make this 0 to line up with errors from readInt.
100     kDirectMaskStreamTag,
101 #if !defined(SK_DISABLE_SDF_TEXT)
102     kSDFTStreamTag,
103 #endif
104     kTransformMaskStreamTag,
105     kPathStreamTag,
106     kDrawableStreamTag,
107     kSubRunStreamTagCount,
108 };
109 
110 }  // namespace sktext::gpu
111 
112 using MaskFormat = skgpu::MaskFormat;
113 
114 using namespace sktext;
115 using namespace sktext::gpu;
116 
117 namespace {
118 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
calculate_colors(skgpu::ganesh::SurfaceDrawContext * sdc,const SkPaint & paint,const SkMatrix & matrix,MaskFormat maskFormat,GrPaint * grPaint)119 SkPMColor4f calculate_colors(skgpu::ganesh::SurfaceDrawContext* sdc,
120                              const SkPaint& paint,
121                              const SkMatrix& matrix,
122                              MaskFormat maskFormat,
123                              GrPaint* grPaint) {
124     GrRecordingContext* rContext = sdc->recordingContext();
125     const GrColorInfo& colorInfo = sdc->colorInfo();
126     const SkSurfaceProps& props = sdc->surfaceProps();
127     if (maskFormat == MaskFormat::kARGB) {
128         SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, props, grPaint);
129         float a = grPaint->getColor4f().fA;
130         return {a, a, a, a};
131     }
132     SkPaintToGrPaint(rContext, colorInfo, paint, matrix, props, grPaint);
133     return grPaint->getColor4f();
134 }
135 
position_matrix(const SkMatrix & drawMatrix,SkPoint drawOrigin)136 SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) {
137     SkMatrix position_matrix = drawMatrix;
138     return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
139 }
140 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
141 
get_packedIDs(SkZip<const SkPackedGlyphID,const SkPoint> accepted)142 SkSpan<const SkPackedGlyphID> get_packedIDs(SkZip<const SkPackedGlyphID, const SkPoint> accepted) {
143     return accepted.get<0>();
144 }
145 
get_glyphIDs(SkZip<const SkGlyphID,const SkPoint> accepted)146 SkSpan<const SkGlyphID> get_glyphIDs(SkZip<const SkGlyphID, const SkPoint> accepted) {
147     return accepted.get<0>();
148 }
149 
150 template <typename U>
get_positions(SkZip<U,const SkPoint> accepted)151 SkSpan<const SkPoint> get_positions(SkZip<U, const SkPoint> accepted) {
152     return accepted.template get<1>();
153 }
154 
155 // -- PathOpSubmitter ------------------------------------------------------------------------------
156 // PathOpSubmitter holds glyph ids until ready to draw. During drawing, the glyph ids are
157 // converted to SkPaths. PathOpSubmitter can only be serialized when it is holding glyph ids;
158 // it can only be serialized before submitDraws has been called.
159 class PathOpSubmitter {
160 public:
161     PathOpSubmitter() = delete;
162     PathOpSubmitter(const PathOpSubmitter&) = delete;
163     const PathOpSubmitter& operator=(const PathOpSubmitter&) = delete;
PathOpSubmitter(PathOpSubmitter && that)164     PathOpSubmitter(PathOpSubmitter&& that)
165             // Transfer ownership of fIDsOrPaths from that to this.
166             : fIDsOrPaths{std::exchange(
167                       const_cast<SkSpan<IDOrPath>&>(that.fIDsOrPaths), SkSpan<IDOrPath>{})}
168             , fPositions{that.fPositions}
169             , fStrikeToSourceScale{that.fStrikeToSourceScale}
170             , fIsAntiAliased{that.fIsAntiAliased}
171             , fStrikePromise{std::move(that.fStrikePromise)} {}
operator =(PathOpSubmitter && that)172     PathOpSubmitter& operator=(PathOpSubmitter&& that) {
173         this->~PathOpSubmitter();
174         new (this) PathOpSubmitter{std::move(that)};
175         return *this;
176     }
177     PathOpSubmitter(bool isAntiAliased,
178                     SkScalar strikeToSourceScale,
179                     SkSpan<SkPoint> positions,
180                     SkSpan<IDOrPath> idsOrPaths,
181                     SkStrikePromise&& strikePromise);
182 
183     ~PathOpSubmitter();
184 
185     static PathOpSubmitter Make(SkZip<const SkGlyphID, const SkPoint> accepted,
186                                 bool isAntiAliased,
187                                 SkScalar strikeToSourceScale,
188                                 SkStrikePromise&& strikePromise,
189                                 SubRunAllocator* alloc);
190 
191     int unflattenSize() const;
192     void flatten(SkWriteBuffer& buffer) const;
193     static std::optional<PathOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
194                                                          SubRunAllocator* alloc,
195                                                          const SkStrikeClient* client);
196 
197     // submitDraws is not thread safe. It only occurs the single thread drawing portion of the GPU
198     // rendering.
199     void submitDraws(SkCanvas*,
200                      SkPoint drawOrigin,
201                      const SkPaint& paint) const;
202 
203 private:
204     // When PathOpSubmitter is created only the glyphIDs are needed, during the submitDraws call,
205     // the glyphIDs are converted to SkPaths.
206     const SkSpan<IDOrPath> fIDsOrPaths;
207     const SkSpan<const SkPoint> fPositions;
208     const SkScalar fStrikeToSourceScale;
209     const bool fIsAntiAliased;
210 
211     mutable SkStrikePromise fStrikePromise;
212     mutable SkOnce fConvertIDsToPaths;
213     mutable bool fPathsAreCreated{false};
214 };
215 
unflattenSize() const216 int PathOpSubmitter::unflattenSize() const {
217     return fPositions.size_bytes() + fIDsOrPaths.size_bytes();
218 }
219 
flatten(SkWriteBuffer & buffer) const220 void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const {
221     fStrikePromise.flatten(buffer);
222 
223     buffer.writeInt(fIsAntiAliased);
224     buffer.writeScalar(fStrikeToSourceScale);
225     buffer.writePointArray(fPositions.data(), SkCount(fPositions));
226     for (IDOrPath& idOrPath : fIDsOrPaths) {
227         buffer.writeInt(idOrPath.fGlyphID);
228     }
229 }
230 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)231 std::optional<PathOpSubmitter> PathOpSubmitter::MakeFromBuffer(SkReadBuffer& buffer,
232                                                                SubRunAllocator* alloc,
233                                                                const SkStrikeClient* client) {
234     std::optional<SkStrikePromise> strikePromise =
235             SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache());
236     if (!buffer.validate(strikePromise.has_value())) {
237         return std::nullopt;
238     }
239 
240     bool isAntiAlias = buffer.readInt();
241 
242     SkScalar strikeToSourceScale = buffer.readScalar();
243     if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; }
244 
245     SkSpan<SkPoint> positions = MakePointsFromBuffer(buffer, alloc);
246     if (positions.empty()) { return std::nullopt; }
247     const int glyphCount = SkCount(positions);
248 
249     // Remember, we stored an int for glyph id.
250     if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; }
251     auto idsOrPaths = SkSpan(alloc->makeUniqueArray<IDOrPath>(glyphCount).release(), glyphCount);
252     for (auto& idOrPath : idsOrPaths) {
253         idOrPath.fGlyphID = SkTo<SkGlyphID>(buffer.readInt());
254     }
255 
256     if (!buffer.isValid()) { return std::nullopt; }
257 
258     return PathOpSubmitter{isAntiAlias,
259                            strikeToSourceScale,
260                            positions,
261                            idsOrPaths,
262                            std::move(strikePromise.value())};
263 }
264 
PathOpSubmitter(bool isAntiAliased,SkScalar strikeToSourceScale,SkSpan<SkPoint> positions,SkSpan<IDOrPath> idsOrPaths,SkStrikePromise && strikePromise)265 PathOpSubmitter::PathOpSubmitter(
266         bool isAntiAliased,
267         SkScalar strikeToSourceScale,
268         SkSpan<SkPoint> positions,
269         SkSpan<IDOrPath> idsOrPaths,
270         SkStrikePromise&& strikePromise)
271         : fIDsOrPaths{idsOrPaths}
272         , fPositions{positions}
273         , fStrikeToSourceScale{strikeToSourceScale}
274         , fIsAntiAliased{isAntiAliased}
275         , fStrikePromise{std::move(strikePromise)} {
276     SkASSERT(!fPositions.empty());
277 }
278 
~PathOpSubmitter()279 PathOpSubmitter::~PathOpSubmitter() {
280     // If we have converted glyph IDs to paths, then clean up the SkPaths.
281     if (fPathsAreCreated) {
282         for (auto& idOrPath : fIDsOrPaths) {
283             idOrPath.fPath.~SkPath();
284         }
285     }
286 }
287 
Make(SkZip<const SkGlyphID,const SkPoint> accepted,bool isAntiAliased,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)288 PathOpSubmitter PathOpSubmitter::Make(SkZip<const SkGlyphID, const SkPoint> accepted,
289                                       bool isAntiAliased,
290                                       SkScalar strikeToSourceScale,
291                                       SkStrikePromise&& strikePromise,
292                                       SubRunAllocator* alloc) {
293     auto mapToIDOrPath = [](SkGlyphID glyphID) { return IDOrPath{glyphID}; };
294 
295     IDOrPath* const rawIDsOrPaths =
296             alloc->makeUniqueArray<IDOrPath>(get_glyphIDs(accepted), mapToIDOrPath).release();
297 
298     return PathOpSubmitter{isAntiAliased,
299                            strikeToSourceScale,
300                            alloc->makePODSpan(get_positions(accepted)),
301                            SkSpan(rawIDsOrPaths, accepted.size()),
302                            std::move(strikePromise)};
303 }
304 
305 void
submitDraws(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint) const306 PathOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint& paint) const {
307     // Convert the glyph IDs to paths if it hasn't been done yet. This is thread safe.
308     fConvertIDsToPaths([&]() {
309         if (SkStrike* strike = fStrikePromise.strike()) {
310             strike->glyphIDsToPaths(fIDsOrPaths);
311 
312             // Drop ref to strike so that it can be purged from the cache if needed.
313             fStrikePromise.resetStrike();
314             fPathsAreCreated = true;
315         }
316     });
317 
318     SkPaint runPaint{paint};
319     runPaint.setAntiAlias(fIsAntiAliased);
320 
321     SkMaskFilterBase* maskFilter = as_MFB(runPaint.getMaskFilter());
322 
323     // Calculate the matrix that maps the path glyphs from their size in the strike to
324     // the graphics source space.
325     SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
326     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
327 
328     // If there are shaders, non-blur mask filters or styles, the path must be scaled into source
329     // space independently of the CTM. This allows the CTM to be correct for the different effects.
330     SkStrokeRec style(runPaint);
331     bool needsExactCTM = runPaint.getShader()
332                          || runPaint.getPathEffect()
333                          || (!style.isFillStyle() && !style.isHairlineStyle())
334                          || (maskFilter != nullptr && !maskFilter->asABlur(nullptr));
335     if (!needsExactCTM) {
336         SkMaskFilterBase::BlurRec blurRec;
337 
338         // If there is a blur mask filter, then sigma needs to be adjusted to account for the
339         // scaling of fStrikeToSourceScale.
340         if (maskFilter != nullptr && maskFilter->asABlur(&blurRec)) {
341             runPaint.setMaskFilter(
342                     SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma / fStrikeToSourceScale));
343         }
344         for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) {
345             // Transform the glyph to source space.
346             SkMatrix pathMatrix = strikeToSource;
347             pathMatrix.postTranslate(pos.x(), pos.y());
348 
349             SkAutoCanvasRestore acr(canvas, true);
350             canvas->concat(pathMatrix);
351             canvas->drawPath(idOrPath.fPath, runPaint);
352         }
353     } else {
354         // Transform the path to device because the deviceMatrix must be unchanged to
355         // draw effect, filter or shader paths.
356         for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) {
357             // Transform the glyph to source space.
358             SkMatrix pathMatrix = strikeToSource;
359             pathMatrix.postTranslate(pos.x(), pos.y());
360 
361             SkPath deviceOutline;
362             idOrPath.fPath.transform(pathMatrix, &deviceOutline);
363             deviceOutline.setIsVolatile(true);
364             canvas->drawPath(deviceOutline, runPaint);
365         }
366     }
367 }
368 
369 // -- PathSubRun -----------------------------------------------------------------------------------
370 class PathSubRun final : public SubRun {
371 public:
PathSubRun(PathOpSubmitter && pathDrawing)372     PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {}
373 
Make(SkZip<const SkGlyphID,const SkPoint> accepted,bool isAntiAliased,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)374     static SubRunOwner Make(SkZip<const SkGlyphID, const SkPoint> accepted,
375                             bool isAntiAliased,
376                             SkScalar strikeToSourceScale,
377                             SkStrikePromise&& strikePromise,
378                             SubRunAllocator* alloc) {
379         return alloc->makeUnique<PathSubRun>(
380             PathOpSubmitter::Make(
381                     accepted, isAntiAliased, strikeToSourceScale, std::move(strikePromise), alloc));
382     }
383 
draw(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt>,const AtlasDrawDelegate &) const384     void draw(SkCanvas* canvas,
385               SkPoint drawOrigin,
386               const SkPaint& paint,
387               sk_sp<SkRefCnt>,
388               const AtlasDrawDelegate&) const override {
389         fPathDrawing.submitDraws(canvas, drawOrigin, paint);
390     }
391 
392     int unflattenSize() const override;
393 
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const394     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
395         return true;
396     }
testingOnly_atlasSubRun() const397     const AtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
398     static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer,
399                                       SubRunAllocator* alloc,
400                                       const SkStrikeClient* client);
401 
402 protected:
subRunStreamTag() const403     SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kPathStreamTag; }
404     void doFlatten(SkWriteBuffer& buffer) const override;
405 
406 private:
407     PathOpSubmitter fPathDrawing;
408 };
409 
unflattenSize() const410 int PathSubRun::unflattenSize() const {
411     return sizeof(PathSubRun) + fPathDrawing.unflattenSize();
412 }
413 
doFlatten(SkWriteBuffer & buffer) const414 void PathSubRun::doFlatten(SkWriteBuffer& buffer) const {
415     fPathDrawing.flatten(buffer);
416 }
417 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)418 SubRunOwner PathSubRun::MakeFromBuffer(SkReadBuffer& buffer,
419                                        SubRunAllocator* alloc,
420                                        const SkStrikeClient* client) {
421     auto pathOpSubmitter = PathOpSubmitter::MakeFromBuffer(buffer, alloc, client);
422     if (!buffer.validate(pathOpSubmitter.has_value())) { return nullptr; }
423     return alloc->makeUnique<PathSubRun>(std::move(*pathOpSubmitter));
424 }
425 
426 // -- DrawableOpSubmitter --------------------------------------------------------------------------
427 // Shared code for submitting GPU ops for drawing glyphs as drawables.
428 class DrawableOpSubmitter {
429 public:
430     DrawableOpSubmitter() = delete;
431     DrawableOpSubmitter(const DrawableOpSubmitter&) = delete;
432     const DrawableOpSubmitter& operator=(const DrawableOpSubmitter&) = delete;
DrawableOpSubmitter(DrawableOpSubmitter && that)433     DrawableOpSubmitter(DrawableOpSubmitter&& that)
434         : fStrikeToSourceScale{that.fStrikeToSourceScale}
435         , fPositions{that.fPositions}
436         , fIDsOrDrawables{that.fIDsOrDrawables}
437         , fStrikePromise{std::move(that.fStrikePromise)} {}
operator =(DrawableOpSubmitter && that)438     DrawableOpSubmitter& operator=(DrawableOpSubmitter&& that) {
439         this->~DrawableOpSubmitter();
440         new (this) DrawableOpSubmitter{std::move(that)};
441         return *this;
442     }
443     DrawableOpSubmitter(SkScalar strikeToSourceScale,
444                         SkSpan<SkPoint> positions,
445                         SkSpan<IDOrDrawable> idsOrDrawables,
446                         SkStrikePromise&& strikePromise);
447 
Make(SkZip<const SkGlyphID,const SkPoint> accepted,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)448     static DrawableOpSubmitter Make(SkZip<const SkGlyphID, const SkPoint> accepted,
449                                     SkScalar strikeToSourceScale,
450                                     SkStrikePromise&& strikePromise,
451                                     SubRunAllocator* alloc) {
452         auto mapToIDOrDrawable = [](const SkGlyphID glyphID) { return IDOrDrawable{glyphID}; };
453 
454         return DrawableOpSubmitter{
455             strikeToSourceScale,
456             alloc->makePODSpan(get_positions(accepted)),
457             alloc->makePODArray<IDOrDrawable>(get_glyphIDs(accepted), mapToIDOrDrawable),
458             std::move(strikePromise)};
459     }
460 
461     int unflattenSize() const;
462     void flatten(SkWriteBuffer& buffer) const;
463     static std::optional<DrawableOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
464                                                              SubRunAllocator* alloc,
465                                                              const SkStrikeClient* client);
466     void submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint& paint) const;
467 
468 private:
469     const SkScalar fStrikeToSourceScale;
470     const SkSpan<SkPoint> fPositions;
471     const SkSpan<IDOrDrawable> fIDsOrDrawables;
472     // When the promise is converted to a strike it acts as the ref on the strike to keep the
473     // SkDrawable data alive.
474     mutable SkStrikePromise fStrikePromise;
475     mutable SkOnce fConvertIDsToDrawables;
476 };
477 
unflattenSize() const478 int DrawableOpSubmitter::unflattenSize() const {
479     return fPositions.size_bytes() + fIDsOrDrawables.size_bytes();
480 }
481 
flatten(SkWriteBuffer & buffer) const482 void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const {
483     fStrikePromise.flatten(buffer);
484 
485     buffer.writeScalar(fStrikeToSourceScale);
486     buffer.writePointArray(fPositions.data(), SkCount(fPositions));
487     for (IDOrDrawable idOrDrawable : fIDsOrDrawables) {
488         buffer.writeInt(idOrDrawable.fGlyphID);
489     }
490 }
491 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)492 std::optional<DrawableOpSubmitter> DrawableOpSubmitter::MakeFromBuffer(
493         SkReadBuffer& buffer, SubRunAllocator* alloc, const SkStrikeClient* client) {
494     std::optional<SkStrikePromise> strikePromise =
495             SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache());
496     if (!buffer.validate(strikePromise.has_value())) {
497         return std::nullopt;
498     }
499 
500     SkScalar strikeToSourceScale = buffer.readScalar();
501     if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; }
502 
503     SkSpan<SkPoint> positions = MakePointsFromBuffer(buffer, alloc);
504     if (positions.empty()) { return std::nullopt; }
505     const int glyphCount = SkCount(positions);
506 
507     if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; }
508     auto idsOrDrawables = alloc->makePODArray<IDOrDrawable>(glyphCount);
509     for (int i = 0; i < SkToInt(glyphCount); ++i) {
510         // Remember, we stored an int for glyph id.
511         idsOrDrawables[i].fGlyphID = SkTo<SkGlyphID>(buffer.readInt());
512     }
513 
514     if (!buffer.isValid()) {
515         return std::nullopt;
516     }
517 
518     return DrawableOpSubmitter{strikeToSourceScale,
519                                positions,
520                                SkSpan(idsOrDrawables, glyphCount),
521                                std::move(strikePromise.value())};
522 }
523 
DrawableOpSubmitter(SkScalar strikeToSourceScale,SkSpan<SkPoint> positions,SkSpan<IDOrDrawable> idsOrDrawables,SkStrikePromise && strikePromise)524 DrawableOpSubmitter::DrawableOpSubmitter(
525         SkScalar strikeToSourceScale,
526         SkSpan<SkPoint> positions,
527         SkSpan<IDOrDrawable> idsOrDrawables,
528         SkStrikePromise&& strikePromise)
529         : fStrikeToSourceScale{strikeToSourceScale}
530         , fPositions{positions}
531         , fIDsOrDrawables{idsOrDrawables}
532         , fStrikePromise(std::move(strikePromise)) {
533     SkASSERT(!fPositions.empty());
534 }
535 
536 void
submitDraws(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint) const537 DrawableOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin,const SkPaint& paint) const {
538     // Convert glyph IDs to Drawables if it hasn't been done yet.
539     fConvertIDsToDrawables([&]() {
540         fStrikePromise.strike()->glyphIDsToDrawables(fIDsOrDrawables);
541         // Do not call resetStrike() because the strike must remain owned to ensure the Drawable
542         // data is not freed.
543     });
544 
545     // Calculate the matrix that maps the path glyphs from their size in the strike to
546     // the graphics source space.
547     SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
548     strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
549 
550     // Transform the path to device because the deviceMatrix must be unchanged to
551     // draw effect, filter or shader paths.
552     for (auto [i, position] : SkMakeEnumerate(fPositions)) {
553         SkDrawable* drawable = fIDsOrDrawables[i].fDrawable;
554 
555         if (drawable == nullptr) {
556             // This better be pinned to keep the drawable data alive.
557             fStrikePromise.strike()->verifyPinnedStrike();
558             SkDEBUGFAIL("Drawable should not be nullptr.");
559             continue;
560         }
561 
562         // Transform the glyph to source space.
563         SkMatrix pathMatrix = strikeToSource;
564         pathMatrix.postTranslate(position.x(), position.y());
565 
566         SkAutoCanvasRestore acr(canvas, false);
567         SkRect drawableBounds = drawable->getBounds();
568         pathMatrix.mapRect(&drawableBounds);
569         canvas->saveLayer(&drawableBounds, &paint);
570         drawable->draw(canvas, &pathMatrix);
571     }
572 }
573 
574 // -- DrawableSubRun -------------------------------------------------------------------------------
575 class DrawableSubRun : public SubRun {
576 public:
DrawableSubRun(DrawableOpSubmitter && drawingDrawing)577     DrawableSubRun(DrawableOpSubmitter&& drawingDrawing)
578             : fDrawingDrawing(std::move(drawingDrawing)) {}
579 
Make(SkZip<const SkGlyphID,const SkPoint> drawables,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)580     static SubRunOwner Make(SkZip<const SkGlyphID, const SkPoint> drawables,
581                             SkScalar strikeToSourceScale,
582                             SkStrikePromise&& strikePromise,
583                             SubRunAllocator* alloc) {
584         return alloc->makeUnique<DrawableSubRun>(
585                 DrawableOpSubmitter::Make(drawables,
586                                           strikeToSourceScale,
587                                           std::move(strikePromise),
588                                           alloc));
589     }
590 
591     static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer,
592                                       SubRunAllocator* alloc,
593                                       const SkStrikeClient* client);
594 
draw(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt>,const AtlasDrawDelegate &) const595     void draw(SkCanvas* canvas,
596               SkPoint drawOrigin,
597               const SkPaint& paint,
598               sk_sp<SkRefCnt>,
599               const AtlasDrawDelegate&) const override {
600         fDrawingDrawing.submitDraws(canvas, drawOrigin, paint);
601     }
602 
603     int unflattenSize() const override;
604 
605     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
606 
607     const AtlasSubRun* testingOnly_atlasSubRun() const override;
608 
609 protected:
subRunStreamTag() const610     SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kDrawableStreamTag; }
611     void doFlatten(SkWriteBuffer& buffer) const override;
612 
613 private:
614     DrawableOpSubmitter fDrawingDrawing;
615 };
616 
unflattenSize() const617 int DrawableSubRun::unflattenSize() const {
618     return sizeof(DrawableSubRun) + fDrawingDrawing.unflattenSize();
619 }
620 
doFlatten(SkWriteBuffer & buffer) const621 void DrawableSubRun::doFlatten(SkWriteBuffer& buffer) const {
622     fDrawingDrawing.flatten(buffer);
623 }
624 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)625 SubRunOwner DrawableSubRun::MakeFromBuffer(SkReadBuffer& buffer,
626                                            SubRunAllocator* alloc,
627                                            const SkStrikeClient* client) {
628     auto drawableOpSubmitter = DrawableOpSubmitter::MakeFromBuffer(buffer, alloc, client);
629     if (!buffer.validate(drawableOpSubmitter.has_value())) { return nullptr; }
630     return alloc->makeUnique<DrawableSubRun>(std::move(*drawableOpSubmitter));
631 }
632 
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const633 bool DrawableSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
634     return true;
635 }
636 
testingOnly_atlasSubRun() const637 const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const {
638     return nullptr;
639 }
640 
641 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
642 enum ClipMethod {
643     kClippedOut,
644     kUnclipped,
645     kGPUClipped,
646     kGeometryClipped
647 };
648 
649 std::tuple<ClipMethod, SkIRect>
calculate_clip(const GrClip * clip,SkRect deviceBounds,SkRect glyphBounds)650 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
651     if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
652         return {kClippedOut, SkIRect::MakeEmpty()};
653     } else if (clip != nullptr) {
654         switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
655             case GrClip::Effect::kClippedOut:
656                 return {kClippedOut, SkIRect::MakeEmpty()};
657             case GrClip::Effect::kUnclipped:
658                 return {kUnclipped, SkIRect::MakeEmpty()};
659             case GrClip::Effect::kClipped: {
660                 if (result.fIsRRect && result.fRRect.isRect()) {
661                     SkRect r = result.fRRect.rect();
662                     if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
663                         SkIRect clipRect = SkIRect::MakeEmpty();
664                         // Clip geometrically during onPrepare using clipRect.
665                         r.round(&clipRect);
666                         if (clipRect.contains(glyphBounds)) {
667                             // If fully within the clip, signal no clipping using the empty rect.
668                             return {kUnclipped, SkIRect::MakeEmpty()};
669                         }
670                         // Use the clipRect to clip the geometry.
671                         return {kGeometryClipped, clipRect};
672                     }
673                     // Partial pixel clipped at this point. Have the GPU handle it.
674                 }
675             }
676             break;
677         }
678     }
679     return {kGPUClipped, SkIRect::MakeEmpty()};
680 }
681 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
682 
683 // -- DirectMaskSubRun -----------------------------------------------------------------------------
684 class DirectMaskSubRun final : public SubRun, public AtlasSubRun {
685 public:
DirectMaskSubRun(VertexFiller && vertexFiller,GlyphVector && glyphs)686     DirectMaskSubRun(VertexFiller&& vertexFiller,
687                      GlyphVector&& glyphs)
688             : fVertexFiller{std::move(vertexFiller)}
689             , fGlyphs{std::move(glyphs)} {}
690 
Make(SkRect creationBounds,SkZip<const SkPackedGlyphID,const SkPoint> accepted,const SkMatrix & creationMatrix,SkStrikePromise && strikePromise,MaskFormat maskType,SubRunAllocator * alloc)691     static SubRunOwner Make(SkRect creationBounds,
692                             SkZip<const SkPackedGlyphID, const SkPoint> accepted,
693                             const SkMatrix& creationMatrix,
694                             SkStrikePromise&& strikePromise,
695                             MaskFormat maskType,
696                             SubRunAllocator* alloc) {
697         auto vertexFiller = VertexFiller::Make(maskType,
698                                                creationMatrix,
699                                                creationBounds,
700                                                get_positions(accepted),
701                                                alloc,
702                                                kIsDirect);
703 
704         auto glyphVector =
705                 GlyphVector::Make(std::move(strikePromise), get_packedIDs(accepted), alloc);
706 
707         return alloc->makeUnique<DirectMaskSubRun>(std::move(vertexFiller), std::move(glyphVector));
708     }
709 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)710     static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer,
711                                       SubRunAllocator* alloc,
712                                       const SkStrikeClient* client) {
713         auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc);
714         if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
715 
716         auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
717         if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
718         if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
719             return nullptr;
720         }
721 
722         SkASSERT(buffer.isValid());
723         return alloc->makeUnique<DirectMaskSubRun>(
724                 std::move(*vertexFiller), std::move(*glyphVector));
725     }
726 
draw(SkCanvas *,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,const AtlasDrawDelegate & drawAtlas) const727     void draw(SkCanvas*,
728               SkPoint drawOrigin,
729               const SkPaint& paint,
730               sk_sp<SkRefCnt> subRunStorage,
731               const AtlasDrawDelegate& drawAtlas) const override {
732         drawAtlas(this, drawOrigin, paint, std::move(subRunStorage),
733                   {/* isSDF = */false, fVertexFiller.isLCD(), fVertexFiller.grMaskType()});
734     }
735 
unflattenSize() const736     int unflattenSize() const override {
737         return sizeof(DirectMaskSubRun) +
738                fGlyphs.unflattenSize() +
739                fVertexFiller.unflattenSize();
740     }
741 
glyphCount() const742     int glyphCount() const override {
743         return SkCount(fGlyphs.glyphs());
744     }
745 
glyphs() const746     SkSpan<const Glyph*> glyphs() const override {
747         return fGlyphs.glyphs();
748     }
749 
maskFormat() const750     MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); }
751 
glyphSrcPadding() const752     int glyphSrcPadding() const override { return 0; }
753 
instanceFlags() const754     unsigned short instanceFlags() const override {
755         return (unsigned short)fVertexFiller.grMaskType();
756     }
757 
testingOnly_packedGlyphIDToGlyph(StrikeCache * cache) const758     void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override {
759         fGlyphs.packedGlyphIDToGlyph(cache);
760     }
761 
762 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
vertexStride(const SkMatrix & drawMatrix) const763     size_t vertexStride(const SkMatrix& drawMatrix) const override {
764         return fVertexFiller.vertexStride(drawMatrix);
765     }
766 
makeAtlasTextOp(const GrClip * clip,const SkMatrix & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> && subRunStorage,skgpu::ganesh::SurfaceDrawContext * sdc) const767     std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
768             const GrClip* clip,
769             const SkMatrix& viewMatrix,
770             SkPoint drawOrigin,
771             const SkPaint& paint,
772             sk_sp<SkRefCnt>&& subRunStorage,
773             skgpu::ganesh::SurfaceDrawContext* sdc) const override {
774         SkASSERT(this->glyphCount() != 0);
775         const SkMatrix& positionMatrix = position_matrix(viewMatrix, drawOrigin);
776 
777         auto [integerTranslate, subRunDeviceBounds] =
778                 fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
779         if (subRunDeviceBounds.isEmpty()) {
780             return {nullptr, nullptr};
781         }
782         // Rect for optimized bounds clipping when doing an integer translate.
783         SkIRect geometricClipRect = SkIRect::MakeEmpty();
784         if (integerTranslate) {
785             // We can clip geometrically using clipRect and ignore clip when an axis-aligned
786             // rectangular non-AA clip is used. If clipRect is empty, and clip is nullptr, then
787             // there is no clipping needed.
788             const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
789             auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds);
790 
791             switch (clipMethod) {
792                 case kClippedOut:
793                     // Returning nullptr as op means skip this op.
794                     return {nullptr, nullptr};
795                 case kUnclipped:
796                 case kGeometryClipped:
797                     // GPU clip is not needed.
798                     clip = nullptr;
799                     break;
800                 case kGPUClipped:
801                     // Use th GPU clip; clipRect is ignored.
802                     break;
803             }
804             geometricClipRect = clipRect;
805 
806             if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); }
807         }
808 
809         GrPaint grPaint;
810         const SkPMColor4f drawingColor = calculate_colors(sdc,
811                                                           paint,
812                                                           viewMatrix,
813                                                           fVertexFiller.grMaskType(),
814                                                           &grPaint);
815 
816         auto geometry = AtlasTextOp::Geometry::Make(*this,
817                                                     viewMatrix,
818                                                     drawOrigin,
819                                                     geometricClipRect,
820                                                     std::move(subRunStorage),
821                                                     drawingColor,
822                                                     sdc->arenaAlloc());
823 
824         GrRecordingContext* const rContext = sdc->recordingContext();
825 
826         GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
827                                                  fVertexFiller.opMaskType(),
828                                                  !integerTranslate,
829                                                  this->glyphCount(),
830                                                  subRunDeviceBounds,
831                                                  geometry,
832                                                  sdc->colorInfo(),
833                                                  std::move(grPaint));
834         return {clip, std::move(op)};
835     }
836 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const837     void fillVertexData(void* vertexDst, int offset, int count,
838                         GrColor color,
839                         const SkMatrix& drawMatrix, SkPoint drawOrigin,
840                         SkIRect clip) const override {
841         const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
842         fVertexFiller.fillVertexData(offset, count,
843                                      fGlyphs.glyphs(),
844                                      color,
845                                      positionMatrix,
846                                      clip,
847                                      vertexDst);
848     }
849 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
850 
regenerateAtlas(int begin,int end,RegenerateAtlasDelegate regenerateAtlas) const851     std::tuple<bool, int> regenerateAtlas(int begin, int end,
852                                           RegenerateAtlasDelegate regenerateAtlas) const override {
853         return regenerateAtlas(
854                 &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding());
855     }
856 
vertexFiller() const857     const VertexFiller& vertexFiller() const override { return fVertexFiller; }
858 
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const859     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
860         auto [reuse, _] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
861         return reuse;
862     }
863 
testingOnly_atlasSubRun() const864     const AtlasSubRun* testingOnly_atlasSubRun() const override {
865         return this;
866     }
867 
868 protected:
subRunStreamTag() const869     SubRunStreamTag subRunStreamTag() const override {
870         return SubRunStreamTag::kDirectMaskStreamTag;
871     }
872 
doFlatten(SkWriteBuffer & buffer) const873     void doFlatten(SkWriteBuffer& buffer) const override {
874         fVertexFiller.flatten(buffer);
875         fGlyphs.flatten(buffer);
876     }
877 
878 private:
879     const VertexFiller fVertexFiller;
880 
881     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
882     // be single threaded.
883     mutable GlyphVector fGlyphs;
884 };
885 
886 // -- TransformedMaskSubRun ------------------------------------------------------------------------
887 class TransformedMaskSubRun final : public SubRun, public AtlasSubRun {
888 public:
TransformedMaskSubRun(bool isBigEnough,VertexFiller && vertexFiller,GlyphVector && glyphs)889     TransformedMaskSubRun(bool isBigEnough,
890                           VertexFiller&& vertexFiller,
891                           GlyphVector&& glyphs)
892             : fIsBigEnough{isBigEnough}
893             , fVertexFiller{std::move(vertexFiller)}
894             , fGlyphs{std::move(glyphs)} {}
895 
Make(SkZip<const SkPackedGlyphID,const SkPoint> accepted,const SkMatrix & initialPositionMatrix,SkStrikePromise && strikePromise,SkMatrix creationMatrix,SkRect creationBounds,MaskFormat maskType,SubRunAllocator * alloc)896     static SubRunOwner Make(SkZip<const SkPackedGlyphID, const SkPoint> accepted,
897                             const SkMatrix& initialPositionMatrix,
898                             SkStrikePromise&& strikePromise,
899                             SkMatrix creationMatrix,
900                             SkRect creationBounds,
901                             MaskFormat maskType,
902                             SubRunAllocator* alloc) {
903         auto vertexFiller = VertexFiller::Make(maskType,
904                                                creationMatrix,
905                                                creationBounds,
906                                                get_positions(accepted),
907                                                alloc,
908                                                kIsTransformed);
909 
910         auto glyphVector = GlyphVector::Make(
911                 std::move(strikePromise), get_packedIDs(accepted), alloc);
912 
913         return alloc->makeUnique<TransformedMaskSubRun>(
914                 initialPositionMatrix.getMaxScale() >= 1,
915                 std::move(vertexFiller),
916                 std::move(glyphVector));
917     }
918 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)919     static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer,
920                                       SubRunAllocator* alloc,
921                                       const SkStrikeClient* client) {
922         auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc);
923         if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
924 
925         auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
926         if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
927         if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
928             return nullptr;
929         }
930         const bool isBigEnough = buffer.readBool();
931         return alloc->makeUnique<TransformedMaskSubRun>(
932                 isBigEnough, std::move(*vertexFiller), std::move(*glyphVector));
933     }
934 
unflattenSize() const935     int unflattenSize() const override {
936         return sizeof(TransformedMaskSubRun) +
937                fGlyphs.unflattenSize() +
938                fVertexFiller.unflattenSize();
939     }
940 
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const941     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
942         // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may
943         // be better.
944         return fIsBigEnough;
945     }
946 
testingOnly_atlasSubRun() const947     const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
948 
testingOnly_packedGlyphIDToGlyph(StrikeCache * cache) const949     void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
950         fGlyphs.packedGlyphIDToGlyph(cache);
951     }
952 
glyphCount() const953     int glyphCount() const override { return SkCount(fGlyphs.glyphs()); }
954 
glyphs() const955     SkSpan<const Glyph*> glyphs() const override {
956         return fGlyphs.glyphs();
957     }
958 
instanceFlags() const959     unsigned short instanceFlags() const override {
960         return (unsigned short)fVertexFiller.grMaskType();
961     }
962 
maskFormat() const963     MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); }
964 
glyphSrcPadding() const965     int glyphSrcPadding() const override { return 1; }
966 
draw(SkCanvas *,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,const AtlasDrawDelegate & drawAtlas) const967     void draw(SkCanvas*,
968               SkPoint drawOrigin,
969               const SkPaint& paint,
970               sk_sp<SkRefCnt> subRunStorage,
971               const AtlasDrawDelegate& drawAtlas) const override {
972         drawAtlas(this, drawOrigin, paint, std::move(subRunStorage),
973                   {/* isSDF = */false, fVertexFiller.isLCD(), fVertexFiller.grMaskType()});
974     }
975 
976 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
977 
vertexStride(const SkMatrix & drawMatrix) const978     size_t vertexStride(const SkMatrix& drawMatrix) const override {
979         return fVertexFiller.vertexStride(drawMatrix);
980     }
981 
makeAtlasTextOp(const GrClip * clip,const SkMatrix & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> && subRunStorage,skgpu::ganesh::SurfaceDrawContext * sdc) const982     std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
983             const GrClip* clip,
984             const SkMatrix& viewMatrix,
985             SkPoint drawOrigin,
986             const SkPaint& paint,
987             sk_sp<SkRefCnt>&& subRunStorage,
988             skgpu::ganesh::SurfaceDrawContext* sdc) const override {
989         SkASSERT(this->glyphCount() != 0);
990 
991         GrPaint grPaint;
992         SkPMColor4f drawingColor = calculate_colors(sdc,
993                                                     paint,
994                                                     viewMatrix,
995                                                     fVertexFiller.grMaskType(),
996                                                     &grPaint);
997 
998         auto geometry = AtlasTextOp::Geometry::Make(*this,
999                                                     viewMatrix,
1000                                                     drawOrigin,
1001                                                     SkIRect::MakeEmpty(),
1002                                                     std::move(subRunStorage),
1003                                                     drawingColor,
1004                                                     sdc->arenaAlloc());
1005 
1006         GrRecordingContext* const rContext = sdc->recordingContext();
1007         SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin);
1008         auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
1009         GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1010                                                  fVertexFiller.opMaskType(),
1011                                                  true,
1012                                                  this->glyphCount(),
1013                                                  deviceRect,
1014                                                  geometry,
1015                                                  sdc->colorInfo(),
1016                                                  std::move(grPaint));
1017         return {clip, std::move(op)};
1018     }
1019 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1020     void fillVertexData(
1021             void* vertexDst, int offset, int count,
1022             GrColor color,
1023             const SkMatrix& drawMatrix, SkPoint drawOrigin,
1024             SkIRect clip) const override {
1025         const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1026         fVertexFiller.fillVertexData(offset, count,
1027                                      fGlyphs.glyphs(),
1028                                      color,
1029                                      positionMatrix,
1030                                      clip,
1031                                      vertexDst);
1032     }
1033 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1034 
regenerateAtlas(int begin,int end,RegenerateAtlasDelegate regenerateAtlas) const1035     std::tuple<bool, int> regenerateAtlas(int begin, int end,
1036                                           RegenerateAtlasDelegate regenerateAtlas) const override {
1037         return regenerateAtlas(
1038                 &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding());
1039     }
1040 
vertexFiller() const1041     const VertexFiller& vertexFiller() const override { return fVertexFiller; }
1042 
1043 protected:
subRunStreamTag() const1044     SubRunStreamTag subRunStreamTag() const override {
1045         return SubRunStreamTag::kTransformMaskStreamTag;
1046     }
1047 
doFlatten(SkWriteBuffer & buffer) const1048     void doFlatten(SkWriteBuffer& buffer) const override {
1049         fVertexFiller.flatten(buffer);
1050         fGlyphs.flatten(buffer);
1051         buffer.writeBool(fIsBigEnough);
1052     }
1053 
1054 private:
1055     const bool fIsBigEnough;
1056 
1057     const VertexFiller fVertexFiller;
1058 
1059     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1060     // be single threaded.
1061     mutable GlyphVector fGlyphs;
1062 };  // class TransformedMaskSubRun
1063 
1064 // -- SDFTSubRun -----------------------------------------------------------------------------------
1065 
has_some_antialiasing(const SkFont & font)1066 bool has_some_antialiasing(const SkFont& font ) {
1067     SkFont::Edging edging = font.getEdging();
1068     return edging == SkFont::Edging::kAntiAlias
1069            || edging == SkFont::Edging::kSubpixelAntiAlias;
1070 }
1071 
1072 #if !defined(SK_DISABLE_SDF_TEXT)
1073 
1074 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1075 
calculate_sdf_parameters(const skgpu::ganesh::SurfaceDrawContext & sdc,const SkMatrix & drawMatrix,bool useLCDText,bool isAntiAliased)1076 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1077         const skgpu::ganesh::SurfaceDrawContext& sdc,
1078         const SkMatrix& drawMatrix,
1079         bool useLCDText,
1080         bool isAntiAliased) {
1081     const GrColorInfo& colorInfo = sdc.colorInfo();
1082     const SkSurfaceProps& props = sdc.surfaceProps();
1083     using MT = AtlasTextOp::MaskType;
1084     bool isLCD = useLCDText && props.pixelGeometry() != kUnknown_SkPixelGeometry;
1085     MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1086                                  : isLCD ? MT::kLCDDistanceField
1087                                          : MT::kGrayscaleDistanceField;
1088 
1089     bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1090     uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1091     DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1092     DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1093     DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1094     DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
1095 
1096     if (isLCD) {
1097         bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1098         bool isVertical = SkPixelGeometryIsV(props.pixelGeometry());
1099         DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1100         DFGPFlags |= isBGR ? kBGR_DistanceFieldEffectFlag : 0;
1101         DFGPFlags |= isVertical ? kPortrait_DistanceFieldEffectFlag : 0;
1102     }
1103     return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1104 }
1105 
1106 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1107 
1108 class SDFTSubRun final : public SubRun, public AtlasSubRun {
1109 public:
SDFTSubRun(bool useLCDText,bool antiAliased,const SDFTMatrixRange & matrixRange,VertexFiller && vertexFiller,GlyphVector && glyphs)1110     SDFTSubRun(bool useLCDText,
1111                bool antiAliased,
1112                const SDFTMatrixRange& matrixRange,
1113                VertexFiller&& vertexFiller,
1114                GlyphVector&& glyphs)
1115         : fUseLCDText{useLCDText}
1116         , fAntiAliased{antiAliased}
1117         , fMatrixRange{matrixRange}
1118         , fVertexFiller{std::move(vertexFiller)}
1119         , fGlyphs{std::move(glyphs)} { }
1120 
Make(SkZip<const SkPackedGlyphID,const SkPoint> accepted,const SkFont & runFont,SkStrikePromise && strikePromise,const SkMatrix & creationMatrix,SkRect creationBounds,const SDFTMatrixRange & matrixRange,SubRunAllocator * alloc)1121     static SubRunOwner Make(SkZip<const SkPackedGlyphID, const SkPoint> accepted,
1122                             const SkFont& runFont,
1123                             SkStrikePromise&& strikePromise,
1124                             const SkMatrix& creationMatrix,
1125                             SkRect creationBounds,
1126                             const SDFTMatrixRange& matrixRange,
1127                             SubRunAllocator* alloc) {
1128         auto vertexFiller = VertexFiller::Make(MaskFormat::kA8,
1129                                                creationMatrix,
1130                                                creationBounds,
1131                                                get_positions(accepted),
1132                                                alloc,
1133                                                kIsTransformed);
1134 
1135         auto glyphVector = GlyphVector::Make(
1136                 std::move(strikePromise), get_packedIDs(accepted), alloc);
1137 
1138         return alloc->makeUnique<SDFTSubRun>(
1139                 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1140                 has_some_antialiasing(runFont),
1141                 matrixRange,
1142                 std::move(vertexFiller),
1143                 std::move(glyphVector));
1144     }
1145 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)1146     static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer,
1147                                       SubRunAllocator* alloc,
1148                                       const SkStrikeClient* client) {
1149         int useLCD = buffer.readInt();
1150         int isAntiAliased = buffer.readInt();
1151         SDFTMatrixRange matrixRange = SDFTMatrixRange::MakeFromBuffer(buffer);
1152         auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc);
1153         if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
1154         if (!buffer.validate(vertexFiller.value().grMaskType() == MaskFormat::kA8)) {
1155             return nullptr;
1156         }
1157         auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1158         if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1159         if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
1160             return nullptr;
1161         }
1162         return alloc->makeUnique<SDFTSubRun>(useLCD,
1163                                              isAntiAliased,
1164                                              matrixRange,
1165                                              std::move(*vertexFiller),
1166                                              std::move(*glyphVector));
1167     }
1168 
unflattenSize() const1169     int unflattenSize() const override {
1170         return sizeof(SDFTSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
1171     }
1172 
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1173     bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
1174         return fMatrixRange.matrixInRange(positionMatrix);
1175     }
1176 
testingOnly_atlasSubRun() const1177     const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
1178 
testingOnly_packedGlyphIDToGlyph(StrikeCache * cache) const1179     void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
1180         fGlyphs.packedGlyphIDToGlyph(cache);
1181     }
1182 
glyphCount() const1183     int glyphCount() const override { return fVertexFiller.count(); }
maskFormat() const1184     MaskFormat maskFormat() const override {
1185         SkASSERT(fVertexFiller.grMaskType() == MaskFormat::kA8);
1186         return MaskFormat::kA8;
1187     }
glyphSrcPadding() const1188     int glyphSrcPadding() const override { return SK_DistanceFieldInset; }
1189 
glyphs() const1190     SkSpan<const Glyph*> glyphs() const override {
1191         return fGlyphs.glyphs();
1192     }
1193 
instanceFlags() const1194     unsigned short instanceFlags() const override {
1195         return (unsigned short)MaskFormat::kA8;
1196     }
1197 
draw(SkCanvas *,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,const AtlasDrawDelegate & drawAtlas) const1198     void draw(SkCanvas*,
1199               SkPoint drawOrigin,
1200               const SkPaint& paint,
1201               sk_sp<SkRefCnt> subRunStorage,
1202               const AtlasDrawDelegate& drawAtlas) const override {
1203         drawAtlas(this, drawOrigin, paint, std::move(subRunStorage),
1204                   {/* isSDF = */true, /* isLCD = */fUseLCDText, skgpu::MaskFormat::kA8});
1205     }
1206 
1207 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
vertexStride(const SkMatrix & drawMatrix) const1208     size_t vertexStride(const SkMatrix& drawMatrix) const override {
1209         return fVertexFiller.vertexStride(drawMatrix);
1210     }
1211 
makeAtlasTextOp(const GrClip * clip,const SkMatrix & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> && subRunStorage,skgpu::ganesh::SurfaceDrawContext * sdc) const1212     std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
1213             const GrClip* clip,
1214             const SkMatrix& viewMatrix,
1215             SkPoint drawOrigin,
1216             const SkPaint& paint,
1217             sk_sp<SkRefCnt>&& subRunStorage,
1218             skgpu::ganesh::SurfaceDrawContext* sdc) const override {
1219         SkASSERT(this->glyphCount() != 0);
1220 
1221         GrPaint grPaint;
1222         SkPMColor4f drawingColor = calculate_colors(sdc,
1223                                                     paint,
1224                                                     viewMatrix,
1225                                                     MaskFormat::kA8,
1226                                                     &grPaint);
1227 
1228         auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1229                 calculate_sdf_parameters(*sdc, viewMatrix, fUseLCDText, fAntiAliased);
1230 
1231         auto geometry = AtlasTextOp::Geometry::Make(*this,
1232                                                     viewMatrix,
1233                                                     drawOrigin,
1234                                                     SkIRect::MakeEmpty(),
1235                                                     std::move(subRunStorage),
1236                                                     drawingColor,
1237                                                     sdc->arenaAlloc());
1238 
1239         GrRecordingContext* const rContext = sdc->recordingContext();
1240         SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin);
1241         auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
1242         GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1243                                                  maskType,
1244                                                  true,
1245                                                  this->glyphCount(),
1246                                                  deviceRect,
1247                                                  SkPaintPriv::ComputeLuminanceColor(paint),
1248                                                  useGammaCorrectDistanceTable,
1249                                                  DFGPFlags,
1250                                                  geometry,
1251                                                  std::move(grPaint));
1252 
1253         return {clip, std::move(op)};
1254     }
1255 
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1256     void fillVertexData(
1257             void *vertexDst, int offset, int count,
1258             GrColor color,
1259             const SkMatrix& drawMatrix, SkPoint drawOrigin,
1260             SkIRect clip) const override {
1261         const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1262 
1263         fVertexFiller.fillVertexData(offset, count,
1264                                      fGlyphs.glyphs(),
1265                                      color,
1266                                      positionMatrix,
1267                                      clip,
1268                                      vertexDst);
1269     }
1270 
1271 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1272 
regenerateAtlas(int begin,int end,RegenerateAtlasDelegate regenerateAtlas) const1273     std::tuple<bool, int> regenerateAtlas(int begin, int end,
1274                                           RegenerateAtlasDelegate regenerateAtlas) const override {
1275         return regenerateAtlas(&fGlyphs, begin, end, MaskFormat::kA8, this->glyphSrcPadding());
1276     }
1277 
vertexFiller() const1278     const VertexFiller& vertexFiller() const override { return fVertexFiller; }
1279 
1280 protected:
subRunStreamTag() const1281     SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kSDFTStreamTag; }
doFlatten(SkWriteBuffer & buffer) const1282     void doFlatten(SkWriteBuffer& buffer) const override {
1283         buffer.writeInt(fUseLCDText);
1284         buffer.writeInt(fAntiAliased);
1285         fMatrixRange.flatten(buffer);
1286         fVertexFiller.flatten(buffer);
1287         fGlyphs.flatten(buffer);
1288     }
1289 
1290 private:
1291     const bool fUseLCDText;
1292     const bool fAntiAliased;
1293     const SDFTMatrixRange fMatrixRange;
1294 
1295     const VertexFiller fVertexFiller;
1296 
1297     // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1298     // be single threaded.
1299     mutable GlyphVector fGlyphs;
1300 };  // class SDFTSubRun
1301 
1302 #endif // !defined(SK_DISABLE_SDF_TEXT)
1303 
1304 // -- SubRun ---------------------------------------------------------------------------------------
1305 
1306 template<typename AddSingleMaskFormat>
add_multi_mask_format(AddSingleMaskFormat addSingleMaskFormat,SkZip<const SkPackedGlyphID,const SkPoint,const SkMask::Format> accepted)1307 void add_multi_mask_format(
1308         AddSingleMaskFormat addSingleMaskFormat,
1309         SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format> accepted) {
1310     if (accepted.empty()) { return; }
1311 
1312     auto maskSpan = accepted.get<2>();
1313     MaskFormat format = Glyph::FormatFromSkGlyph(maskSpan[0]);
1314     size_t startIndex = 0;
1315     for (size_t i = 1; i < accepted.size(); i++) {
1316         MaskFormat nextFormat = Glyph::FormatFromSkGlyph(maskSpan[i]);
1317         if (format != nextFormat) {
1318             auto interval = accepted.subspan(startIndex, i - startIndex);
1319             // Only pass the packed glyph ids and positions.
1320             auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>());
1321             // Take a ref on the strike. This should rarely happen.
1322             addSingleMaskFormat(glyphsWithSameFormat, format);
1323             format = nextFormat;
1324             startIndex = i;
1325         }
1326     }
1327     auto interval = accepted.last(accepted.size() - startIndex);
1328     auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>());
1329     addSingleMaskFormat(glyphsWithSameFormat, format);
1330 }
1331 }  // namespace
1332 
1333 namespace sktext::gpu {
1334 SubRun::~SubRun() = default;
flatten(SkWriteBuffer & buffer) const1335 void SubRun::flatten(SkWriteBuffer& buffer) const {
1336     buffer.writeInt(this->subRunStreamTag());
1337     this->doFlatten(buffer);
1338 }
1339 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)1340 SubRunOwner SubRun::MakeFromBuffer(SkReadBuffer& buffer,
1341                                    SubRunAllocator* alloc,
1342                                    const SkStrikeClient* client) {
1343     using Maker = SubRunOwner (*)(SkReadBuffer&,
1344                                   SubRunAllocator*,
1345                                   const SkStrikeClient*);
1346 
1347     static Maker makers[kSubRunStreamTagCount] = {
1348             nullptr,                                             // 0 index is bad.
1349             DirectMaskSubRun::MakeFromBuffer,
1350 #if !defined(SK_DISABLE_SDF_TEXT)
1351             SDFTSubRun::MakeFromBuffer,
1352 #endif
1353             TransformedMaskSubRun::MakeFromBuffer,
1354             PathSubRun::MakeFromBuffer,
1355             DrawableSubRun::MakeFromBuffer,
1356     };
1357     int subRunTypeInt = buffer.readInt();
1358     if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunStreamTagCount)) {
1359         return nullptr;
1360     }
1361     auto maker = makers[subRunTypeInt];
1362     if (!buffer.validate(maker != nullptr)) { return nullptr; }
1363     return maker(buffer, alloc, client);
1364 }
1365 
1366 // -- SubRunContainer ------------------------------------------------------------------------------
SubRunContainer(const SkMatrix & initialPositionMatrix)1367 SubRunContainer::SubRunContainer(const SkMatrix& initialPositionMatrix)
1368         : fInitialPositionMatrix{initialPositionMatrix} {}
1369 
flattenAllocSizeHint(SkWriteBuffer & buffer) const1370 void SubRunContainer::flattenAllocSizeHint(SkWriteBuffer& buffer) const {
1371     int unflattenSizeHint = 0;
1372     for (auto& subrun : fSubRuns) {
1373         unflattenSizeHint += subrun.unflattenSize();
1374     }
1375     buffer.writeInt(unflattenSizeHint);
1376 }
1377 
AllocSizeHintFromBuffer(SkReadBuffer & buffer)1378 int SubRunContainer::AllocSizeHintFromBuffer(SkReadBuffer& buffer) {
1379     int subRunsSizeHint = buffer.readInt();
1380 
1381     // Since the hint doesn't affect correctness, if it looks fishy just pick a reasonable
1382     // value.
1383     if (subRunsSizeHint < 0 || (1 << 16) < subRunsSizeHint) {
1384         subRunsSizeHint = 128;
1385     }
1386     return subRunsSizeHint;
1387 }
1388 
flattenRuns(SkWriteBuffer & buffer) const1389 void SubRunContainer::flattenRuns(SkWriteBuffer& buffer) const {
1390     buffer.writeMatrix(fInitialPositionMatrix);
1391     int subRunCount = 0;
1392     for ([[maybe_unused]] auto& subRun : fSubRuns) {
1393         subRunCount += 1;
1394     }
1395     buffer.writeInt(subRunCount);
1396     for (auto& subRun : fSubRuns) {
1397         subRun.flatten(buffer);
1398     }
1399 }
1400 
MakeFromBufferInAlloc(SkReadBuffer & buffer,const SkStrikeClient * client,SubRunAllocator * alloc)1401 SubRunContainerOwner SubRunContainer::MakeFromBufferInAlloc(SkReadBuffer& buffer,
1402                                                             const SkStrikeClient* client,
1403                                                             SubRunAllocator* alloc) {
1404     SkMatrix positionMatrix;
1405     buffer.readMatrix(&positionMatrix);
1406     if (!buffer.isValid()) { return nullptr; }
1407     SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix);
1408 
1409     int subRunCount = buffer.readInt();
1410     if (!buffer.validate(subRunCount > 0)) { return nullptr; }
1411     for (int i = 0; i < subRunCount; ++i) {
1412         auto subRunOwner = SubRun::MakeFromBuffer(buffer, alloc, client);
1413         if (!buffer.validate(subRunOwner != nullptr)) { return nullptr; }
1414         if (subRunOwner != nullptr) {
1415             container->fSubRuns.append(std::move(subRunOwner));
1416         }
1417     }
1418     return container;
1419 }
1420 
EstimateAllocSize(const GlyphRunList & glyphRunList)1421 size_t SubRunContainer::EstimateAllocSize(const GlyphRunList& glyphRunList) {
1422     // The difference in alignment from the per-glyph data to the SubRun;
1423     constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(SkPoint);
1424     constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
1425     size_t totalGlyphCount = glyphRunList.totalGlyphCount();
1426     // This is optimized for DirectMaskSubRun which is by far the most common case.
1427     return totalGlyphCount * sizeof(SkPoint)
1428            + GlyphVector::GlyphVectorSize(totalGlyphCount)
1429            + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding)
1430            + sizeof(SubRunContainer);
1431 }
1432 
find_maximum_glyph_dimension(StrikeForGPU * strike,SkSpan<const SkGlyphID> glyphs)1433 SkScalar find_maximum_glyph_dimension(StrikeForGPU* strike, SkSpan<const SkGlyphID> glyphs) {
1434     StrikeMutationMonitor m{strike};
1435     SkScalar maxDimension = 0;
1436     for (SkGlyphID glyphID : glyphs) {
1437         SkGlyphDigest digest = strike->digestFor(kMask, SkPackedGlyphID{glyphID});
1438         maxDimension = std::max(static_cast<SkScalar>(digest.maxDimension()), maxDimension);
1439     }
1440 
1441     return maxDimension;
1442 }
1443 
1444 #if !defined(SK_DISABLE_SDF_TEXT)
1445 std::tuple<SkZip<const SkPackedGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>, SkRect>
prepare_for_SDFT_drawing(StrikeForGPU * strike,const SkMatrix & creationMatrix,SkZip<const SkGlyphID,const SkPoint> source,SkZip<SkPackedGlyphID,SkPoint> acceptedBuffer,SkZip<SkGlyphID,SkPoint> rejectedBuffer)1446 prepare_for_SDFT_drawing(StrikeForGPU* strike,
1447                          const SkMatrix& creationMatrix,
1448                          SkZip<const SkGlyphID, const SkPoint> source,
1449                          SkZip<SkPackedGlyphID, SkPoint> acceptedBuffer,
1450                          SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1451     int acceptedSize = 0,
1452         rejectedSize = 0;
1453     SkGlyphRect boundingRect = skglyph::empty_rect();
1454     StrikeMutationMonitor m{strike};
1455     for (const auto [glyphID, pos] : source) {
1456         if (!SkIsFinite(pos.x(), pos.y())) {
1457             continue;
1458         }
1459 
1460         const SkPackedGlyphID packedID{glyphID};
1461         switch (const SkGlyphDigest digest = strike->digestFor(skglyph::kSDFT, packedID);
1462                 digest.actionFor(skglyph::kSDFT)) {
1463             case GlyphAction::kAccept: {
1464                 SkPoint mappedPos = creationMatrix.mapPoint(pos);
1465                 const SkGlyphRect glyphBounds =
1466                     digest.bounds()
1467                         // The SDFT glyphs have 2-pixel wide padding that should
1468                         // not be used in calculating the source rectangle.
1469                         .inset(SK_DistanceFieldInset, SK_DistanceFieldInset)
1470                         .offset(mappedPos);
1471                 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
1472                 acceptedBuffer[acceptedSize++] = std::make_tuple(packedID, glyphBounds.leftTop());
1473                 break;
1474             }
1475             case GlyphAction::kReject:
1476                 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1477             break;
1478             default:
1479                 break;
1480         }
1481     }
1482 
1483     return {acceptedBuffer.first(acceptedSize),
1484             rejectedBuffer.first(rejectedSize),
1485             boundingRect.rect()};
1486 }
1487 #endif
1488 
1489 std::tuple<SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format>,
1490            SkZip<SkGlyphID, SkPoint>,
1491            SkRect>
prepare_for_direct_mask_drawing(StrikeForGPU * strike,const SkMatrix & positionMatrix,SkZip<const SkGlyphID,const SkPoint> source,SkZip<SkPackedGlyphID,SkPoint,SkMask::Format> acceptedBuffer,SkZip<SkGlyphID,SkPoint> rejectedBuffer)1492 prepare_for_direct_mask_drawing(StrikeForGPU* strike,
1493                                 const SkMatrix& positionMatrix,
1494                                 SkZip<const SkGlyphID, const SkPoint> source,
1495                                 SkZip<SkPackedGlyphID, SkPoint, SkMask::Format> acceptedBuffer,
1496                                 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1497     const SkIPoint mask = strike->roundingSpec().ignorePositionFieldMask;
1498     const SkPoint halfSampleFreq = strike->roundingSpec().halfAxisSampleFreq;
1499 
1500     // Build up the mapping from source space to device space. Add the rounding constant
1501     // halfSampleFreq, so we just need to floor to get the device result.
1502     SkMatrix positionMatrixWithRounding = positionMatrix;
1503     positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
1504 
1505     int acceptedSize = 0,
1506         rejectedSize = 0;
1507     SkGlyphRect boundingRect = skglyph::empty_rect();
1508     StrikeMutationMonitor m{strike};
1509     for (auto [glyphID, pos] : source) {
1510         if (!SkIsFinite(pos.x(), pos.y())) {
1511             continue;
1512         }
1513 
1514         const SkPoint mappedPos = positionMatrixWithRounding.mapPoint(pos);
1515         const SkPackedGlyphID packedID{glyphID, mappedPos, mask};
1516         switch (const SkGlyphDigest digest = strike->digestFor(skglyph::kDirectMask, packedID);
1517                 digest.actionFor(skglyph::kDirectMask)) {
1518             case GlyphAction::kAccept: {
1519                 const SkPoint roundedPos{SkScalarFloorToScalar(mappedPos.x()),
1520                                          SkScalarFloorToScalar(mappedPos.y())};
1521                 const SkGlyphRect glyphBounds = digest.bounds().offset(roundedPos);
1522                 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
1523                 acceptedBuffer[acceptedSize++] =
1524                         std::make_tuple(packedID, glyphBounds.leftTop(), digest.maskFormat());
1525                 break;
1526             }
1527             case GlyphAction::kReject:
1528                 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1529                 break;
1530             default:
1531                 break;
1532         }
1533     }
1534 
1535     return {acceptedBuffer.first(acceptedSize),
1536             rejectedBuffer.first(rejectedSize),
1537             boundingRect.rect()};
1538 }
1539 
1540 std::tuple<SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format>,
1541            SkZip<SkGlyphID, SkPoint>,
1542            SkRect>
prepare_for_mask_drawing(StrikeForGPU * strike,const SkMatrix & creationMatrix,SkZip<const SkGlyphID,const SkPoint> source,SkZip<SkPackedGlyphID,SkPoint,SkMask::Format> acceptedBuffer,SkZip<SkGlyphID,SkPoint> rejectedBuffer)1543 prepare_for_mask_drawing(StrikeForGPU* strike,
1544                          const SkMatrix& creationMatrix,
1545                          SkZip<const SkGlyphID, const SkPoint> source,
1546                          SkZip<SkPackedGlyphID, SkPoint, SkMask::Format> acceptedBuffer,
1547                          SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1548     int acceptedSize = 0,
1549         rejectedSize = 0;
1550     SkGlyphRect boundingRect = skglyph::empty_rect();
1551     StrikeMutationMonitor m{strike};
1552     for (auto [glyphID, pos] : source) {
1553         if (!SkIsFinite(pos.x(), pos.y())) {
1554             continue;
1555         }
1556 
1557         const SkPackedGlyphID packedID{glyphID};
1558         switch (const SkGlyphDigest digest = strike->digestFor(kMask, packedID);
1559                 digest.actionFor(kMask)) {
1560             case GlyphAction::kAccept: {
1561                 const SkPoint mappedPos = creationMatrix.mapPoint(pos);
1562                 const SkGlyphRect glyphBounds = digest.bounds().offset(mappedPos);
1563                 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
1564                 acceptedBuffer[acceptedSize++] =
1565                         std::make_tuple(packedID, glyphBounds.leftTop(), digest.maskFormat());
1566                 break;
1567             }
1568             case GlyphAction::kReject:
1569                 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1570                 break;
1571             default:
1572                 break;
1573         }
1574     }
1575 
1576     return {acceptedBuffer.first(acceptedSize),
1577             rejectedBuffer.first(rejectedSize),
1578             boundingRect.rect()};
1579 }
1580 
1581 std::tuple<SkZip<const SkGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>>
prepare_for_path_drawing(StrikeForGPU * strike,SkZip<const SkGlyphID,const SkPoint> source,SkZip<SkGlyphID,SkPoint> acceptedBuffer,SkZip<SkGlyphID,SkPoint> rejectedBuffer)1582 prepare_for_path_drawing(StrikeForGPU* strike,
1583                          SkZip<const SkGlyphID, const SkPoint> source,
1584                          SkZip<SkGlyphID, SkPoint> acceptedBuffer,
1585                          SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1586     int acceptedSize = 0;
1587     int rejectedSize = 0;
1588     StrikeMutationMonitor m{strike};
1589     for (const auto [glyphID, pos] : source) {
1590         if (!SkIsFinite(pos.x(), pos.y())) {
1591             continue;
1592         }
1593 
1594         switch (strike->digestFor(skglyph::kPath, SkPackedGlyphID{glyphID})
1595                        .actionFor(skglyph::kPath)) {
1596             case GlyphAction::kAccept:
1597                 acceptedBuffer[acceptedSize++] = std::make_tuple(glyphID, pos);
1598                 break;
1599             case GlyphAction::kReject:
1600                 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1601                 break;
1602             default:
1603                 break;
1604         }
1605     }
1606     return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
1607 }
1608 
1609 std::tuple<SkZip<const SkGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>>
prepare_for_drawable_drawing(StrikeForGPU * strike,SkZip<const SkGlyphID,const SkPoint> source,SkZip<SkGlyphID,SkPoint> acceptedBuffer,SkZip<SkGlyphID,SkPoint> rejectedBuffer)1610  prepare_for_drawable_drawing(StrikeForGPU* strike,
1611                              SkZip<const SkGlyphID, const SkPoint> source,
1612                              SkZip<SkGlyphID, SkPoint> acceptedBuffer,
1613                              SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1614     int acceptedSize = 0;
1615     int rejectedSize = 0;
1616     StrikeMutationMonitor m{strike};
1617     for (const auto [glyphID, pos] : source) {
1618         if (!SkIsFinite(pos.x(), pos.y())) {
1619             continue;
1620         }
1621 
1622         switch (strike->digestFor(skglyph::kDrawable, SkPackedGlyphID{glyphID})
1623                        .actionFor(skglyph::kDrawable)) {
1624             case GlyphAction::kAccept:
1625                 acceptedBuffer[acceptedSize++] = std::make_tuple(glyphID, pos);
1626                 break;
1627             case GlyphAction::kReject:
1628                 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1629                 break;
1630             default:
1631                 break;
1632         }
1633     }
1634     return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
1635 }
1636 
1637 #if !defined(SK_DISABLE_SDF_TEXT)
1638 static std::tuple<SkStrikeSpec, SkScalar, sktext::gpu::SDFTMatrixRange>
make_sdft_strike_spec(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const SkMatrix & deviceMatrix,const SkPoint & textLocation,const sktext::gpu::SubRunControl & control)1639 make_sdft_strike_spec(const SkFont& font, const SkPaint& paint,
1640                       const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix,
1641                       const SkPoint& textLocation, const sktext::gpu::SubRunControl& control) {
1642     // Add filter to the paint which creates the SDFT data for A8 masks.
1643     SkPaint dfPaint{paint};
1644     dfPaint.setMaskFilter(sktext::gpu::SDFMaskFilter::Make());
1645 
1646     auto [dfFont, strikeToSourceScale, matrixRange] = control.getSDFFont(font, deviceMatrix,
1647                                                                          textLocation);
1648 
1649     // Adjust the stroke width by the scale factor for drawing the SDFT.
1650     dfPaint.setStrokeWidth(paint.getStrokeWidth() / strikeToSourceScale);
1651 
1652     // Check for dashing and adjust the intervals.
1653     if (SkPathEffect* pathEffect = paint.getPathEffect(); pathEffect != nullptr) {
1654         SkPathEffectBase::DashInfo dashInfo;
1655         if (as_PEB(pathEffect)->asADash(&dashInfo) == SkPathEffectBase::DashType::kDash) {
1656             if (dashInfo.fCount > 0) {
1657                 // Allocate the intervals.
1658                 std::vector<SkScalar> scaledIntervals(dashInfo.fCount);
1659                 dashInfo.fIntervals = scaledIntervals.data();
1660                 // Call again to get the interval data.
1661                 (void)as_PEB(pathEffect)->asADash(&dashInfo);
1662                 for (SkScalar& interval : scaledIntervals) {
1663                     interval /= strikeToSourceScale;
1664                 }
1665                 auto scaledDashes = SkDashPathEffect::Make(scaledIntervals.data(),
1666                                                            scaledIntervals.size(),
1667                                                            dashInfo.fPhase / strikeToSourceScale);
1668                 dfPaint.setPathEffect(scaledDashes);
1669             }
1670         }
1671     }
1672 
1673     // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
1674     // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
1675     SkScalerContextFlags flags = SkScalerContextFlags::kNone;
1676     SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(dfFont, dfPaint, surfaceProps, flags,
1677                                                      SkMatrix::I());
1678 
1679     return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, matrixRange);
1680 }
1681 #endif
1682 
MakeInAlloc(const GlyphRunList & glyphRunList,const SkMatrix & positionMatrix,const SkPaint & runPaint,SkStrikeDeviceInfo strikeDeviceInfo,StrikeForGPUCacheInterface * strikeCache,SubRunAllocator * alloc,SubRunCreationBehavior creationBehavior,const char * tag)1683 SubRunContainerOwner SubRunContainer::MakeInAlloc(
1684         const GlyphRunList& glyphRunList,
1685         const SkMatrix& positionMatrix,
1686         const SkPaint& runPaint,
1687         SkStrikeDeviceInfo strikeDeviceInfo,
1688         StrikeForGPUCacheInterface* strikeCache,
1689         SubRunAllocator* alloc,
1690         SubRunCreationBehavior creationBehavior,
1691         const char* tag) {
1692     SkASSERT(alloc != nullptr);
1693     SkASSERT(strikeDeviceInfo.fSubRunControl != nullptr);
1694 
1695     SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix);
1696     // If there is no SubRunControl description ignore all SubRuns.
1697     if (strikeDeviceInfo.fSubRunControl == nullptr) {
1698         return container;
1699     }
1700 
1701     const SkSurfaceProps deviceProps = strikeDeviceInfo.fSurfaceProps;
1702     const SkScalerContextFlags scalerContextFlags = strikeDeviceInfo.fScalerContextFlags;
1703     const SubRunControl* subRunControl = strikeDeviceInfo.fSubRunControl;
1704 #if !defined(SK_DISABLE_SDF_TEXT)
1705     const SkScalar maxMaskSize = subRunControl->maxSize();
1706 #else
1707     const SkScalar maxMaskSize = 256;
1708 #endif
1709 
1710     // TODO: hoist the buffer structure to the GlyphRunBuilder. The buffer structure here is
1711     //  still begin tuned, and this is expected to be slower until tuned.
1712     const int maxGlyphRunSize = glyphRunList.maxGlyphRunSize();
1713 
1714     // Accepted buffers.
1715     STArray<64, SkPackedGlyphID> acceptedPackedGlyphIDs;
1716     STArray<64, SkGlyphID> acceptedGlyphIDs;
1717     STArray<64, SkPoint> acceptedPositions;
1718     STArray<64, SkMask::Format> acceptedFormats;
1719     acceptedPackedGlyphIDs.resize(maxGlyphRunSize);
1720     acceptedGlyphIDs.resize(maxGlyphRunSize);
1721     acceptedPositions.resize(maxGlyphRunSize);
1722     acceptedFormats.resize(maxGlyphRunSize);
1723 
1724     // Rejected buffers.
1725     STArray<64, SkGlyphID> rejectedGlyphIDs;
1726     STArray<64, SkPoint> rejectedPositions;
1727     rejectedGlyphIDs.resize(maxGlyphRunSize);
1728     rejectedPositions.resize(maxGlyphRunSize);
1729     const auto rejectedBuffer = SkMakeZip(rejectedGlyphIDs, rejectedPositions);
1730 
1731     const SkPoint glyphRunListLocation = glyphRunList.sourceBounds().center();
1732 
1733     // Handle all the runs in the glyphRunList
1734     for (auto& glyphRun : glyphRunList) {
1735         SkZip<const SkGlyphID, const SkPoint> source = glyphRun.source();
1736         const SkFont& runFont = glyphRun.font();
1737 
1738         const SkScalar approximateDeviceTextSize =
1739                 // Since the positionMatrix has the origin prepended, use the plain
1740                 // sourceBounds from above.
1741                 SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix,
1742                                                            glyphRunListLocation);
1743 
1744         // Atlas mask cases - SDFT and direct mask
1745         // Only consider using direct or SDFT drawing if not drawing hairlines and not too big.
1746         if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0) &&
1747                 approximateDeviceTextSize < maxMaskSize) {
1748 
1749 #if !defined(SK_DISABLE_SDF_TEXT)
1750             // SDFT case
1751             if (subRunControl->isSDFT(approximateDeviceTextSize, runPaint, positionMatrix)) {
1752                 // Process SDFT - This should be the .009% case.
1753                 const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
1754                         make_sdft_strike_spec(
1755                                 runFont, runPaint, deviceProps, positionMatrix,
1756                                 glyphRunListLocation, *subRunControl);
1757 
1758                 if (!SkScalarNearlyZero(strikeToSourceScale)) {
1759                     sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1760 
1761                     // The creationMatrix needs to scale the strike data when inverted and
1762                     // multiplied by the positionMatrix. The final CTM should be:
1763                     //   [positionMatrix][scale by strikeToSourceScale],
1764                     // which should equal the following because of the transform during the vertex
1765                     // calculation,
1766                     //   [positionMatrix][creationMatrix]^-1.
1767                     // So, the creation matrix needs to be
1768                     //   [scale by 1/strikeToSourceScale].
1769                     SkMatrix creationMatrix =
1770                             SkMatrix::Scale(1.f/strikeToSourceScale, 1.f/strikeToSourceScale);
1771 
1772                     auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions);
1773                     auto [accepted, rejected, creationBounds] = prepare_for_SDFT_drawing(
1774                             strike.get(), creationMatrix, source, acceptedBuffer, rejectedBuffer);
1775                     source = rejected;
1776 
1777                     if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1778                         container->fSubRuns.append(SDFTSubRun::Make(
1779                                 accepted,
1780                                 runFont,
1781                                 strike->strikePromise(),
1782                                 creationMatrix,
1783                                 creationBounds,
1784                                 matrixRange,
1785                                 alloc));
1786                     }
1787                 }
1788             }
1789 #endif  // !defined(SK_DISABLE_SDF_TEXT)
1790 
1791             // Direct Mask case
1792             // Handle all the directly mapped mask subruns.
1793             if (!source.empty() && !positionMatrix.hasPerspective()) {
1794                 // Process masks including ARGB - this should be the 99.99% case.
1795                 // This will handle medium size emoji that are sharing the run with SDFT drawn text.
1796                 // If things are too big they will be passed along to the drawing of last resort
1797                 // below.
1798                 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
1799                         runFont, runPaint, deviceProps, scalerContextFlags, positionMatrix);
1800 
1801                 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1802 
1803                 auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs,
1804                                                 acceptedPositions,
1805                                                 acceptedFormats);
1806                 auto [accepted, rejected, creationBounds] = prepare_for_direct_mask_drawing(
1807                         strike.get(), positionMatrix, source, acceptedBuffer, rejectedBuffer);
1808                 source = rejected;
1809 
1810                 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1811                     auto addGlyphsWithSameFormat =
1812                         [&, bounds = creationBounds](
1813                                 SkZip<const SkPackedGlyphID, const SkPoint> subrun,
1814                                 MaskFormat format) {
1815                             container->fSubRuns.append(
1816                                     DirectMaskSubRun::Make(bounds,
1817                                                            subrun,
1818                                                            container->initialPosition(),
1819                                                            strike->strikePromise(),
1820                                                            format,
1821                                                            alloc));
1822                         };
1823                     add_multi_mask_format(addGlyphsWithSameFormat, accepted);
1824                 }
1825             }
1826         }
1827 
1828         // Drawable case
1829         // Handle all the drawable glyphs - usually large or perspective color glyphs.
1830         if (!source.empty()) {
1831             auto [strikeSpec, strikeToSourceScale] =
1832                     SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
1833 
1834             if (!SkScalarNearlyZero(strikeToSourceScale)) {
1835                 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1836 
1837                 auto acceptedBuffer = SkMakeZip(acceptedGlyphIDs, acceptedPositions);
1838                 auto [accepted, rejected] =
1839                 prepare_for_drawable_drawing(strike.get(), source, acceptedBuffer, rejectedBuffer);
1840                 source = rejected;
1841 
1842                 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1843                     container->fSubRuns.append(
1844                             DrawableSubRun::Make(
1845                                 accepted,
1846                                 strikeToSourceScale,
1847                                 strike->strikePromise(),
1848                                 alloc));
1849                 }
1850             }
1851         }
1852 
1853         // Path case
1854         // Handle path subruns. Mainly, large or large perspective glyphs with no color.
1855         if (!source.empty()) {
1856             auto [strikeSpec, strikeToSourceScale] =
1857                     SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
1858 
1859             if (!SkScalarNearlyZero(strikeToSourceScale)) {
1860                 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1861 
1862                 auto acceptedBuffer = SkMakeZip(acceptedGlyphIDs, acceptedPositions);
1863                 auto [accepted, rejected] =
1864                 prepare_for_path_drawing(strike.get(), source, acceptedBuffer, rejectedBuffer);
1865                 source = rejected;
1866 
1867                 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1868                     const bool isAntiAliased =
1869                             subRunControl->forcePathAA() || has_some_antialiasing(runFont);
1870                     container->fSubRuns.append(
1871                             PathSubRun::Make(accepted,
1872                                              isAntiAliased,
1873                                              strikeToSourceScale,
1874                                              strike->strikePromise(),
1875                                              alloc));
1876                 }
1877             }
1878         }
1879 
1880         // Drawing of last resort case
1881         // Draw all the rest of the rejected glyphs from above. This scales out of the atlas to
1882         // the screen, so quality will suffer. This mainly handles large color or perspective
1883         // color not handled by Drawables.
1884         if (!source.empty() && !SkScalarNearlyZero(approximateDeviceTextSize)) {
1885             // Creation matrix will be changed below to meet the following criteria:
1886             // * No perspective - the font scaler and the strikes can't handle perspective masks.
1887             // * Fits atlas - creationMatrix will be conditioned so that the maximum glyph
1888             //   dimension for this run will be <  kMaxBilerpAtlasDimension.
1889             SkMatrix creationMatrix = positionMatrix;
1890 
1891             // Condition creationMatrix for perspective.
1892             if (creationMatrix.hasPerspective()) {
1893                 // Find a scale factor that reduces pixelation caused by keystoning.
1894                 SkPoint center = glyphRunList.sourceBounds().center();
1895                 SkScalar maxAreaScale = SkMatrixPriv::DifferentialAreaScale(creationMatrix, center);
1896                 SkScalar perspectiveFactor = 1;
1897                 if (SkIsFinite(maxAreaScale) && !SkScalarNearlyZero(maxAreaScale)) {
1898                     perspectiveFactor = SkScalarSqrt(maxAreaScale);
1899                 }
1900 
1901                 // Masks can not be created in perspective. Create a non-perspective font with a
1902                 // scale that will support the perspective keystoning.
1903                 creationMatrix = SkMatrix::Scale(perspectiveFactor, perspectiveFactor);
1904             }
1905 
1906             // Reduce to make a one pixel border for the bilerp padding.
1907             static const constexpr SkScalar kMaxBilerpAtlasDimension =
1908                     SkGlyphDigest::kSkSideTooBigForAtlas - 2;
1909 
1910             // Get the raw glyph IDs to simulate device drawing to figure the maximum device
1911             // dimension.
1912             const SkSpan<const SkGlyphID> glyphs = get_glyphIDs(source);
1913 
1914             // maxGlyphDimension always returns an integer even though the return type is SkScalar.
1915             auto maxGlyphDimension = [&](const SkMatrix& m) {
1916                 const SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask(
1917                         runFont, runPaint, deviceProps, scalerContextFlags, m);
1918                 const sk_sp<StrikeForGPU> gaugingStrike =
1919                         strikeSpec.findOrCreateScopedStrike(strikeCache);
1920                 const SkScalar maxDimension =
1921                         find_maximum_glyph_dimension(gaugingStrike.get(), glyphs);
1922                 // TODO: There is a problem where a small character (say .) and a large
1923                 //  character (say M) are in the same run. If the run is scaled to be very
1924                 //  large, then the M may return 0 because its dimensions are > 65535, but
1925                 //  the small character produces regular result because its largest dimension
1926                 //  is < 65535. This will create an improper scale factor causing the M to be
1927                 //  too large to fit in the atlas. Tracked by skia:13714.
1928                 return maxDimension;
1929             };
1930 
1931             // Condition the creationMatrix so that glyphs fit in the atlas.
1932             for (SkScalar maxDimension = maxGlyphDimension(creationMatrix);
1933                  kMaxBilerpAtlasDimension < maxDimension;
1934                  maxDimension = maxGlyphDimension(creationMatrix))
1935             {
1936                 // The SkScalerContext has a limit of 65536 maximum dimension.
1937                 // reductionFactor will always be < 1 because
1938                 // maxDimension > kMaxBilerpAtlasDimension, and because maxDimension will always
1939                 // be an integer the reduction factor will always be at most 254 / 255.
1940                 SkScalar reductionFactor = kMaxBilerpAtlasDimension / maxDimension;
1941                 creationMatrix.postScale(reductionFactor, reductionFactor);
1942             }
1943 
1944             // Draw using the creationMatrix.
1945             SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask(
1946                     runFont, runPaint, deviceProps, scalerContextFlags, creationMatrix);
1947 
1948             sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1949 
1950             auto acceptedBuffer =
1951                     SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions, acceptedFormats);
1952             auto [accepted, rejected, creationBounds] =
1953                 prepare_for_mask_drawing(
1954                         strike.get(), creationMatrix, source, acceptedBuffer, rejectedBuffer);
1955             source = rejected;
1956 
1957             if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1958 
1959                 auto addGlyphsWithSameFormat =
1960                         [&, bounds = creationBounds](
1961                                 SkZip<const SkPackedGlyphID, const SkPoint> subrun,
1962                                 MaskFormat format) {
1963                             container->fSubRuns.append(
1964                                     TransformedMaskSubRun::Make(subrun,
1965                                                                 container->initialPosition(),
1966                                                                 strike->strikePromise(),
1967                                                                 creationMatrix,
1968                                                                 bounds,
1969                                                                 format,
1970                                                                 alloc));
1971                         };
1972                 add_multi_mask_format(addGlyphsWithSameFormat, accepted);
1973             }
1974         }
1975     }
1976 
1977     return container;
1978 }
1979 
draw(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint,const SkRefCnt * subRunStorage,const AtlasDrawDelegate & atlasDelegate) const1980 void SubRunContainer::draw(SkCanvas* canvas,
1981                            SkPoint drawOrigin,
1982                            const SkPaint& paint,
1983                            const SkRefCnt* subRunStorage,
1984                            const AtlasDrawDelegate& atlasDelegate) const {
1985     for (auto& subRun : fSubRuns) {
1986         subRun.draw(canvas, drawOrigin, paint, sk_ref_sp(subRunStorage), atlasDelegate);
1987     }
1988 }
1989 
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1990 bool SubRunContainer::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1991     for (const SubRun& subRun : fSubRuns) {
1992         if (!subRun.canReuse(paint, positionMatrix)) {
1993             return false;
1994         }
1995     }
1996     return true;
1997 }
1998 
1999 // Returns the empty span if there is a problem reading the positions.
MakePointsFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc)2000 SkSpan<SkPoint> MakePointsFromBuffer(SkReadBuffer& buffer, SubRunAllocator* alloc) {
2001     uint32_t glyphCount = buffer.getArrayCount();
2002 
2003     // Zero indicates a problem with serialization.
2004     if (!buffer.validate(glyphCount != 0)) { return {}; }
2005 
2006     // Check that the count will not overflow the arena.
2007     if (!buffer.validate(glyphCount <= INT_MAX &&
2008                          BagOfBytes::WillCountFit<SkPoint>(glyphCount))) { return {}; }
2009 
2010     SkPoint* positionsData = alloc->makePODArray<SkPoint>(glyphCount);
2011     if (!buffer.readPointArray(positionsData, glyphCount)) { return {}; }
2012     return {positionsData, glyphCount};
2013 }
2014 
2015 }  // namespace sktext::gpu
2016