Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/core/SkTextBlob.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2014 Google Inc.
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 "include/core/SkTextBlob.h"
9
10
#include "include/core/SkData.h"
11
#include "include/core/SkMaskFilter.h"
12
#include "include/core/SkMatrix.h"
13
#include "include/core/SkPaint.h"
14
#include "include/core/SkPathEffect.h"
15
#include "include/core/SkPoint.h"
16
#include "include/core/SkRSXform.h"
17
#include "include/private/base/SkAlign.h"
18
#include "include/private/base/SkFloatingPoint.h"
19
#include "include/private/base/SkMalloc.h"
20
#include "include/private/base/SkSpan_impl.h"
21
#include "include/private/base/SkTo.h"
22
#include "src/base/SkSafeMath.h"
23
#include "src/base/SkTLazy.h"
24
#include "src/core/SkFontPriv.h"
25
#include "src/core/SkGlyph.h"
26
#include "src/core/SkReadBuffer.h"
27
#include "src/core/SkStrikeSpec.h"
28
#include "src/core/SkTextBlobPriv.h"
29
#include "src/core/SkWriteBuffer.h"
30
#include "src/text/GlyphRun.h"
31
32
#include <algorithm>
33
#include <atomic>
34
#include <limits>
35
#include <new>
36
#include <vector>
37
38
using namespace skia_private;
39
40
namespace {
41
struct RunFontStorageEquivalent {
42
    SkScalar fSize, fScaleX;
43
    void*    fTypeface;
44
    SkScalar fSkewX;
45
    uint32_t fFlags;
46
};
47
static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
48
}  // namespace
49
50
size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
51
                                          SkTextBlob::GlyphPositioning positioning,
52
126k
                                          SkSafeMath* safe) {
53
126k
    static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
54
55
126k
    auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
56
126k
            posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
57
58
    // RunRecord object + (aligned) glyph buffer + position buffer
59
126k
    auto size = sizeof(SkTextBlob::RunRecord);
60
126k
    size = safe->add(size, safe->alignUp(glyphSize, 4));
61
126k
    size = safe->add(size, posSize);
62
63
126k
    if (textSize) {  // Extended run.
64
868
        size = safe->add(size, sizeof(uint32_t));
65
868
        size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
66
868
        size = safe->add(size, textSize);
67
868
    }
68
69
126k
    return safe->alignUp(size, sizeof(void*));
70
126k
}
71
72
36.0k
const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) {
73
    // The first record (if present) is stored following the blob object.
74
    // (aligned up to make the RunRecord aligned too)
75
36.0k
    return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
76
36.0k
}
77
78
115k
const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
79
115k
    return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
80
115k
}
81
82
namespace {
83
struct RunRecordStorageEquivalent {
84
    SkFont   fFont;
85
    SkPoint  fOffset;
86
    uint32_t fCount;
87
    uint32_t fFlags;
88
    SkDEBUGCODE(unsigned fMagic;)
89
};
90
}  // namespace
91
92
34.3k
void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
93
34.3k
    SkASSERT(kRunRecordMagic == fMagic);
94
34.3k
    SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop);
95
96
34.3k
    SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
97
34.3k
    SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
98
34.3k
             <= (const SkScalar*)NextUnchecked(this));
99
34.3k
    if (isExtended()) {
100
344
        SkASSERT(textSize() > 0);
101
344
        SkASSERT(textSizePtr() < (const uint32_t*)NextUnchecked(this));
102
344
        SkASSERT(clusterBuffer() < (const uint32_t*)NextUnchecked(this));
103
344
        SkASSERT(textBuffer() + textSize() <= (const char*)NextUnchecked(this));
104
344
    }
105
34.3k
    static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
106
34.3k
                  "runrecord_should_stay_packed");
107
34.3k
}
SkTextBlob::RunRecord::validate(unsigned char const*) const
Line
Count
Source
92
34.3k
void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
93
34.3k
    SkASSERT(kRunRecordMagic == fMagic);
94
34.3k
    SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop);
95
96
34.3k
    SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
97
34.3k
    SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
98
34.3k
             <= (const SkScalar*)NextUnchecked(this));
99
34.3k
    if (isExtended()) {
100
344
        SkASSERT(textSize() > 0);
101
344
        SkASSERT(textSizePtr() < (const uint32_t*)NextUnchecked(this));
102
344
        SkASSERT(clusterBuffer() < (const uint32_t*)NextUnchecked(this));
103
344
        SkASSERT(textBuffer() + textSize() <= (const char*)NextUnchecked(this));
104
344
    }
105
34.3k
    static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
106
34.3k
                  "runrecord_should_stay_packed");
107
34.3k
}
Unexecuted instantiation: SkTextBlob::RunRecord::validate(unsigned char const*) const
108
109
89.4k
const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) {
110
89.4k
    SkSafeMath safe;
111
89.4k
    auto res = reinterpret_cast<const RunRecord*>(
112
89.4k
            reinterpret_cast<const uint8_t*>(run)
113
89.4k
            + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
114
89.4k
    SkASSERT(safe);
115
89.4k
    return res;
116
89.4k
}
117
118
size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
119
                                       SkTextBlob::GlyphPositioning positioning,
120
128k
                                       SkSafeMath* safe) {
121
128k
    return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
122
128k
}
123
124
1.88k
uint32_t* SkTextBlob::RunRecord::textSizePtr() const {
125
    // textSize follows the position buffer.
126
1.88k
    SkASSERT(isExtended());
127
1.88k
    SkSafeMath safe;
128
1.88k
    auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
129
1.88k
    SkASSERT(safe);
130
1.88k
    return res;
131
1.88k
}
132
133
2.69k
void SkTextBlob::RunRecord::grow(uint32_t count) {
134
2.69k
    SkScalar* initialPosBuffer = posBuffer();
135
2.69k
    uint32_t initialCount = fCount;
136
2.69k
    fCount += count;
137
138
    // Move the initial pos scalars to their new location.
139
2.69k
    size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
140
2.69k
    SkASSERT((uint8_t*)posBuffer() + copySize <= (const uint8_t*)NextUnchecked(this));
141
142
    // memmove, as the buffers may overlap
143
2.69k
    memmove(posBuffer(), initialPosBuffer, copySize);
144
2.69k
}
145
146
9.81k
static uint32_t next_id() {
147
9.81k
    static std::atomic<uint32_t> nextID{1};
148
9.81k
    uint32_t id;
149
9.81k
    do {
150
9.81k
        id = nextID.fetch_add(1, std::memory_order_relaxed);
151
9.81k
    } while (id == SK_InvalidGenID);
152
9.81k
    return id;
153
9.81k
}
154
155
SkTextBlob::SkTextBlob(const SkRect& bounds)
156
    : fBounds(bounds)
