Coverage Report

Created: 2024-05-20 07:14

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