Coverage Report

Created: 2025-06-24 08:20

/src/skia/include/core/SkTextBlob.h
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
#ifndef SkTextBlob_DEFINED
9
#define SkTextBlob_DEFINED
10
11
#include "include/core/SkFont.h"
12
#include "include/core/SkFontTypes.h"
13
#include "include/core/SkPoint.h"
14
#include "include/core/SkRSXform.h"
15
#include "include/core/SkRect.h"
16
#include "include/core/SkRefCnt.h"
17
#include "include/core/SkScalar.h"
18
#include "include/core/SkSpan.h"
19
#include "include/core/SkTypes.h"
20
#include "include/private/base/SkDebug.h"
21
#include "include/private/base/SkTemplates.h"
22
23
#include <atomic>
24
#include <cstdint>
25
#include <cstring>
26
27
class SkData;
28
class SkPaint;
29
class SkTypeface;
30
struct SkDeserialProcs;
31
struct SkSerialProcs;
32
33
namespace sktext {
34
class GlyphRunList;
35
}
36
37
/** \class SkTextBlob
38
    SkTextBlob combines multiple text runs into an immutable container. Each text
39
    run consists of glyphs, SkPaint, and position. Only parts of SkPaint related to
40
    fonts and text rendering are used by run.
41
*/
42
class SK_API SkTextBlob final : public SkNVRefCnt<SkTextBlob> {
43
private:
44
    class RunRecord;
45
46
public:
47
48
    /** Returns conservative bounding box. Uses SkPaint associated with each glyph to
49
        determine glyph bounds, and unions all bounds. Returned bounds may be
50
        larger than the bounds of all glyphs in runs.
51
52
        @return  conservative bounding box
53
    */
54
748k
    const SkRect& bounds() const { return fBounds; }
55
56
    /** Returns a non-zero value unique among all text blobs.
57
58
        @return  identifier for SkTextBlob
59
    */
60
7.49k
    uint32_t uniqueID() const { return fUniqueID; }
61
62
    /** Returns the number of intervals that intersect bounds.
63
        bounds describes a pair of lines parallel to the text advance.
64
        The return count is zero or a multiple of two, and is at most twice the number of glyphs in
65
        the the blob.
66
67
        Pass nullptr for intervals to determine the size of the interval array.
68
69
        Runs within the blob that contain SkRSXform are ignored when computing intercepts.
70
71
        @param bounds     lower and upper line parallel to the advance
72
        @param intervals  returned intersections; may be nullptr
73
        @param paint      specifies stroking, SkPathEffect that affects the result; may be nullptr
74
        @return           number of intersections; may be zero
75
     */
76
    int getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
77
                      const SkPaint* paint = nullptr) const;
78
79
    /** Creates SkTextBlob with a single run.
80
81
        font contains attributes used to define the run text.
82
83
        When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or
84
        SkTextEncoding::kUTF32, this function uses the default
85
        character-to-glyph mapping from the SkTypeface in font.  It does not
86
        perform typeface fallback for characters not found in the SkTypeface.
87
        It does not perform kerning or other complex shaping; glyphs are
88
        positioned based on their default advances.
89
90
        @param text        character code points or glyphs drawn
91
        @param byteLength  byte length of text array
92
        @param font        text size, typeface, text scale, and so on, used to draw
93
        @param encoding    text encoding used in the text array
94
        @return            SkTextBlob constructed from one run
95
    */
96
    static sk_sp<SkTextBlob> MakeFromText(const void* text, size_t byteLength, const SkFont& font,
97
                                          SkTextEncoding encoding = SkTextEncoding::kUTF8);
98
99
    /** Creates SkTextBlob with a single run. string meaning depends on SkTextEncoding;
100
        by default, string is encoded as UTF-8.
101
102
        font contains attributes used to define the run text.
103
104
        When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or
105
        SkTextEncoding::kUTF32, this function uses the default
106
        character-to-glyph mapping from the SkTypeface in font.  It does not
107
        perform typeface fallback for characters not found in the SkTypeface.
108
        It does not perform kerning or other complex shaping; glyphs are
109
        positioned based on their default advances.
110
111
        @param string   character code points or glyphs drawn
112
        @param font     text size, typeface, text scale, and so on, used to draw
113
        @param encoding text encoding used in the text array
114
        @return         SkTextBlob constructed from one run
115
    */