157
    , fUniqueID(next_id())
158
    , fCacheID(SK_InvalidUniqueID)
159
9.81k
    , fPurgeDelegate(nullptr) {}
160
161
9.81k
SkTextBlob::~SkTextBlob() {
162
9.81k
    if (SK_InvalidUniqueID != fCacheID.load()) {
163
782
        PurgeDelegate f = fPurgeDelegate.load();
164
782
        SkASSERT(f);
165
782
        f(fUniqueID, fCacheID);
166
782
    }
167
168
9.81k
    const auto* run = RunRecord::First(this);
169
31.6k
    do {
170
31.6k
        const auto* nextRun = RunRecord::Next(run);
171
31.6k
        SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
172
31.6k
        run->~RunRecord();
173
31.6k
        run = nextRun;
174
31.6k
    } while (run);
175
9.81k
}
176
177
namespace {
178
179
union PositioningAndExtended {
180
    int32_t intValue;
181
    struct {
182
        uint8_t  positioning;
183
        uint8_t  extended;
184
        uint16_t padding;
185
    };
186
};
187
188
static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
189
190
} // namespace
191
192
enum SkTextBlob::GlyphPositioning : uint8_t {
193
    kDefault_Positioning      = 0, // Default glyph advances -- zero scalars per glyph.
194
    kHorizontal_Positioning   = 1, // Horizontal positioning -- one scalar per glyph.
195
    kFull_Positioning         = 2, // Point positioning -- two scalars per glyph.
196
    kRSXform_Positioning      = 3, // RSXform positioning -- four scalars per glyph.
197
};
198
199
144k
unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
200
144k
    const uint8_t gScalarsPerPositioning[] = {
201
144k
        0,  // kDefault_Positioning
202
144k
        1,  // kHorizontal_Positioning
203
144k
        2,  // kFull_Positioning
204
144k
        4,  // kRSXform_Positioning
205
144k
    };
206
144k
    SkASSERT((unsigned)pos <= 3);
207
144k
    return gScalarsPerPositioning[pos];
208
144k
}
209
210
9.81k
void SkTextBlob::operator delete(void* p) {
211
9.81k
    sk_free(p);
212
9.81k
}
213
214
0
void* SkTextBlob::operator new(size_t) {
215
0
    SK_ABORT("All blobs are created by placement new.");
216
0
}
217
218
9.81k
void* SkTextBlob::operator new(size_t, void* p) {
219
9.81k
    return p;
220
9.81k
}
221
222
SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
223
32.4k
    : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
224
32.4k
    SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;)
225
32.4k
}
SkTextBlobRunIterator::SkTextBlobRunIterator(SkTextBlob const*)
Line
Count
Source
223
16.2k
    : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
224
16.2k
    SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;)
225
16.2k
}
SkTextBlobRunIterator::SkTextBlobRunIterator(SkTextBlob const*)
Line
Count
Source
223
16.2k
    : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
224
16.2k
    SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;)
225
16.2k
}
226
227
56.4k
void SkTextBlobRunIterator::next() {
228
56.4k
    SkASSERT(!this->done());
229
230
56.4k
    if (!this->done()) {
231
56.4k
        SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
232
56.4k
        fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
233
56.4k
    }
234
56.4k
}
235
236
84.4k
SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const {
237
84.4k
    SkASSERT(!this->done());
238
84.4k
    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) ==
239
84.4k
                  kDefault_Positioning, "");
240
84.4k
    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) ==
241
84.4k
                  kHorizontal_Positioning, "");
242
84.4k
    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) ==
243
84.4k
                  kFull_Positioning, "");
244
84.4k
    static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) ==
245
84.4k
                  kRSXform_Positioning, "");
246
247
84.4k
    return SkTo<GlyphPositioning>(fCurrentRun->positioning());
248
84.4k
}
249
250
0
unsigned SkTextBlobRunIterator::scalarsPerGlyph() const {
251
0
    return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning());
252
0
}
253
254
0
bool SkTextBlobRunIterator::isLCD() const {
255
0
    return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias;
256
0
}
257
258
SkTextBlobBuilder::SkTextBlobBuilder()
259
    : fStorageSize(0)
260
    , fStorageUsed(0)
261
    , fRunCount(0)
262
    , fDeferredBounds(false)
263
13.3k
    , fLastRun(0) {
264
13.3k
    fBounds.setEmpty();
265
13.3k
}
266
267
13.3k
SkTextBlobBuilder::~SkTextBlobBuilder() {
268
13.3k
    if (nullptr != fStorage.get()) {
269
        // We are abandoning runs and must destruct the associated font data.
270
        // The easiest way to accomplish that is to use the blob destructor.
271
509
        this->make();
272
509
    }
273
13.3k
}
274
275
19.5k
static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) {
276
19.5k
    return SkMatrix().setRSXform(xform).mapRect(rect);
277
19.5k
}
278
279
15.4k
SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
280
15.4k
    const SkFont& font = run.font();
281
15.4k
    SkRect bounds;
282
283
15.4k
    if (SkTextBlob::kDefault_Positioning == run.positioning()) {
284
12.5k
        font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
285
12.5k
                         SkTextEncoding::kGlyphID, &bounds);
286
12.5k
        return bounds.makeOffset(run.offset().x(), run.offset().y());
287
12.5k
    }
288
289
2.89k
    AutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
290
2.89k
    font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
