Coverage Report

Created: 2024-05-20 07:14

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