116
    static sk_sp<SkTextBlob> MakeFromString(const char* string, const SkFont& font,
117
0
                                            SkTextEncoding encoding = SkTextEncoding::kUTF8) {
118
0
        if (!string) {
119
0
            return nullptr;
120
0
        }
121
0
        return MakeFromText(string, strlen(string), font, encoding);
122
0
    }
123
124
    /** Returns a textblob built from a single run of text with x-positions and a single y value.
125
        This is equivalent to using SkTextBlobBuilder and calling allocRunPosH().
126
        Returns nullptr if byteLength is zero.
127
128
        @param text        character code points or glyphs drawn (based on encoding)
129
        @param byteLength  byte length of text array
130
        @param xpos    array of x-positions, must contain values for all of the character points.
131
        @param constY  shared y-position for each character point, to be paired with each xpos.
132
        @param font    SkFont used for this run
133
        @param encoding specifies the encoding of the text array.
134
        @return        new textblob or nullptr
135
     */
136
    static sk_sp<SkTextBlob> MakeFromPosTextH(const void* text, size_t byteLength,
137
                                              SkSpan<const SkScalar> xpos, SkScalar constY,
138
                                              const SkFont& font,
139
                                              SkTextEncoding encoding = SkTextEncoding::kUTF8);
140
141
    /** Returns a textblob built from a single run of text with positions.
142
        This is equivalent to using SkTextBlobBuilder and calling allocRunPos().
143
        Returns nullptr if byteLength is zero.
144
145
        @param text        character code points or glyphs drawn (based on encoding)
146
        @param byteLength  byte length of text array
147
        @param pos     array of positions, must contain values for all of the character points.
148
        @param font    SkFont used for this run
149
        @param encoding specifies the encoding of the text array.
150
        @return        new textblob or nullptr
151
     */
152
    static sk_sp<SkTextBlob> MakeFromPosText(const void* text, size_t byteLength,
153
                                             SkSpan<const SkPoint> pos, const SkFont& font,
154
                                             SkTextEncoding encoding = SkTextEncoding::kUTF8);
155
156
    static sk_sp<SkTextBlob> MakeFromRSXform(const void* text, size_t byteLength,
157
                                             SkSpan<const SkRSXform> xform, const SkFont& font,
158
                                             SkTextEncoding encoding = SkTextEncoding::kUTF8);
159
160
    // Helpers for glyphs
161
162
    static sk_sp<SkTextBlob> MakeFromPosHGlyphs(SkSpan<const SkGlyphID> glyphs,
163
                                                SkSpan<const SkScalar> xpos, SkScalar constY,
164
0
                                                const SkFont& font) {
165
0
        return MakeFromPosTextH(glyphs.data(), glyphs.size() * sizeof(SkGlyphID), xpos, constY,
166
0
                                font, SkTextEncoding::kGlyphID);
167
0
    }
168
    static sk_sp<SkTextBlob> MakeFromPosGlyphs(SkSpan<const SkGlyphID> glyphs,
169
0
                                               SkSpan<const SkPoint> pos, const SkFont& font) {
170
0
        return MakeFromPosText(glyphs.data(), glyphs.size() * sizeof(SkGlyphID), pos, font,
171
0
                               SkTextEncoding::kGlyphID);
172
0
    }
173
    static sk_sp<SkTextBlob> MakeFromRSXformGlyphs(SkSpan<const SkGlyphID> glyphs,
174
                                                   SkSpan<const SkRSXform> xform,
175
0
                                                   const SkFont& font) {
176
0
        return MakeFromRSXform(glyphs.data(), glyphs.size() * sizeof(SkGlyphID), xform, font,
177
0
                               SkTextEncoding::kGlyphID);
178
0
    }