291
292
2.89k
    if (SkTextBlob::kRSXform_Positioning == run.positioning()) {
293
0
        bounds.setEmpty();
294
0
        const SkRSXform* xform = run.xformBuffer();
295
0
        SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
296
0
        for (unsigned i = 0; i < run.glyphCount(); ++i) {
297
0
            bounds.join(map_quad_to_rect(xform[i], glyphBounds[i]));
298
0
        }
299
2.89k
    } else {
300
2.89k
        SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
301
2.89k
                 SkTextBlob::kHorizontal_Positioning == run.positioning());
302
        // kFull_Positioning       => [ x, y, x, y... ]
303
        // kHorizontal_Positioning => [ x, x, x... ]
304
        //                            (const y applied by runBounds.offset(run->offset()) later)
305
2.89k
        const SkScalar horizontalConstY = 0;
306
2.89k
        const SkScalar* glyphPosX = run.posBuffer();
307
2.89k
        const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
308
2.89k
                                                        glyphPosX + 1 : &horizontalConstY;
309
2.89k
        const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
310
2.89k
        const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
311
2.89k
                                                    posXInc : 0;
312
313
2.89k
        bounds.setEmpty();
314
18.8k
        for (unsigned i = 0; i < run.glyphCount(); ++i) {
315
15.9k
            bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
316
15.9k
            glyphPosX += posXInc;
317
15.9k
            glyphPosY += posYInc;
318
15.9k
        }
319
320
2.89k
        SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
321
2.89k
    }
322
2.89k
    return bounds.makeOffset(run.offset().x(), run.offset().y());
323
15.4k
}
SkTextBlobBuilder::TightRunBounds(SkTextBlob::RunRecord const&)
Line
Count
Source
279
15.4k
SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
280
15.4k
    const SkFont& font = run.font();
281
15.4k
    SkRect bounds;
282
283
15.4k
    if (SkTextBlob::kDefault_Positioning == run.positioning()) {
284
12.5k
        font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
285
12.5k
                         SkTextEncoding::kGlyphID, &bounds);
286
12.5k
        return bounds.makeOffset(run.offset().x(), run.offset().y());
287
12.5k
    }
288
289
2.89k
    AutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
290
2.89k
    font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
291
292
2.89k
    if (SkTextBlob::kRSXform_Positioning == run.positioning()) {
293
0
        bounds.setEmpty();
294
0
        const SkRSXform* xform = run.xformBuffer();
295
0
        SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
296
0
        for (unsigned i = 0; i < run.glyphCount(); ++i) {
297
0
            bounds.join(map_quad_to_rect(xform[i], glyphBounds[i]));
298
0
        }
299
2.89k
    } else {
300
2.89k
        SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
301
2.89k
                 SkTextBlob::kHorizontal_Positioning == run.positioning());
302
        // kFull_Positioning       => [ x, y, x, y... ]
303
        // kHorizontal_Positioning => [ x, x, x... ]
304
        //                            (const y applied by runBounds.offset(run->offset()) later)
305
2.89k
        const SkScalar horizontalConstY = 0;
306
2.89k
        const SkScalar* glyphPosX = run.posBuffer();
307
2.89k
        const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
308
2.89k
                                                        glyphPosX + 1 : &horizontalConstY;
309
2.89k
        const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
310
2.89k
        const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
311
2.89k
                                                    posXInc : 0;
312
313
2.89k
        bounds.setEmpty();
314
18.8k
        for (unsigned i = 0; i < run.glyphCount(); ++i) {
315
15.9k
            bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
316
15.9k
            glyphPosX += posXInc;
317
15.9k
            glyphPosY += posYInc;
318
15.9k
        }
319
320
2.89k
        SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
321
2.89k
    }
322
2.89k
    return bounds.makeOffset(run.offset().x(), run.offset().y());
323
15.4k
}
Unexecuted instantiation: SkTextBlobBuilder::TightRunBounds(SkTextBlob::RunRecord const&)
324
325
12.4k
SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
326
12.4k
    SkASSERT(run.glyphCount() > 0);
327
12.4k
    SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
328
12.4k
             SkTextBlob::kHorizontal_Positioning == run.positioning() ||
329
12.4k
             SkTextBlob::kRSXform_Positioning == run.positioning());
330
331
12.4k
    const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font());
332
12.4k
    if (fontBounds.isEmpty()) {
333
        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
334
        // producing useful results in this case.
335
2.89k
        return TightRunBounds(run);
336
2.89k
    }
337
338
    // Compute the glyph position bbox.
339
9.60k
    SkRect bounds;
340
9.60k
    switch (run.positioning()) {
341
1.67k
    case SkTextBlob::kHorizontal_Positioning: {
342
1.67k
        const SkScalar* glyphPos = run.posBuffer();
343
1.67k
        SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
344
345
1.67k
        SkScalar minX = *glyphPos;
346
1.67k
        SkScalar maxX = *glyphPos;
347
33.1k
        for (unsigned i = 1; i < run.glyphCount(); ++i) {
348
31.4k
            SkScalar x = glyphPos[i];
349
31.4k
            minX = std::min(x, minX);
350
31.4k
            maxX = std::max(x, maxX);
351
31.4k
        }
352
353
1.67k
        bounds.setLTRB(minX, 0, maxX, 0);
354
1.67k
    } break;
355
7.56k
    case SkTextBlob::kFull_Positioning: {
356
7.56k
        const SkPoint* glyphPosPts = run.pointBuffer();
357
7.56k
        SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
358
359
7.56k
        bounds.setBounds(glyphPosPts, run.glyphCount());
360
7.56k
    } break;
361
366
    case SkTextBlob::kRSXform_Positioning: {
362
366
        const SkRSXform* xform = run.xformBuffer();
363
366
        SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
364
366
        bounds.setEmpty();
365
19.9k
        for (unsigned i = 0; i < run.glyphCount(); ++i) {
366
19.5k
            bounds.join(map_quad_to_rect(xform[i], fontBounds));
367
19.5k
        }
368
366
    } break;
369
0
    default:
370
0
        SK_ABORT("unsupported positioning mode");
371
9.60k
    }
372
373
9.60k
    if (run.positioning() != SkTextBlob::kRSXform_Positioning) {
374
        // Expand by typeface glyph bounds.
375
9.23k
        bounds.fLeft   += fontBounds.left();
376
9.23k
        bounds.fTop    += fontBounds.top();
377
9.23k
        bounds.fRight  += fontBounds.right();
378
9.23k
        bounds.fBottom += fontBounds.bottom();
379
9.23k
    }
