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 "../private/SkTemplates.h"
12#include "../private/SkAtomics.h"
13#include "SkPaint.h"
14#include "SkString.h"
15#include "SkRefCnt.h"
16
17class SkReadBuffer;
18class SkWriteBuffer;
19
20struct SkSerialProcs;
21struct SkDeserialProcs;
22
23typedef void (*SkTypefaceCatalogerProc)(SkTypeface*, void* ctx);
24typedef sk_sp<SkTypeface> (*SkTypefaceResolverProc)(uint32_t id, void* ctx);
25
26/** \class SkTextBlob
27
28    SkTextBlob combines multiple text runs into an immutable, ref-counted structure.
29*/
30class SK_API SkTextBlob final : public SkNVRefCnt<SkTextBlob> {
31public:
32    /**
33     *  Returns a conservative blob bounding box.
34     */
35    const SkRect& bounds() const { return fBounds; }
36
37    /**
38     *  Return a non-zero, unique value representing the text blob.
39     */
40    uint32_t uniqueID() const { return fUniqueID; }
41
42    /**
43     *  Serialize to a buffer.
44     */
45    void flatten(SkWriteBuffer&) const;
46
47    /**
48     *  Recreate an SkTextBlob that was serialized into a buffer.
49     *
50     *  @param  SkReadBuffer Serialized blob data.
51     *  @return A new SkTextBlob representing the serialized data, or NULL if the buffer is
52     *          invalid.
53     */
54    static sk_sp<SkTextBlob> MakeFromBuffer(SkReadBuffer&);
55
56    enum GlyphPositioning : uint8_t {
57        kDefault_Positioning      = 0, // Default glyph advances -- zero scalars per glyph.
58        kHorizontal_Positioning   = 1, // Horizontal positioning -- one scalar per glyph.
59        kFull_Positioning         = 2  // Point positioning -- two scalars per glyph.
60    };
61
62    /**
63     *  Serialize the typeface into a data blob, storing type uniqueID of each referenced typeface.
64     *  During this process, each time a typeface is encountered, it is passed to the catalog,
65     *  allowing the caller to what typeface IDs will need to be resolved in Deserialize().
66     */
67    sk_sp<SkData> serialize(SkTypefaceCatalogerProc, void* ctx) const;
68
69    /**
70     *  Re-create a text blob previously serialized. Since the serialized form records the uniqueIDs
71     *  of its typefaces, deserialization requires that the caller provide the corresponding
72     *  SkTypefaces for those IDs.
73     */
74    static sk_sp<SkTextBlob> Deserialize(const void* data, size_t size,
75                                         SkTypefaceResolverProc, void* ctx);
76
77    sk_sp<SkData> serialize(const SkSerialProcs&) const;
78    sk_sp<SkData> serialize() const;
79    static sk_sp<SkTextBlob> Deserialize(const void* data, size_t size, const SkDeserialProcs&);
80    static sk_sp<SkTextBlob> Deserialize(const void* data, size_t size);
81
82private:
83    friend class SkNVRefCnt<SkTextBlob>;
84    class RunRecord;
85
86    explicit SkTextBlob(const SkRect& bounds);
87
88    ~SkTextBlob();
89
90    // Memory for objects of this class is created with sk_malloc rather than operator new and must
91    // be freed with sk_free.
92    void operator delete(void* p) { sk_free(p); }
93    void* operator new(size_t) {
94        SK_ABORT("All blobs are created by placement new.");
95        return sk_malloc_throw(0);
96    }
97    void* operator new(size_t, void* p) { return p; }
98
99    static unsigned ScalarsPerGlyph(GlyphPositioning pos);
100
101    // Call when this blob is part of the key to a cache entry. This allows the cache
102    // to know automatically those entries can be purged when this SkTextBlob is deleted.
103    void notifyAddedToCache(uint32_t cacheID) const {
104        fCacheID.store(cacheID);
105    }
106
107    friend class GrTextBlobCache;
108    friend class SkTextBlobBuilder;
109    friend class SkTextBlobRunIterator;
110
111    const SkRect               fBounds;
112    const uint32_t             fUniqueID;
113    mutable SkAtomic<uint32_t> fCacheID;
114
115    SkDEBUGCODE(size_t fStorageSize;)
116
117    // The actual payload resides in externally-managed storage, following the object.
118    // (see the .cpp for more details)
119
120    typedef SkRefCnt INHERITED;
121};
122
123/** \class SkTextBlobBuilder
124
125    Helper class for constructing SkTextBlobs.
126 */
127class SK_API SkTextBlobBuilder {
128public:
129    SkTextBlobBuilder();
130
131    ~SkTextBlobBuilder();
132
133    /**
134     *  Returns an immutable SkTextBlob for the current runs/glyphs,
135     *  or nullptr if no runs were allocated.
136     *
137     *  The builder is reset and can be reused.
138     */
139    sk_sp<SkTextBlob> make();
140
141    /**
142     *  Glyph and position buffers associated with a run.
143     *
144     *  A run is a sequence of glyphs sharing the same font metrics
145     *  and positioning mode.
146     *
147     *  If textByteCount is 0, utf8text and clusters will be NULL (no
148     *  character information will be associated with the glyphs).
149     *
150     *  utf8text will point to a buffer of size textByteCount bytes.
151     *
152     *  clusters (if not NULL) will point to an array of size count.
153     *  For each glyph, give the byte-offset into the text for the
154     *  first byte in the first character in that glyph's cluster.
155     *  Each value in the array should be an integer less than
156     *  textByteCount.  Values in the array should either be
157     *  monotonically increasing (left-to-right text) or monotonically
158     *  decreasing (right-to-left text).  This definiton is conviently
159     *  the same as used by Harfbuzz's hb_glyph_info_t::cluster field,
160     *  except that Harfbuzz interleaves glyphs and clusters.
161     */
162    struct RunBuffer {
163        SkGlyphID* glyphs;
164        SkScalar* pos;
165        char* utf8text;
166        uint32_t* clusters;
167    };
168
169    /**
170     *  Allocates a new default-positioned run and returns its writable glyph buffer
171     *  for direct manipulation.
172     *
173     *  @param font    The font to be used for this run.
174     *  @param count   Number of glyphs.
175     *  @param x,y     Position within the blob.
176     *  @param textByteCount length of the original UTF-8 text that
177     *                 corresponds to this sequence of glyphs.  If 0,
178     *                 text will not be included in the textblob.
179     *  @param lang    Language code, currently unimplemented.
180     *  @param bounds  Optional run bounding box. If known in advance (!= NULL), it will
181     *                 be used when computing the blob bounds, to avoid re-measuring.
182     *
183     *  @return        A writable glyph buffer, valid until the next allocRun() or
184     *                 build() call. The buffer is guaranteed to hold @count@ glyphs.
185     */
186    const RunBuffer& allocRunText(const SkPaint& font,
187                                  int count,
188                                  SkScalar x,
189                                  SkScalar y,
190                                  int textByteCount,
191                                  SkString lang,
192                                  const SkRect* bounds = nullptr);
193    const RunBuffer& allocRun(const SkPaint& font, int count, SkScalar x, SkScalar y,
194                              const SkRect* bounds = nullptr) {
195        return this->allocRunText(font, count, x, y, 0, SkString(), bounds);
196    }
197
198    /**
199     *  Allocates a new horizontally-positioned run and returns its writable glyph and position
200     *  buffers for direct manipulation.
201     *
202     *  @param font    The font to be used for this run.
203     *  @param count   Number of glyphs.
204     *  @param y       Vertical offset within the blob.
205     *  @param textByteCount length of the original UTF-8 text that
206     *                 corresponds to this sequence of glyphs.  If 0,
207     *                 text will not be included in the textblob.
208     *  @param lang    Language code, currently unimplemented.
209     *  @param bounds  Optional run bounding box. If known in advance (!= NULL), it will
210     *                 be used when computing the blob bounds, to avoid re-measuring.
211     *
212     *  @return        Writable glyph and position buffers, valid until the next allocRun()
213     *                 or build() call. The buffers are guaranteed to hold @count@ elements.
214     */
215    const RunBuffer& allocRunTextPosH(const SkPaint& font, int count, SkScalar y,
216                                      int textByteCount, SkString lang,
217                                      const SkRect* bounds = nullptr);
218    const RunBuffer& allocRunPosH(const SkPaint& font, int count, SkScalar y,
219                                  const SkRect* bounds = nullptr) {
220        return this->allocRunTextPosH(font, count, y, 0, SkString(), bounds);
221    }
222
223    /**
224     *  Allocates a new fully-positioned run and returns its writable glyph and position
225     *  buffers for direct manipulation.
226     *
227     *  @param font   The font to be used for this run.
228     *  @param count  Number of glyphs.
229     *  @param textByteCount length of the original UTF-8 text that
230     *                 corresponds to this sequence of glyphs.  If 0,
231     *                 text will not be included in the textblob.
232     *  @param lang    Language code, currently unimplemented.
233     *  @param bounds Optional run bounding box. If known in advance (!= NULL), it will
234     *                be used when computing the blob bounds, to avoid re-measuring.
235     *
236     *  @return       Writable glyph and position buffers, valid until the next allocRun()
237     *                or build() call. The glyph buffer and position buffer are
238     *                guaranteed to hold @count@ and 2 * @count@ elements, respectively.
239     */
240    const RunBuffer& allocRunTextPos(const SkPaint& font, int count,
241                                     int textByteCount, SkString lang,
242                                     const SkRect* bounds = nullptr);
243    const RunBuffer& allocRunPos(const SkPaint& font, int count,
244                                 const SkRect* bounds = nullptr) {
245        return this->allocRunTextPos(font, count, 0, SkString(), bounds);
246    }
247
248private:
249    void reserve(size_t size);
250    void allocInternal(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
251                       int count, int textBytes, SkPoint offset, const SkRect* bounds);
252    bool mergeRun(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
253                  uint32_t count, SkPoint offset);
254    void updateDeferredBounds();
255
256    static SkRect ConservativeRunBounds(const SkTextBlob::RunRecord&);
257    static SkRect TightRunBounds(const SkTextBlob::RunRecord&);
258
259    SkAutoTMalloc<uint8_t> fStorage;
260    size_t                 fStorageSize;
261    size_t                 fStorageUsed;
262
263    SkRect                 fBounds;
264    int                    fRunCount;
265    bool                   fDeferredBounds;
266    size_t                 fLastRun; // index into fStorage
267
268    RunBuffer              fCurrentRunBuffer;
269};
270
271#endif // SkTextBlob_DEFINED
272