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