380
381
    // Offset by run position.
382
9.60k
    return bounds.makeOffset(run.offset().x(), run.offset().y());
383
9.60k
}
384
385
41.4k
void SkTextBlobBuilder::updateDeferredBounds() {
386
41.4k
    SkASSERT(!fDeferredBounds || fRunCount > 0);
387
388
41.4k
    if (!fDeferredBounds) {
389
16.3k
        return;
390
16.3k
    }
391
392
25.0k
    SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
393
25.0k
    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
394
25.0k
                                                                          fLastRun);
395
396
    // FIXME: we should also use conservative bounds for kDefault_Positioning.
397
25.0k
    SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
398
12.5k
                       TightRunBounds(*run) : ConservativeRunBounds(*run);
399
25.0k
    fBounds.join(runBounds);
400
25.0k
    fDeferredBounds = false;
401
25.0k
}
402
403
34.3k
void SkTextBlobBuilder::reserve(size_t size) {
404
34.3k
    SkSafeMath safe;
405
406
    // We don't currently pre-allocate, but maybe someday...
407
34.3k
    if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
408
36
        return;
409
36
    }
410
411
34.3k
    if (0 == fRunCount) {
412
9.81k
        SkASSERT(nullptr == fStorage.get());
413
9.81k
        SkASSERT(0 == fStorageSize);
414
9.81k
        SkASSERT(0 == fStorageUsed);
415
416
        // the first allocation also includes blob storage
417
        // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
418
9.81k
        fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
419
9.81k
    }
420
421
34.3k
    fStorageSize = safe.add(fStorageUsed, size);
422
423
    // FYI: This relies on everything we store being relocatable, particularly SkPaint.
424
    //      Also, this is counting on the underlying realloc to throw when passed max().
425
34.3k
    fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
426
34.3k
}
SkTextBlobBuilder::reserve(unsigned long)
Line
Count
Source
403
34.3k
void SkTextBlobBuilder::reserve(size_t size) {
404
34.3k
    SkSafeMath safe;
405
406
    // We don't currently pre-allocate, but maybe someday...
407
34.3k
    if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
408
36
        return;
409
36
    }
410
411
34.3k
    if (0 == fRunCount) {
412
9.81k
        SkASSERT(nullptr == fStorage.get());
413
9.81k
        SkASSERT(0 == fStorageSize);
414
9.81k
        SkASSERT(0 == fStorageUsed);
415
416
        // the first allocation also includes blob storage
417
        // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
418
9.81k
        fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
419
9.81k
    }
420
421
34.3k
    fStorageSize = safe.add(fStorageUsed, size);
422
423
    // FYI: This relies on everything we store being relocatable, particularly SkPaint.
424
    //      Also, this is counting on the underlying realloc to throw when passed max().
425
34.3k
    fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
426
34.3k
}
Unexecuted instantiation: SkTextBlobBuilder::reserve(unsigned long)
427
428
bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
429
34.0k
                                 uint32_t count, SkPoint offset) {
430
34.0k
    if (0 == fLastRun) {
431
9.70k
        SkASSERT(0 == fRunCount);
432
9.70k
        return false;
433
9.70k
    }
434
435
24.3k
    SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
436
24.3k
    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
437
24.3k
                                                                          fLastRun);
438
24.3k
    SkASSERT(run->glyphCount() > 0);
439
440
24.3k
    if (run->textSize() != 0) {
441
44
        return false;
442
44
    }
443
444
24.2k
    if (run->positioning() != positioning
445
24.2k
        || run->font() != font
446
24.2k
        || (run->glyphCount() + count < run->glyphCount())) {
447
14.3k
        return false;
448
14.3k
    }
449
450
    // we can merge same-font/same-positioning runs in the following cases:
451
    //   * fully positioned run following another fully positioned run
452
    //   * horizontally postioned run following another horizontally positioned run with the same
453
    //     y-offset
454
9.87k
    if (SkTextBlob::kFull_Positioning != positioning
455
9.87k
        && (SkTextBlob::kHorizontal_Positioning != positioning
456
7.50k
            || run->offset().y() != offset.y())) {
457
7.18k
        return false;
458
7.18k
    }
459
460
2.69k
    SkSafeMath safe;
461
2.69k
    size_t sizeDelta =
462
2.69k
        SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
463
2.69k
        SkTextBlob::RunRecord::StorageSize(run->glyphCount()        , 0, positioning, &safe);
464
2.69k
    if (!safe) {
465
0
        return false;
466
0
    }
467
468
2.69k
    this->reserve(sizeDelta);
469
470
    // reserve may have realloced
471
2.69k
    run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
472
2.69k
    uint32_t preMergeCount = run->glyphCount();
473
2.69k
    run->grow(count);
474
475
    // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
476
2.69k
    fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
477
2.69k
    fCurrentRunBuffer.pos = run->posBuffer()
478
2.69k
                          + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
479
480
2.69k
    fStorageUsed += sizeDelta;
481
482
2.69k
    SkASSERT(fStorageUsed <= fStorageSize);
483
2.69k
    run->validate(fStorage.get() + fStorageUsed);
484
485
2.69k
    return true;
486
2.69k
}
SkTextBlobBuilder::mergeRun(SkFont const&, SkTextBlob::GlyphPositioning, unsigned int, SkPoint)
Line
Count
Source
429
34.0k
                                 uint32_t count, SkPoint offset) {
430
34.0k
    if (0 == fLastRun) {
431
9.70k
        SkASSERT(0 == fRunCount);
432
9.70k
        return false;
433
9.70k
    }
434
435
24.3k
    SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
436
24.3k
    SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
437
24.3k
                                                                          fLastRun);
438
24.3k
    SkASSERT(run->glyphCount() > 0);
439
440
24.3k
    if (run->textSize() != 0) {
441
44
        return false;
442
44
    }
443
444
24.2k
    if (run->positioning() != positioning
445
24.2k
        || run->font() != font
446
24.2k
        || (run->glyphCount() + count < run->glyphCount())) {
447
14.3k
        return false;
448
14.3k
    }
