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