179
180
    /** Writes data to allow later reconstruction of SkTextBlob. memory points to storage
181
        to receive the encoded data, and memory_size describes the size of storage.
182
        Returns bytes used if provided storage is large enough to hold all data;
183
        otherwise, returns zero.
184
185
        procs.fTypefaceProc permits supplying a custom function to encode SkTypeface.
186
        If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx
187
        may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc
188
        is called with a pointer to SkTypeface and user context.
189
190
        @param procs       custom serial data encoders; may be nullptr
191
        @param memory      storage for data
192
        @param memory_size size of storage
193
        @return            bytes written, or zero if required storage is larger than memory_size
194
195
        example: https://fiddle.skia.org/c/@TextBlob_serialize
196
    */
197
    size_t serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const;
198
199
    /** Returns storage containing SkData describing SkTextBlob, using optional custom
200
        encoders.
201
202
        procs.fTypefaceProc permits supplying a custom function to encode SkTypeface.
203
        If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx
204
        may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc
205
        is called with a pointer to SkTypeface and user context.
206
207
        @param procs  custom serial data encoders; may be nullptr
208
        @return       storage containing serialized SkTextBlob
209
210
        example: https://fiddle.skia.org/c/@TextBlob_serialize_2
211
    */
212
    sk_sp<SkData> serialize(const SkSerialProcs& procs) const;
213
214
    /** Recreates SkTextBlob that was serialized into data. Returns constructed SkTextBlob
215
        if successful; otherwise, returns nullptr. Fails if size is smaller than
216
        required data length, or if data does not permit constructing valid SkTextBlob.
217
218
        procs.fTypefaceProc permits supplying a custom function to decode SkTypeface.
219
        If procs.fTypefaceProc is nullptr, default decoding is used. procs.fTypefaceCtx
220
        may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc
221
        is called with a pointer to SkTypeface data, data byte length, and user context.
222
223
        @param data   pointer for serial data
224
        @param size   size of data
225
        @param procs  custom serial data decoders; may be nullptr
226
        @return       SkTextBlob constructed from data in memory
227
    */
228
    static sk_sp<SkTextBlob> Deserialize(const void* data, size_t size,
229
                                         const SkDeserialProcs& procs);
230
231
    class SK_API Iter {
232
    public:
233
        struct Run {
234
            SkTypeface* fTypeface;
235
            int fGlyphCount;
236
            const SkGlyphID* fGlyphIndices;
237
#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED
238
            const uint32_t* fClusterIndex_forTest;
239
            int fUtf8Size_forTest;
240
            const char* fUtf8_forTest;
241
#endif
242
        };
243
244
        Iter(const SkTextBlob&);
245
246
        /**
247
         * Returns true for each "run" inside the textblob, setting the Run fields (if not null).
248
         * If this returns false, there are no more runs, and the Run parameter will be ignored.
249
         */
250
        bool next(Run*);
251
252
        // Experimental, DO NO USE, will change/go-away
253
        struct ExperimentalRun {
254
            SkFont font;
255
            int count;
256
            const SkGlyphID* glyphs;
257
            const SkPoint* positions;
258
        };
259
        bool experimentalNext(ExperimentalRun*);
260
261
    private:
262
        const RunRecord* fRunRecord;
263
    };
264
265
#ifdef SK_SUPPORT_UNSPANNED_APIS
266
    static sk_sp<SkTextBlob> MakeFromPosTextH(const void* text, size_t byteLength,
267
                                              const SkScalar xpos[], SkScalar constY,
268
                                              const SkFont& font,
269
                                              SkTextEncoding encoding = SkTextEncoding::kUTF8) {
270
        const size_t worstCaseCount = byteLength;
271
        return MakeFromPosTextH(text, byteLength, {xpos, worstCaseCount}, constY, font, encoding);
272
    }
273
    static sk_sp<SkTextBlob> MakeFromPosText(const void* text, size_t byteLength,
274
                                             const SkPoint pos[], const SkFont& font,
275
                                             SkTextEncoding encoding = SkTextEncoding::kUTF8) {
276
        const size_t worstCaseCount = byteLength;
277
        return MakeFromPosText(text, byteLength, {pos, worstCaseCount}, font, encoding);
278
    }