449
450
    // we can merge same-font/same-positioning runs in the following cases:
451
    //   * fully positioned run following another fully positioned run
452
    //   * horizontally postioned run following another horizontally positioned run with the same
453
    //     y-offset
454
9.87k
    if (SkTextBlob::kFull_Positioning != positioning
455
9.87k
        && (SkTextBlob::kHorizontal_Positioning != positioning
456
7.50k
            || run->offset().y() != offset.y())) {
457
7.18k
        return false;
458
7.18k
    }
459
460
2.69k
    SkSafeMath safe;
461
2.69k
    size_t sizeDelta =
462
2.69k
        SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
463
2.69k
        SkTextBlob::RunRecord::StorageSize(run->glyphCount()        , 0, positioning, &safe);
464
2.69k
    if (!safe) {
465
0
        return false;
466
0
    }
467
468
2.69k
    this->reserve(sizeDelta);
469
470
    // reserve may have realloced
471
2.69k
    run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
472
2.69k
    uint32_t preMergeCount = run->glyphCount();
473
2.69k
    run->grow(count);
474
475
    // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
476
2.69k
    fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
477
2.69k
    fCurrentRunBuffer.pos = run->posBuffer()
478
2.69k
                          + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
479
480
2.69k
    fStorageUsed += sizeDelta;
481
482
2.69k
    SkASSERT(fStorageUsed <= fStorageSize);
483
2.69k
    run->validate(fStorage.get() + fStorageUsed);
484
485
2.69k
    return true;
486
2.69k
}
Unexecuted instantiation: SkTextBlobBuilder::mergeRun(SkFont const&, SkTextBlob::GlyphPositioning, unsigned int, SkPoint)
487
488
void SkTextBlobBuilder::allocInternal(const SkFont& font,
489
                                      SkTextBlob::GlyphPositioning positioning,
490
                                      int count, int textSize, SkPoint offset,
491
34.3k
                                      const SkRect* bounds) {
492
34.3k
    if (count <= 0 || textSize < 0) {
493
0
        fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
494
0
        return;
495
0
    }
496
497
34.3k
    if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
498
31.6k
        this->updateDeferredBounds();
499
500
31.6k
        SkSafeMath safe;
501
31.6k
        size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
502
31.6k
        if (!safe) {
503
0
            fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
504
0
            return;
505
0
        }
506
507
31.6k
        this->reserve(runSize);
508
509
31.6k
        SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob)));
510
31.6k
        SkASSERT(fStorageUsed + runSize <= fStorageSize);
511
512
31.6k
        SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
513
31.6k
            SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
514
31.6k
        fCurrentRunBuffer.glyphs = run->glyphBuffer();
515
31.6k
        fCurrentRunBuffer.pos = run->posBuffer();
516
31.6k
        fCurrentRunBuffer.utf8text = run->textBuffer();
517
31.6k
        fCurrentRunBuffer.clusters = run->clusterBuffer();
518
519
31.6k
        fLastRun = fStorageUsed;
520
31.6k
        fStorageUsed += runSize;
521
31.6k
        fRunCount++;
522
523
31.6k
        SkASSERT(fStorageUsed <= fStorageSize);
524
31.6k
        run->validate(fStorage.get() + fStorageUsed);
525
31.6k
    }
526
34.3k
    SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
527
34.3k
    SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
528
34.3k
    if (!fDeferredBounds) {
529
31.8k
        if (bounds) {
530
6.78k
            fBounds.join(*bounds);
531
25.0k
        } else {
532
25.0k
            fDeferredBounds = true;
533
25.0k
        }
534
31.8k
    }
535
34.3k
}
536
537
// SkFont versions
538
539
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count,
540
                                                                SkScalar x, SkScalar y,
541
12.5k
                                                                const SkRect* bounds) {
542
12.5k
    this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds);
543
12.5k
    return fCurrentRunBuffer;
544
12.5k
}
545
546
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count,
547
                                                                    SkScalar y,
548
1.83k
                                                                    const SkRect* bounds) {
549
1.83k
    this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds);
550
1.83k
    return fCurrentRunBuffer;
551
1.83k
}
552
553
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count,
554
12.7k
                                                                   const SkRect* bounds) {
555
12.7k
    this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds);
556
12.7k
    return fCurrentRunBuffer;
557
12.7k
}
558
559
const SkTextBlobBuilder::RunBuffer&
560
366
SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) {
561
366
    this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr);
562
366
    return fCurrentRunBuffer;
563
366
}
564
565
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count,
566
                                                                    SkScalar x, SkScalar y,
567
                                                                    int textByteCount,
568
5.17k
                                                                    const SkRect* bounds) {
569
5.17k
    this->allocInternal(font,
570
5.17k
                        SkTextBlob::kDefault_Positioning,
571
5.17k
                        count,
572
5.17k
                        textByteCount,
573
5.17k
                        SkPoint::Make(x, y),
574
5.17k
                        bounds);
575
5.17k
    return fCurrentRunBuffer;
576
5.17k
}
577
578
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font,
579
                                                                        int count,
580
                                                                        SkScalar y,
581
                                                                        int textByteCount,
582
505
                                                                        const SkRect* bounds) {
583
505
    this->allocInternal(font,
584
505
                        SkTextBlob::kHorizontal_Positioning,
585
505
                        count,
586
505
                        textByteCount,
587
505
                        SkPoint::Make(0, y),
588
505
                        bounds);
589
505
    return fCurrentRunBuffer;
590
505
}
591
592
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font,
593
                                                                       int count,
594
                                                                       int textByteCount,
595
143
                                                                       const SkRect *bounds) {
596
143
    this->allocInternal(font,
597
143
                        SkTextBlob::kFull_Positioning,
598
143
                        count, textByteCount,
599
143
                        SkPoint::Make(0, 0),
600
143
                        bounds);
601
143
    return fCurrentRunBuffer;
602
143
}
603
604
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextRSXform(const SkFont& font,
605
                                                                           int count,
606
                                                                           int textByteCount,
607
967
                                                                           const SkRect *bounds) {
608
967
    this->allocInternal(font,
609
967
                        SkTextBlob::kRSXform_Positioning,
610
967
                        count,
611
967
                        textByteCount,
612
967
                        {0, 0},
613
967
                        bounds);
614
967
    return fCurrentRunBuffer;
615
967
}
616
617
9.84k
sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
618
9.84k
    if (!fRunCount) {
619
        // We don't instantiate empty blobs.
620
36
        SkASSERT(!fStorage.get());
621
36
        SkASSERT(fStorageUsed == 0);
622
36
        SkASSERT(fStorageSize == 0);
623
36
        SkASSERT(fLastRun == 0);
624
36
        SkASSERT(fBounds.isEmpty());
625
36
        return nullptr;
626
36
    }
