xref: /aosp_15_r20/external/skia/include/core/SkTextBlob.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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     */
bounds()53     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     */
uniqueID()59     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                                             SkTextEncoding encoding = SkTextEncoding::kUTF8) {
117         if (!string) {
118             return nullptr;
119         }
120         return MakeFromText(string, strlen(string), font, encoding);
121     }
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.
notifyAddedToCache(uint32_t cacheID,PurgeDelegate purgeDelegate)264     void notifyAddedToCache(uint32_t cacheID, PurgeDelegate purgeDelegate) const {
265         fCacheID.store(cacheID);
266         fPurgeDelegate.store(purgeDelegate);
267     }
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).
pointsRunBuffer335         SkPoint*    points() const { return reinterpret_cast<SkPoint*>(pos); }
xformsRunBuffer336         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
520