279
    static sk_sp<SkTextBlob> MakeFromRSXform(const void* text, size_t byteLength,
280
                                              const SkRSXform xform[], const SkFont& font,
281
                                              SkTextEncoding encoding = SkTextEncoding::kUTF8) {
282
        const size_t worstCaseCount = byteLength;
283
        return MakeFromRSXform(text, byteLength, {xform, worstCaseCount}, font, encoding);
284
    }
285
#endif
286
287
private:
288
    friend class SkNVRefCnt<SkTextBlob>;
289
290
    enum GlyphPositioning : uint8_t;
291
292
    explicit SkTextBlob(const SkRect& bounds);
293
294
    ~SkTextBlob();
295
296
    // Memory for objects of this class is created with sk_malloc rather than operator new and must
297
    // be freed with sk_free.
298
    void operator delete(void* p);
299
    void* operator new(size_t);
300
    void* operator new(size_t, void* p);
301
302
    static unsigned ScalarsPerGlyph(GlyphPositioning pos);
303
304
    using PurgeDelegate = void (*)(uint32_t blobID, uint32_t cacheID);
305
306
    // Call when this blob is part of the key to a cache entry. This allows the cache
307
    // to know automatically those entries can be purged when this SkTextBlob is deleted.
308
2.90k
    void notifyAddedToCache(uint32_t cacheID, PurgeDelegate purgeDelegate) const {
309
2.90k
        fCacheID.store(cacheID);
310
2.90k
        fPurgeDelegate.store(purgeDelegate);
311
2.90k
    }
312
313
    friend class sktext::GlyphRunList;
314
    friend class SkTextBlobBuilder;
315
    friend class SkTextBlobPriv;
316
    friend class SkTextBlobRunIterator;
317
318
    const SkRect                  fBounds;
319
    const uint32_t                fUniqueID;
320
    mutable std::atomic<uint32_t> fCacheID;
321
    mutable std::atomic<PurgeDelegate> fPurgeDelegate;
322
323
    SkDEBUGCODE(size_t fStorageSize;)
324
325
    // The actual payload resides in externally-managed storage, following the object.
326
    // (see the .cpp for more details)
327
328
    using INHERITED = SkRefCnt;