627
628
9.81k
    this->updateDeferredBounds();
629
630
    // Tag the last run as such.
631
9.81k
    auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
632
9.81k
    lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
633
634
9.81k
    SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
635
9.81k
    SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
636
637
9.81k
    SkDEBUGCODE(
638
9.81k
        SkSafeMath safe;
639
9.81k
        size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
640
9.81k
        for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
641
9.81k
             run = SkTextBlob::RunRecord::Next(run)) {
642
9.81k
            validateSize += SkTextBlob::RunRecord::StorageSize(
643
9.81k
                    run->fCount, run->textSize(), run->positioning(), &safe);
644
9.81k
            run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
645
9.81k
            fRunCount--;
646
9.81k
        }
647
9.81k
        SkASSERT(validateSize == fStorageUsed);
648
9.81k
        SkASSERT(fRunCount == 0);
649
9.81k
        SkASSERT(safe);
650
9.81k
    )
651
652
9.81k
    fStorageUsed = 0;
653
9.81k
    fStorageSize = 0;
654
9.81k
    fRunCount = 0;
655
9.81k
    fLastRun = 0;
656
9.81k
    fBounds.setEmpty();
657
658
9.81k
    return sk_sp<SkTextBlob>(blob);
659
9.84k
}
SkTextBlobBuilder::make()
Line
Count
Source
617
9.84k
sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
618
9.84k
    if (!fRunCount) {
619
        // We don't instantiate empty blobs.
620
36
        SkASSERT(!fStorage.get());
621
36
        SkASSERT(fStorageUsed == 0);
622
36
        SkASSERT(fStorageSize == 0);
623
36
        SkASSERT(fLastRun == 0);
624
36
        SkASSERT(fBounds.isEmpty());
625
36
        return nullptr;
626
36
    }
627
628
9.81k
    this->updateDeferredBounds();
629
630
    // Tag the last run as such.
631
9.81k
    auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
632
9.81k
    lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
633
634
9.81k
    SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
635
9.81k
    SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
636
637
9.81k
    SkDEBUGCODE(
638
9.81k
        SkSafeMath safe;
639
9.81k
        size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
640
9.81k
        for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
641
9.81k
             run = SkTextBlob::RunRecord::Next(run)) {
642
9.81k
            validateSize += SkTextBlob::RunRecord::StorageSize(
643
9.81k
                    run->fCount, run->textSize(), run->positioning(), &safe);
644
9.81k
            run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
645
9.81k
            fRunCount--;
646
9.81k
        }
647
9.81k
        SkASSERT(validateSize == fStorageUsed);
648
9.81k
        SkASSERT(fRunCount == 0);
649
9.81k
        SkASSERT(safe);
650
9.81k
    )
651
652
9.81k
    fStorageUsed = 0;
653
9.81k
    fStorageSize = 0;
654
9.81k
    fRunCount = 0;
655
9.81k
    fLastRun = 0;
656
9.81k
    fBounds.setEmpty();
657
658
9.81k
    return sk_sp<SkTextBlob>(blob);
659
9.84k
}
Unexecuted instantiation: SkTextBlobBuilder::make()
660
661
///////////////////////////////////////////////////////////////////////////////////////////////////
662
663
0
void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) {
664
    // seems like we could skip this, and just recompute bounds in unflatten, but
665
    // some cc_unittests fail if we remove this...
666
0
    buffer.writeRect(blob.bounds());
667
668
0
    SkTextBlobRunIterator it(&blob);
669
0
    while (!it.done()) {
670
0
        SkASSERT(it.glyphCount() > 0);
671
672
0
        buffer.write32(it.glyphCount());
673
0
        PositioningAndExtended pe;
674
0
        pe.intValue = 0;
675
0
        pe.positioning = it.positioning();
676
0
        SkASSERT((int32_t)it.positioning() == pe.intValue);  // backwards compat.
677
678
0
        uint32_t textSize = it.textSize();
679
0
        pe.extended = textSize > 0;
680
0
        buffer.write32(pe.intValue);
681
0
        if (pe.extended) {
682
0
            buffer.write32(textSize);
683
0
        }
684
0
        buffer.writePoint(it.offset());
685
686
0
        SkFontPriv::Flatten(it.font(), buffer);
687
688
0
        buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
689
0
        buffer.writeByteArray(it.pos(),
690
0
                              it.glyphCount() * sizeof(SkScalar) *
691
0
                              SkTextBlob::ScalarsPerGlyph(
692
0
                                  SkTo<SkTextBlob::GlyphPositioning>(it.positioning())));
693
0
        if (pe.extended) {
694
0
            buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
695
0
            buffer.writeByteArray(it.text(), it.textSize());
696
0
        }
697
698
0
        it.next();
699
0
    }
700
701
    // Marker for the last run (0 is not a valid glyph count).
702
0
    buffer.write32(0);
703
0
}
Unexecuted instantiation: SkTextBlobPriv::Flatten(SkTextBlob const&, SkWriteBuffer&)
Unexecuted instantiation: SkTextBlobPriv::Flatten(SkTextBlob const&, SkWriteBuffer&)
704
705
2.30k
sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) {
706
2.30k
    SkRect bounds;
707
2.30k
    reader.readRect(&bounds);
708
709
2.30k
    SkTextBlobBuilder blobBuilder;
710
2.30k
    SkSafeMath safe;
711
8.70k
    for (;;) {
712
8.70k
        int glyphCount = reader.read32();
713
8.70k
        if (glyphCount == 0) {
714
            // End-of-runs marker.
715
1.16k
            break;
716
1.16k
        }
717
718
7.54k
        PositioningAndExtended pe;
719
7.54k
        pe.intValue = reader.read32();
720
7.54k
        const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning);
721
7.54k
        if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) {
722
78
            return nullptr;
723
78
        }
