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