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