724
7.46k
        int textSize = pe.extended ? reader.read32() : 0;
725
7.46k
        if (textSize < 0) {
726
80
            return nullptr;
727
80
        }
728
729
7.38k
        SkPoint offset;
730
7.38k
        reader.readPoint(&offset);
731
7.38k
        SkFont font;
732
7.38k
        SkFontPriv::Unflatten(&font, reader);
733
734
        // Compute the expected size of the buffer and ensure we have enough to deserialize
735
        // a run before allocating it.
736
7.38k
        const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)),
737
7.38k
                     posSize =
738
7.38k
                             safe.mul(glyphCount, safe.mul(sizeof(SkScalar),
739
7.38k
                             SkTextBlob::ScalarsPerGlyph(pos))),
740
7.38k
                     clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0;
741
7.38k
        const size_t totalSize =
742
7.38k
                safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize));
743
744
7.38k
        if (!reader.isValid() || !safe || totalSize > reader.available()) {
745
595
            return nullptr;
746
595
        }
747
748
6.78k
        const SkTextBlobBuilder::RunBuffer* buf = nullptr;
749
6.78k
        switch (pos) {
750
5.17k
            case SkTextBlob::kDefault_Positioning:
751
5.17k
                buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
752
5.17k
                                                textSize, &bounds);
753
5.17k
                break;
754
505
            case SkTextBlob::kHorizontal_Positioning:
755
505
                buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
756
505
                                                    textSize, &bounds);
757
505
                break;
758
143
            case SkTextBlob::kFull_Positioning:
759
143
                buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, &bounds);
760
143
                break;
761
967
            case SkTextBlob::kRSXform_Positioning:
762
967
                buf = &blobBuilder.allocRunTextRSXform(font, glyphCount, textSize, &bounds);
763
967
                break;
764
6.78k
        }
765
766
6.78k
        if (!buf->glyphs ||
767
6.78k
            !buf->pos ||
768
6.78k
            (pe.extended && (!buf->clusters || !buf->utf8text))) {
769
3
            return nullptr;
770
3
        }
771
772
6.78k
        if (!reader.readByteArray(buf->glyphs, glyphSize) ||
773
6.78k
            !reader.readByteArray(buf->pos, posSize)) {
774
362
            return nullptr;
775
362
            }
776
777
6.42k
        if (pe.extended) {
778
326
            if (!reader.readByteArray(buf->clusters, clusterSize) ||
779
326
                !reader.readByteArray(buf->utf8text, textSize)) {
780
21
                return nullptr;
781
21
            }
782
326
        }
783
6.42k
    }
784
785
1.16k
    return blobBuilder.make();
786
2.30k
}
787
788
sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font,
789
0
                                           SkTextEncoding encoding) {
790
    // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the
791
    // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now.
792
0
    const int count = font.countText(text, byteLength, encoding);
793
0
    if (count < 1) {
794
0
        return nullptr;
795
0
    }
796
0
    SkTextBlobBuilder builder;
797
0
    auto buffer = builder.allocRunPos(font, count);
798
0
    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
799
0
    font.getPos(buffer.glyphs, count, buffer.points(), {0, 0});
800
0
    return builder.make();
801
0
}
802
803
sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength,
804
                                              const SkPoint pos[], const SkFont& font,
805
2.20k
                                              SkTextEncoding encoding) {
806
2.20k
    const int count = font.countText(text, byteLength, encoding);
807
2.20k
    if (count < 1) {
808
0
        return nullptr;
809
0
    }
810
2.20k
    SkTextBlobBuilder builder;
811
2.20k
    auto buffer = builder.allocRunPos(font, count);
812
2.20k
    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
813
2.20k
    memcpy(buffer.points(), pos, count * sizeof(SkPoint));
814
2.20k
    return builder.make();
815
2.20k
}
816
817
sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength,
818
                                               const SkScalar xpos[], SkScalar constY,
819
0
                                               const SkFont& font, SkTextEncoding encoding) {
820
0
    const int count = font.countText(text, byteLength, encoding);
821
0
    if (count < 1) {
822
0
        return nullptr;
823
0
    }
824
0
    SkTextBlobBuilder builder;
825
0
    auto buffer = builder.allocRunPosH(font, count, constY);
826
0
    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
827
0
    memcpy(buffer.pos, xpos, count * sizeof(SkScalar));
828
0
    return builder.make();
829
0
}
830
831
sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength,
832
                                              const SkRSXform xform[], const SkFont& font,
833
0
                                              SkTextEncoding encoding) {
834
0
    const int count = font.countText(text, byteLength, encoding);
835
0
    if (count < 1) {
836
0
        return nullptr;
837
0
    }
838
0
    SkTextBlobBuilder builder;
839
0
    auto buffer = builder.allocRunRSXform(font, count);
840
0
    font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
841
0
    memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform));
842
0
    return builder.make();
843
0
}
844
845
0
sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
846
0
    SkBinaryWriteBuffer buffer(procs);
847
0
    SkTextBlobPriv::Flatten(*this, buffer);
848
849
0
    size_t total = buffer.bytesWritten();
850
0
    sk_sp<SkData> data = SkData::MakeUninitialized(total);
851
0
    buffer.writeToMemory(data->writable_data());
852
0
    return data;