329
};
330
331
/** \class SkTextBlobBuilder
332
    Helper class for constructing SkTextBlob.
333
*/
334
class SK_API SkTextBlobBuilder {
335
public:
336
337
    /** Constructs empty SkTextBlobBuilder. By default, SkTextBlobBuilder has no runs.
338
339
        @return  empty SkTextBlobBuilder
340
341
        example: https://fiddle.skia.org/c/@TextBlobBuilder_empty_constructor
342
    */
343
    SkTextBlobBuilder();
344
345
    /** Deletes data allocated internally by SkTextBlobBuilder.
346
    */
347
    ~SkTextBlobBuilder();
348
349
    /** Returns SkTextBlob built from runs of glyphs added by builder. Returned
350
        SkTextBlob is immutable; it may be copied, but its contents may not be altered.
351
        Returns nullptr if no runs of glyphs were added by builder.
352
353
        Resets SkTextBlobBuilder to its initial empty state, allowing it to be
354
        reused to build a new set of runs.
355
356
        @return  SkTextBlob or nullptr
357
358
        example: https://fiddle.skia.org/c/@TextBlobBuilder_make
359
    */
360
    sk_sp<SkTextBlob> make();
361
362
    /** \struct SkTextBlobBuilder::RunBuffer
363
        RunBuffer supplies storage for glyphs and positions within a run.
364
365
        A run is a sequence of glyphs sharing font metrics and positioning.
366
        Each run may position its glyphs in one of three ways:
367
        by specifying where the first glyph is drawn, and allowing font metrics to
368
        determine the advance to subsequent glyphs; by specifying a baseline, and
369
        the position on that baseline for each glyph in run; or by providing SkPoint
370
        array, one per glyph.
371
    */
372
    struct RunBuffer {
373
        SkGlyphID* glyphs;   //!< storage for glyph indexes in run
374
        SkScalar*  pos;      //!< storage for glyph positions in run
375
        char*      utf8text; //!< storage for text UTF-8 code units in run
376
        uint32_t*  clusters; //!< storage for glyph clusters (index of UTF-8 code unit)
377
378
        // Helpers, since the "pos" field can be different types (always some number of floats).
379
9.00k
        SkPoint*    points() const { return reinterpret_cast<SkPoint*>(pos); }
380
4.40M
        SkRSXform*  xforms() const { return reinterpret_cast<SkRSXform*>(pos); }
381
    };
382
383
    /** Returns run with storage for glyphs. Caller must write count glyphs to
384
        RunBuffer::glyphs before next call to SkTextBlobBuilder.
385
386
        RunBuffer::pos, RunBuffer::utf8text, and RunBuffer::clusters should be ignored.
387
388
        Glyphs share metrics in font.
389
390
        Glyphs are positioned on a baseline at (x, y), using font metrics to
391
        determine their relative placement.
392
393
        bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
394
        bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
395
        is computed from (x, y) and RunBuffer::glyphs metrics.
396
397
        @param font    SkFont used for this run
398
        @param count   number of glyphs
399
        @param x       horizontal offset within the blob
400
        @param y       vertical offset within the blob
401
        @param bounds  optional run bounding box
402
        @return writable glyph buffer
403
    */
404
    const RunBuffer& allocRun(const SkFont& font, int count, SkScalar x, SkScalar y,
405
                              const SkRect* bounds = nullptr);
406
407
    /** Returns run with storage for glyphs and positions along baseline. Caller must
408
        write count glyphs to RunBuffer::glyphs and count scalars to RunBuffer::pos
409
        before next call to SkTextBlobBuilder.
410
411
        RunBuffer::utf8text and RunBuffer::clusters should be ignored.
412
413
        Glyphs share metrics in font.
414
415
        Glyphs are positioned on a baseline at y, using x-axis positions written by
416
        caller to RunBuffer::pos.
417
418
        bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
419
        bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
420
        is computed from y, RunBuffer::pos, and RunBuffer::glyphs metrics.
421
422
        @param font    SkFont used for this run
423
        @param count   number of glyphs
424
        @param y       vertical offset within the blob
425
        @param bounds  optional run bounding box
426
        @return writable glyph buffer and x-axis position buffer
427
    */
428
    const RunBuffer& allocRunPosH(const SkFont& font, int count, SkScalar y,
429
                                  const SkRect* bounds = nullptr);
430
431
    /** Returns run with storage for glyphs and SkPoint positions. Caller must
432
        write count glyphs to RunBuffer::glyphs and count SkPoint to RunBuffer::pos
433
        before next call to SkTextBlobBuilder.
434
435
        RunBuffer::utf8text and RunBuffer::clusters should be ignored.
436
437
        Glyphs share metrics in font.
438
439
        Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using
440
        two scalar values for each SkPoint.
441
442
        bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
443
        bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
444
        is computed from RunBuffer::pos, and RunBuffer::glyphs metrics.
445
446
        @param font    SkFont used for this run
447
        @param count   number of glyphs
448
        @param bounds  optional run bounding box
449
        @return writable glyph buffer and SkPoint buffer
450
    */
451
    const RunBuffer& allocRunPos(const SkFont& font, int count,
452
                                 const SkRect* bounds = nullptr);
453
454
    // RunBuffer.pos points to SkRSXform array
455
    const RunBuffer& allocRunRSXform(const SkFont& font, int count);
456
457
    /** Returns run with storage for glyphs, text, and clusters. Caller must
458
        write count glyphs to RunBuffer::glyphs, textByteCount UTF-8 code units
459
        into RunBuffer::utf8text, and count monotonic indexes into utf8text
460
        into RunBuffer::clusters before next call to SkTextBlobBuilder.
461
462
        RunBuffer::pos should be ignored.
463
464
        Glyphs share metrics in font.
465
466
        Glyphs are positioned on a baseline at (x, y), using font metrics to
467
        determine their relative placement.
468
469
        bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
470
        bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
471
        is computed from (x, y) and RunBuffer::glyphs metrics.
472
473
        @param font          SkFont used for this run
474
        @param count         number of glyphs
475
        @param x             horizontal offset within the blob
476
        @param y             vertical offset within the blob
477
        @param textByteCount number of UTF-8 code units
478
        @param bounds        optional run bounding box
479
        @return writable glyph buffer, text buffer, and cluster buffer
480
    */
481
    const RunBuffer& allocRunText(const SkFont& font, int count, SkScalar x, SkScalar y,
482
                                  int textByteCount, const SkRect* bounds = nullptr);
483
484
    /** Returns run with storage for glyphs, positions along baseline, text,
485
        and clusters. Caller must write count glyphs to RunBuffer::glyphs,
486
        count scalars to RunBuffer::pos, textByteCount UTF-8 code units into
487
        RunBuffer::utf8text, and count monotonic indexes into utf8text into
488
        RunBuffer::clusters before next call to SkTextBlobBuilder.
489
490
        Glyphs share metrics in font.
491
492
        Glyphs are positioned on a baseline at y, using x-axis positions written by
493
        caller to RunBuffer::pos.
494
495
        bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
496
        bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
497
        is computed from y, RunBuffer::pos, and RunBuffer::glyphs metrics.
498
499
        @param font          SkFont used for this run
500
        @param count         number of glyphs
501
        @param y             vertical offset within the blob
502
        @param textByteCount number of UTF-8 code units
503
        @param bounds        optional run bounding box
504
        @return writable glyph buffer, x-axis position buffer, text buffer, and cluster buffer
505
    */
506
    const RunBuffer& allocRunTextPosH(const SkFont& font, int count, SkScalar y, int textByteCount,
507
                                      const SkRect* bounds = nullptr);
508
509
    /** Returns run with storage for glyphs, SkPoint positions, text, and
510
        clusters. Caller must write count glyphs to RunBuffer::glyphs, count
511
        SkPoint to RunBuffer::pos, textByteCount UTF-8 code units into
512
        RunBuffer::utf8text, and count monotonic indexes into utf8text into
513
        RunBuffer::clusters before next call to SkTextBlobBuilder.
514
515
        Glyphs share metrics in font.
516
517
        Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using
518
        two scalar values for each SkPoint.
519
520
        bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
521
        bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
522
        is computed from RunBuffer::pos, and RunBuffer::glyphs metrics.
523
524
        @param font          SkFont used for this run
525
        @param count         number of glyphs
526
        @param textByteCount number of UTF-8 code units
527
        @param bounds        optional run bounding box
528
        @return writable glyph buffer, SkPoint buffer, text buffer, and cluster buffer
529
    */
530
    const RunBuffer& allocRunTextPos(const SkFont& font, int count, int textByteCount,
531
                                     const SkRect* bounds = nullptr);
532
533
    // RunBuffer.pos points to SkRSXform array
534
    const RunBuffer& allocRunTextRSXform(const SkFont& font, int count, int textByteCount,
535
                                         const SkRect* bounds = nullptr);
536
537
private:
538
    void reserve(size_t size);
539
    void allocInternal(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
540
                       int count, int textBytes, SkPoint offset, const SkRect* bounds);
541
    bool mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
542
                  uint32_t count, SkPoint offset);
543
    void updateDeferredBounds();
544
545
    static SkRect ConservativeRunBounds(const SkTextBlob::RunRecord&);
546
    static SkRect TightRunBounds(const SkTextBlob::RunRecord&);
547
548
    friend class SkTextBlobPriv;
549
    friend class SkTextBlobBuilderPriv;
550
551
    skia_private::AutoTMalloc<uint8_t> fStorage;
552
    size_t                 fStorageSize;
553
    size_t                 fStorageUsed;
554
555
    SkRect                 fBounds;
556
    int                    fRunCount;
557
    bool                   fDeferredBounds;
558
    size_t                 fLastRun; // index into fStorage
559
560
    RunBuffer              fCurrentRunBuffer;
561
};
562
563
#endif // SkTextBlob_DEFINED