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