853
0
}
854
855
sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
856
0
                                          const SkDeserialProcs& procs) {
857
0
    SkReadBuffer buffer(data, length);
858
0
    buffer.setDeserialProcs(procs);
859
0
    return SkTextBlobPriv::MakeFromBuffer(buffer);
860
0
}
861
862
///////////////////////////////////////////////////////////////////////////////////////////////////
863
864
0
size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const {
865
0
    SkBinaryWriteBuffer buffer(memory, memory_size, procs);
866
0
    SkTextBlobPriv::Flatten(*this, buffer);
867
0
    return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u;
868
0
}
869
870
///////////////////////////////////////////////////////////////////////////////////////////////////
871
872
namespace {
873
int get_glyph_run_intercepts(const sktext::GlyphRun& glyphRun,
874
                             const SkPaint& paint,
875
                             const SkScalar bounds[2],
876
                             SkScalar intervals[],
877
0
                             int* intervalCount) {
878
0
    SkScalar scale = SK_Scalar1;
879
0
    SkPaint interceptPaint{paint};
880
0
    SkFont interceptFont{glyphRun.font()};
881
882
0
    interceptPaint.setMaskFilter(nullptr);   // don't want this affecting our path-cache lookup
883
884
    // can't use our canonical size if we need to apply path effects
885
0
    if (interceptPaint.getPathEffect() == nullptr) {
886
        // If the wrong size is going to be used, don't hint anything.
887
0
        interceptFont.setHinting(SkFontHinting::kNone);
888
0
        interceptFont.setSubpixel(true);
889
0
        scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
890
0
        interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
891
        // Note: fScale can be zero here (even if it wasn't before the divide). It can also
892
        // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
893
        // since downstream we will check for the resulting coordinates being non-finite anyway.
894
        // Thus we don't need to check for zero here.
895
0
        if (interceptPaint.getStrokeWidth() > 0
896
0
            && interceptPaint.getStyle() != SkPaint::kFill_Style) {
897
0
            interceptPaint.setStrokeWidth(
898
0
                    sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
899
0
        }
900
0
    }
901
902
0
    interceptPaint.setStyle(SkPaint::kFill_Style);
903
0
    interceptPaint.setPathEffect(nullptr);
904
905
0
    SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
906
0
    SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
907
908
0
    const SkPoint* posCursor = glyphRun.positions().begin();
909
0
    for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
910
0
        SkPoint pos = *posCursor++;
911
912
0
        if (glyph->path() != nullptr) {
913
            // The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
914
            // Also ensure the bounds are properly offset by the vertical positioning of the glyph.
915
0
            SkScalar scaledBounds[2] = {
916
0
                (bounds[0] - pos.y()) / scale,
917
0
                (bounds[1] - pos.y()) / scale
918
0
            };
919
0
            metricsAndPaths.findIntercepts(
920
0
                    scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
921
0
        }
922
0
    }
923
0
    return *intervalCount;
924
0
}
925
}  // namespace
926
927
int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
928
0
                              const SkPaint* paint) const {
929
0
    SkTLazy<SkPaint> defaultPaint;
930
0
    if (paint == nullptr) {
931
0
        defaultPaint.init();
932
0
        paint = defaultPaint.get();
933
0
    }
934
935
0
    sktext::GlyphRunBuilder builder;
936
0
    auto glyphRunList = builder.blobToGlyphRunList(*this, {0, 0});
937
938
0
    int intervalCount = 0;
939
0
    for (const sktext::GlyphRun& glyphRun : glyphRunList) {
940
        // Ignore RSXForm runs.
941
0
        if (glyphRun.scaledRotations().empty()) {
942
0
            intervalCount = get_glyph_run_intercepts(
943
0
                glyphRun, *paint, bounds, intervals, &intervalCount);
944
0
        }
945
0
    }
946
947
0
    return intervalCount;
948
0
}
949
950
std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count,
951
                                            const SkPoint positions[],
952
                                            SkScalar top, SkScalar bottom,
953
0
                                            const SkPaint* paintPtr) const {
954
0
    if (count <= 0) {
955
0
        return std::vector<SkScalar>();
956
0
    }
957
958
0
    const SkPaint paint(paintPtr ? *paintPtr : SkPaint());
959
0
    const SkScalar bounds[] = {top, bottom};
960
0
    const sktext::GlyphRun run(*this,
961
0
                         {positions, size_t(count)}, {glyphs, size_t(count)},
962
0
                         {nullptr, 0}, {nullptr, 0}, {nullptr, 0});
963
964
0
    std::vector<SkScalar> result;
965
0
    result.resize(count * 2);   // worst case allocation
966
0
    int intervalCount = 0;
967
0
    intervalCount = get_glyph_run_intercepts(run, paint, bounds, result.data(), &intervalCount);
968
0
    result.resize(intervalCount);
969
0
    return result;
970
0
}
971
972
////////
973
974
9.94k
SkTextBlob::Iter::Iter(const SkTextBlob& blob) {
975
9.94k
    fRunRecord = RunRecord::First(&blob);
976
9.94k
}
977
978
47.3k
bool SkTextBlob::Iter::next(Run* rec) {
979
47.3k
    if (fRunRecord) {
980
37.3k
        if (rec) {
981
37.3k
            rec->fTypeface = fRunRecord->font().getTypeface();
982
37.3k
            rec->fGlyphCount = fRunRecord->glyphCount();
983
37.3k
            rec->fGlyphIndices = fRunRecord->glyphBuffer();
984
#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED
985
            rec->fClusterIndex_forTest = fRunRecord->clusterBuffer();
986
            rec->fUtf8Size_forTest = fRunRecord->textSize();
987
            rec->fUtf8_forTest = fRunRecord->textBuffer();
988
#endif
989
37.3k
        }
990
37.3k
        if (fRunRecord->isLastRun()) {
991
9.94k
            fRunRecord = nullptr;
992
27.4k
        } else {
993
27.4k
            fRunRecord = RunRecord::Next(fRunRecord);
994
27.4k
        }
995
37.3k
        return true;
996
37.3k
    }
997
9.94k
    return false;
998
47.3k
}
999
1000
0
bool SkTextBlob::Iter::experimentalNext(ExperimentalRun* rec) {
1001
0
    if (fRunRecord) {
1002
0
        if (rec) {
1003
0
            rec->font = fRunRecord->font();
1004
0
            rec->count = fRunRecord->glyphCount();
1005
0
            rec->glyphs = fRunRecord->glyphBuffer();
1006
0
            rec->positions = fRunRecord->pointBuffer();
1007
0
        }
1008
0
        if (fRunRecord->isLastRun()) {
1009
0
            fRunRecord = nullptr;
1010
0
        } else {
1011
0
            fRunRecord = RunRecord::Next(fRunRecord);
1012
0
        }
1013
0
        return true;
1014
0
    }
1015
0
    return false;
1016
0
}