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