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     size_t serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const;
161 
162     /** Returns storage containing SkData describing SkTextBlob, using optional custom
163         encoders.
164 
165         procs.fTypefaceProc permits supplying a custom function to encode SkTypeface.
166         If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx
167         may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc
168         is called with a pointer to SkTypeface and user context.
169 
170         @param procs  custom serial data encoders; may be nullptr
171         @return       storage containing serialized SkTextBlob
172     */
173     sk_sp<SkData> serialize(const SkSerialProcs& procs) const;
174 
175     /** Recreates SkTextBlob that was serialized into data. Returns constructed SkTextBlob
176         if successful; otherwise, returns nullptr. Fails if size is smaller than
177         required data length, or if data does not permit constructing valid SkTextBlob.
178 
179         procs.fTypefaceProc permits supplying a custom function to decode SkTypeface.
180         If procs.fTypefaceProc is nullptr, default decoding is used. procs.fTypefaceCtx
181         may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc
182         is called with a pointer to SkTypeface data, data byte length, and user context.
183 
184         @param data   pointer for serial data
185         @param size   size of data
186         @param procs  custom serial data decoders; may be nullptr
187         @return       SkTextBlob constructed from data in memory
188     */
189     static sk_sp<SkTextBlob> Deserialize(const void* data, size_t size,
190                                          const SkDeserialProcs& procs);
191 
192     class SK_API Iter {
193     public:
194         struct Run {
195             SkTypeface*     fTypeface;
196             int             fGlyphCount;
197             const uint16_t* fGlyphIndices;
198         };
199 
200         Iter(const SkTextBlob&);
201 
202         /**
203          * Returns true for each "run" inside the textblob, setting the Run fields (if not null).
204          * If this returns false, there are no more runs, and the Run parameter will be ignored.
205          */
206         bool next(Run*);
207 
208     private:
209         const RunRecord* fRunRecord;
210     };
211 
212 private:
213     friend class SkNVRefCnt<SkTextBlob>;
214 
215     enum GlyphPositioning : uint8_t;
216 
217     explicit SkTextBlob(const SkRect& bounds);
218 
219     ~SkTextBlob();
220 
221     // Memory for objects of this class is created with sk_malloc rather than operator new and must
222     // be freed with sk_free.
223     void operator delete(void* p);
224     void* operator new(size_t);
225     void* operator new(size_t, void* p);
226 
227     static unsigned ScalarsPerGlyph(GlyphPositioning pos);
228 
229     // Call when this blob is part of the key to a cache entry. This allows the cache
230     // to know automatically those entries can be purged when this SkTextBlob is deleted.
notifyAddedToCache(uint32_t cacheID)231     void notifyAddedToCache(uint32_t cacheID) const {
232         fCacheID.store(cacheID);
233     }
234 
235     friend class SkGlyphRunList;
236     friend class GrTextBlobCache;
237     friend class SkTextBlobBuilder;
238     friend class SkTextBlobPriv;
239     friend class SkTextBlobRunIterator;
240 
241     const SkRect                  fBounds;
242     const uint32_t                fUniqueID;
243     mutable std::atomic<uint32_t> fCacheID;
244 
245     SkDEBUGCODE(size_t fStorageSize;)
246 
247     // The actual payload resides in externally-managed storage, following the object.
248     // (see the .cpp for more details)
249 
250     typedef SkRefCnt INHERITED;
251 };
252 
253 /** \class SkTextBlobBuilder
254     Helper class for constructing SkTextBlob.
255 */
256 class SK_API SkTextBlobBuilder {
257 public:
258 
259     /** Constructs empty SkTextBlobBuilder. By default, SkTextBlobBuilder has no runs.
260 
261         @return  empty SkTextBlobBuilder
262     */
263     SkTextBlobBuilder();
264 
265     /** Deletes data allocated internally by SkTextBlobBuilder.
266     */
267     ~SkTextBlobBuilder();
268 
269     /** Returns SkTextBlob built from runs of glyphs added by builder. Returned
270         SkTextBlob is immutable; it may be copied, but its contents may not be altered.
271         Returns nullptr if no runs of glyphs were added by builder.
272 
273         Resets SkTextBlobBuilder to its initial empty state, allowing it to be
274         reused to build a new set of runs.
275 
276         @return  SkTextBlob or nullptr
277     */
278     sk_sp<SkTextBlob> make();
279 
280     /** \struct SkTextBlobBuilder::RunBuffer
281         RunBuffer supplies storage for glyphs and positions within a run.
282 
283         A run is a sequence of glyphs sharing font metrics and positioning.
284         Each run may position its glyphs in one of three ways:
285         by specifying where the first glyph is drawn, and allowing font metrics to
286         determine the advance to subsequent glyphs; by specifying a baseline, and
287         the position on that baseline for each glyph in run; or by providing SkPoint
288         array, one per glyph.
289     */
290     struct RunBuffer {
291         SkGlyphID* glyphs;   //!< storage for glyphs in run
292         SkScalar*  pos;      //!< storage for positions in run
293         char*      utf8text; //!< reserved for future use
294         uint32_t*  clusters; //!< reserved for future use
295 
296         // Helpers, since the "pos" field can be different types (always some number of floats).
pointsRunBuffer297         SkPoint*    points() const { return reinterpret_cast<SkPoint*>(pos); }
xformsRunBuffer298         SkRSXform*  xforms() const { return reinterpret_cast<SkRSXform*>(pos); }
299     };
300 
301     /** Returns run with storage for glyphs. Caller must write count glyphs to
302         RunBuffer::glyphs before next call to SkTextBlobBuilder.
303 
304         RunBuffer::utf8text, and RunBuffer::clusters should be ignored.
305 
306         Glyphs share metrics in font.
307 
308         Glyphs are positioned on a baseline at (x, y), using font metrics to
309         determine their relative placement.
310 
311         bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
312         bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
313         is computed from (x, y) and RunBuffer::glyphs metrics.
314 
315         @param font    SkFont used for this run
316         @param count   number of glyphs
317         @param x       horizontal offset within the blob
318         @param y       vertical offset within the blob
319         @param bounds  optional run bounding box
320         @return        writable glyph buffer
321     */
322     const RunBuffer& allocRun(const SkFont& font, int count, SkScalar x, SkScalar y,
323                               const SkRect* bounds = nullptr);
324 
325     /** Returns run with storage for glyphs and positions along baseline. Caller must
326         write count glyphs to RunBuffer::glyphs, and count scalars to RunBuffer::pos;
327         before next call to SkTextBlobBuilder.
328 
329         RunBuffer::utf8text, and RunBuffer::clusters should be ignored.
330 
331         Glyphs share metrics in font.
332 
333         Glyphs are positioned on a baseline at y, using x-axis positions written by
334         caller to RunBuffer::pos.
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 y, RunBuffer::pos, and RunBuffer::glyphs metrics.
339 
340         @param font    SkFont used for this run
341         @param count   number of glyphs
342         @param y       vertical offset within the blob
343         @param bounds  optional run bounding box
344         @return        writable glyph buffer and x-axis position buffer
345     */
346     const RunBuffer& allocRunPosH(const SkFont& font, int count, SkScalar y,
347                                   const SkRect* bounds = nullptr);
348 
349     /** Returns run with storage for glyphs and SkPoint positions. Caller must
350         write count glyphs to RunBuffer::glyphs, and count SkPoint to RunBuffer::pos;
351         before next call to SkTextBlobBuilder.
352 
353         RunBuffer::utf8text, and RunBuffer::clusters should be ignored.
354 
355         Glyphs share metrics in font.
356 
357         Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using
358         two scalar values for each SkPoint.
359 
360         bounds defines an optional bounding box, used to suppress drawing when SkTextBlob
361         bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds
362         is computed from RunBuffer::pos, and RunBuffer::glyphs metrics.
363 
364         @param font    SkFont used for this run
365         @param count   number of glyphs
366         @param bounds  optional run bounding box
367         @return        writable glyph buffer and SkPoint buffer
368     */
369     const RunBuffer& allocRunPos(const SkFont& font, int count,
370                                  const SkRect* bounds = nullptr);
371 
372     // RunBuffer.pos points to SkRSXform array
373     const RunBuffer& allocRunRSXform(const SkFont& font, int count);
374 
375 private:
376     const RunBuffer& allocRunText(const SkFont& font,
377                                   int count,
378                                   SkScalar x,
379                                   SkScalar y,
380                                   int textByteCount,
381                                   SkString lang,
382                                   const SkRect* bounds = nullptr);
383     const RunBuffer& allocRunTextPosH(const SkFont& font, int count, SkScalar y,
384                                       int textByteCount, SkString lang,
385                                       const SkRect* bounds = nullptr);
386     const RunBuffer& allocRunTextPos(const SkFont& font, int count,
387                                      int textByteCount, SkString lang,
388                                      const SkRect* bounds = nullptr);
389     const RunBuffer& allocRunRSXform(const SkFont& font, int count,
390                                      int textByteCount, SkString lang,
391                                      const SkRect* bounds = nullptr);
392 
393     void reserve(size_t size);
394     void allocInternal(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
395                        int count, int textBytes, SkPoint offset, const SkRect* bounds);
396     bool mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
397                   uint32_t count, SkPoint offset);
398     void updateDeferredBounds();
399 
400     static SkRect ConservativeRunBounds(const SkTextBlob::RunRecord&);
401     static SkRect TightRunBounds(const SkTextBlob::RunRecord&);
402 
403     friend class SkTextBlobPriv;
404     friend class SkTextBlobBuilderPriv;
405 
406     SkAutoTMalloc<uint8_t> fStorage;
407     size_t                 fStorageSize;
408     size_t                 fStorageUsed;
409 
410     SkRect                 fBounds;
411     int                    fRunCount;
412     bool                   fDeferredBounds;
413     size_t                 fLastRun; // index into fStorage
414 
415     RunBuffer              fCurrentRunBuffer;
416 };
417 
418 #endif // SkTextBlob_DEFINED
419