• 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