blob: 3879c1e78e8e8e7d2bdbe0dba312d31d4773a7b8 [file] [log] [blame]
fmalita00d5c2c2014-08-21 08:53:26 -07001/*
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
halcanary33779752015-10-27 14:01:05 -07008#include "SkTextBlobRunIterator.h"
fmalita00d5c2c2014-08-21 08:53:26 -07009
Florin Malitad923a712017-11-22 10:11:12 -050010#include "SkSafeMath.h"
fmalita3dc40ac2015-01-28 10:56:06 -080011#include "SkTypeface.h"
Florin Malitad923a712017-11-22 10:11:12 -050012#include "SkValidatingReadBuffer.h"
fmalitab7425172014-08-26 07:56:44 -070013#include "SkWriteBuffer.h"
14
Florin Malitad923a712017-11-22 10:11:12 -050015#include <limits>
16
Florin Malita4a01ac92017-03-13 16:45:28 -040017#if SK_SUPPORT_GPU
18#include "text/GrTextBlobCache.h"
19#endif
20
fmalita055f6b52015-04-09 08:49:32 -070021namespace {
22
23// TODO(fmalita): replace with SkFont.
24class RunFont : SkNoncopyable {
25public:
26 RunFont(const SkPaint& paint)
27 : fSize(paint.getTextSize())
28 , fScaleX(paint.getTextScaleX())
29 , fTypeface(SkSafeRef(paint.getTypeface()))
30 , fSkewX(paint.getTextSkewX())
fmalita32d6b872015-11-17 11:39:32 -080031 , fAlign(paint.getTextAlign())
fmalita055f6b52015-04-09 08:49:32 -070032 , fHinting(paint.getHinting())
33 , fFlags(paint.getFlags() & kFlagsMask) { }
34
35 void applyToPaint(SkPaint* paint) const {
36 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
bungeman13b9c952016-05-12 10:09:30 -070037 paint->setTypeface(fTypeface);
fmalita055f6b52015-04-09 08:49:32 -070038 paint->setTextSize(fSize);
39 paint->setTextScaleX(fScaleX);
40 paint->setTextSkewX(fSkewX);
fmalita32d6b872015-11-17 11:39:32 -080041 paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
fmalita055f6b52015-04-09 08:49:32 -070042 paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
43
44 paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
45 }
46
47 bool operator==(const RunFont& other) const {
48 return fTypeface == other.fTypeface
49 && fSize == other.fSize
50 && fScaleX == other.fScaleX
51 && fSkewX == other.fSkewX
fmalita32d6b872015-11-17 11:39:32 -080052 && fAlign == other.fAlign
fmalita055f6b52015-04-09 08:49:32 -070053 && fHinting == other.fHinting
54 && fFlags == other.fFlags;
55 }
56
57 bool operator!=(const RunFont& other) const {
58 return !(*this == other);
59 }
joshualitt9e36c1a2015-04-14 12:17:27 -070060
61 uint32_t flags() const { return fFlags; }
62
fmalita055f6b52015-04-09 08:49:32 -070063private:
64 const static uint32_t kFlagsMask =
65 SkPaint::kAntiAlias_Flag |
fmalita055f6b52015-04-09 08:49:32 -070066 SkPaint::kFakeBoldText_Flag |
67 SkPaint::kLinearText_Flag |
68 SkPaint::kSubpixelText_Flag |
69 SkPaint::kDevKernText_Flag |
70 SkPaint::kLCDRenderText_Flag |
71 SkPaint::kEmbeddedBitmapText_Flag |
72 SkPaint::kAutoHinting_Flag |
73 SkPaint::kVerticalText_Flag |
jvanverth4854d132015-06-22 06:46:56 -070074 SkPaint::kGenA8FromLCD_Flag;
fmalita055f6b52015-04-09 08:49:32 -070075
76 SkScalar fSize;
77 SkScalar fScaleX;
78
Hal Canary704cd322016-11-07 14:13:52 -050079 // Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
fmalita055f6b52015-04-09 08:49:32 -070080 // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
bungeman13b9c952016-05-12 10:09:30 -070081 sk_sp<SkTypeface> fTypeface;
fmalita055f6b52015-04-09 08:49:32 -070082 SkScalar fSkewX;
83
fmalita32d6b872015-11-17 11:39:32 -080084 static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
85 uint32_t fAlign : 2;
bungeman99fe8222015-08-20 07:57:51 -070086 static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
fmalita055f6b52015-04-09 08:49:32 -070087 uint32_t fHinting : 2;
bungeman99fe8222015-08-20 07:57:51 -070088 static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
fmalita055f6b52015-04-09 08:49:32 -070089 uint32_t fFlags : 16;
90
91 typedef SkNoncopyable INHERITED;
92};
93
94struct RunFontStorageEquivalent {
95 SkScalar fSize, fScaleX;
96 void* fTypeface;
97 SkScalar fSkewX;
98 uint32_t fFlags;
99};
bungeman99fe8222015-08-20 07:57:51 -0700100static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
fmalita055f6b52015-04-09 08:49:32 -0700101
102} // anonymous namespace
103
fmalita3c196de2014-09-20 05:40:22 -0700104//
105// Textblob data is laid out into externally-managed storage as follows:
106//
107// -----------------------------------------------------------------------------
108// | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
109// -----------------------------------------------------------------------------
110//
111// Each run record describes a text blob run, and can be used to determine the (implicit)
112// location of the following record.
halcanary4f0a23a2016-08-30 11:58:33 -0700113//
114// Extended Textblob runs have more data after the Pos[] array:
115//
116// -------------------------------------------------------------------------
117// ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
118// -------------------------------------------------------------------------
119//
120// To determine the length of the extended run data, the TextSize must be read.
121//
122// Extended Textblob runs may be mixed with non-extended runs.
fmalita3c196de2014-09-20 05:40:22 -0700123
124SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
125
halcanary4f0a23a2016-08-30 11:58:33 -0700126namespace {
127struct RunRecordStorageEquivalent {
128 RunFont fFont;
129 SkPoint fOffset;
130 uint32_t fCount;
131 uint32_t fFlags;
132 SkDEBUGCODE(unsigned fMagic;)
133};
134}
135
fmalita3c196de2014-09-20 05:40:22 -0700136class SkTextBlob::RunRecord {
137public:
halcanary4f0a23a2016-08-30 11:58:33 -0700138 RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
fmalita055f6b52015-04-09 08:49:32 -0700139 : fFont(font)
140 , fCount(count)
fmalita3c196de2014-09-20 05:40:22 -0700141 , fOffset(offset)
Florin Malita3a9a7a32017-03-13 09:03:24 -0400142 , fFlags(pos) {
143 SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
144
fmalita3c196de2014-09-20 05:40:22 -0700145 SkDEBUGCODE(fMagic = kRunRecordMagic);
halcanary4f0a23a2016-08-30 11:58:33 -0700146 if (textSize > 0) {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400147 fFlags |= kExtended_Flag;
halcanary4f0a23a2016-08-30 11:58:33 -0700148 *this->textSizePtr() = textSize;
149 }
fmalita3c196de2014-09-20 05:40:22 -0700150 }
151
152 uint32_t glyphCount() const {
153 return fCount;
154 }
155
156 const SkPoint& offset() const {
157 return fOffset;
158 }
159
fmalita055f6b52015-04-09 08:49:32 -0700160 const RunFont& font() const {
fmalita3c196de2014-09-20 05:40:22 -0700161 return fFont;
162 }
163
164 GlyphPositioning positioning() const {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400165 return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
fmalita3c196de2014-09-20 05:40:22 -0700166 }
167
168 uint16_t* glyphBuffer() const {
halcanary4f0a23a2016-08-30 11:58:33 -0700169 static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
170 // Glyphs are stored immediately following the record.
fmalita3c196de2014-09-20 05:40:22 -0700171 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
172 }
173
174 SkScalar* posBuffer() const {
175 // Position scalars follow the (aligned) glyph buffer.
176 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
177 SkAlign4(fCount * sizeof(uint16_t)));
178 }
179
Florin Malita3a9a7a32017-03-13 09:03:24 -0400180 uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
halcanary4f0a23a2016-08-30 11:58:33 -0700181
182 uint32_t* clusterBuffer() const {
183 // clusters follow the textSize.
Florin Malita3a9a7a32017-03-13 09:03:24 -0400184 return isExtended() ? 1 + this->textSizePtr() : nullptr;
halcanary4f0a23a2016-08-30 11:58:33 -0700185 }
186
187 char* textBuffer() const {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400188 return isExtended()
189 ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
190 : nullptr;
halcanary4f0a23a2016-08-30 11:58:33 -0700191 }
192
Florin Malitad923a712017-11-22 10:11:12 -0500193 static size_t StorageSize(uint32_t glyphCount, uint32_t textSize,
194 SkTextBlob::GlyphPositioning positioning,
195 SkSafeMath* safe) {
halcanary4f0a23a2016-08-30 11:58:33 -0700196 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
Florin Malitad923a712017-11-22 10:11:12 -0500197
198 auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
199 posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
200
fmalita3c196de2014-09-20 05:40:22 -0700201 // RunRecord object + (aligned) glyph buffer + position buffer
Florin Malitad923a712017-11-22 10:11:12 -0500202 auto size = sizeof(SkTextBlob::RunRecord);
203 size = safe->add(size, safe->alignUp(glyphSize, 4));
204 size = safe->add(size, posSize);
205
206 if (textSize) { // Extended run.
207 size = safe->add(size, sizeof(uint32_t));
208 size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
209 size = safe->add(size, textSize);
halcanary4f0a23a2016-08-30 11:58:33 -0700210 }
Florin Malitad923a712017-11-22 10:11:12 -0500211
212 return safe->alignUp(size, sizeof(void*));
fmalita3c196de2014-09-20 05:40:22 -0700213 }
214
215 static const RunRecord* First(const SkTextBlob* blob) {
216 // The first record (if present) is stored following the blob object.
217 return reinterpret_cast<const RunRecord*>(blob + 1);
218 }
219
220 static const RunRecord* Next(const RunRecord* run) {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400221 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
fmalita3c196de2014-09-20 05:40:22 -0700222 }
223
fmalita92d976c2015-10-05 11:09:57 -0700224 void validate(const uint8_t* storageTop) const {
fmalita3c196de2014-09-20 05:40:22 -0700225 SkASSERT(kRunRecordMagic == fMagic);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400226 SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
halcanary4f0a23a2016-08-30 11:58:33 -0700227
fmalita3c196de2014-09-20 05:40:22 -0700228 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
Florin Malita3a9a7a32017-03-13 09:03:24 -0400229 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
230 <= (SkScalar*)NextUnchecked(this));
231 if (isExtended()) {
halcanary4f0a23a2016-08-30 11:58:33 -0700232 SkASSERT(textSize() > 0);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400233 SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
234 SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
235 SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
halcanary4f0a23a2016-08-30 11:58:33 -0700236 }
237 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
238 "runrecord_should_stay_packed");
fmalita3c196de2014-09-20 05:40:22 -0700239 }
240
241private:
242 friend class SkTextBlobBuilder;
243
Florin Malita3a9a7a32017-03-13 09:03:24 -0400244 enum Flags {
245 kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning
246 kLast_Flag = 0x04, // set for the last blob run
247 kExtended_Flag = 0x08, // set for runs with text/cluster info
248 };
249
250 static const RunRecord* NextUnchecked(const RunRecord* run) {
Florin Malitad923a712017-11-22 10:11:12 -0500251 SkSafeMath safe;
252 auto res = reinterpret_cast<const RunRecord*>(
253 reinterpret_cast<const uint8_t*>(run)
254 + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
255 SkASSERT(safe);
256 return res;
Florin Malita3a9a7a32017-03-13 09:03:24 -0400257 }
258
Florin Malitad923a712017-11-22 10:11:12 -0500259 static size_t PosCount(uint32_t glyphCount,
260 SkTextBlob::GlyphPositioning positioning,
261 SkSafeMath* safe) {
262 return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
halcanary4f0a23a2016-08-30 11:58:33 -0700263 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400264
halcanary4f0a23a2016-08-30 11:58:33 -0700265 uint32_t* textSizePtr() const {
266 // textSize follows the position buffer.
Florin Malita3a9a7a32017-03-13 09:03:24 -0400267 SkASSERT(isExtended());
Florin Malitad923a712017-11-22 10:11:12 -0500268 SkSafeMath safe;
269 auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
270 SkASSERT(safe);
271 return res;
halcanary4f0a23a2016-08-30 11:58:33 -0700272 }
273
fmalita3c196de2014-09-20 05:40:22 -0700274 void grow(uint32_t count) {
275 SkScalar* initialPosBuffer = posBuffer();
276 uint32_t initialCount = fCount;
277 fCount += count;
278
279 // Move the initial pos scalars to their new location.
Florin Malita3a9a7a32017-03-13 09:03:24 -0400280 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
281 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
fmalita3c196de2014-09-20 05:40:22 -0700282
283 // memmove, as the buffers may overlap
284 memmove(posBuffer(), initialPosBuffer, copySize);
285 }
286
Florin Malita3a9a7a32017-03-13 09:03:24 -0400287 bool isExtended() const {
288 return fFlags & kExtended_Flag;
289 }
290
fmalita055f6b52015-04-09 08:49:32 -0700291 RunFont fFont;
fmalita3c196de2014-09-20 05:40:22 -0700292 uint32_t fCount;
293 SkPoint fOffset;
Florin Malita3a9a7a32017-03-13 09:03:24 -0400294 uint32_t fFlags;
fmalita3c196de2014-09-20 05:40:22 -0700295
296 SkDEBUGCODE(unsigned fMagic;)
297};
298
joshualitt2af85832015-03-25 13:40:13 -0700299static int32_t gNextID = 1;
300static int32_t next_id() {
301 int32_t id;
302 do {
303 id = sk_atomic_inc(&gNextID);
304 } while (id == SK_InvalidGenID);
305 return id;
306}
307
Florin Malita3a9a7a32017-03-13 09:03:24 -0400308SkTextBlob::SkTextBlob(const SkRect& bounds)
309 : fBounds(bounds)
Florin Malita4a01ac92017-03-13 16:45:28 -0400310 , fUniqueID(next_id())
311 , fAddedToCache(false) {}
fmalita00d5c2c2014-08-21 08:53:26 -0700312
fmalita3c196de2014-09-20 05:40:22 -0700313SkTextBlob::~SkTextBlob() {
Florin Malita4a01ac92017-03-13 16:45:28 -0400314#if SK_SUPPORT_GPU
315 if (fAddedToCache.load()) {
316 GrTextBlobCache::PostPurgeBlobMessage(fUniqueID);
317 }
318#endif
319
Florin Malita3a9a7a32017-03-13 09:03:24 -0400320 const auto* run = RunRecord::First(this);
321 do {
322 const auto* nextRun = RunRecord::Next(run);
fmalita3c196de2014-09-20 05:40:22 -0700323 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
324 run->~RunRecord();
325 run = nextRun;
Florin Malita3a9a7a32017-03-13 09:03:24 -0400326 } while (run);
fmalita3c196de2014-09-20 05:40:22 -0700327}
328
halcanary4f0a23a2016-08-30 11:58:33 -0700329namespace {
Florin Malita90dcafc2017-11-22 10:53:33 -0500330
halcanary4f0a23a2016-08-30 11:58:33 -0700331union PositioningAndExtended {
332 int32_t intValue;
333 struct {
334 SkTextBlob::GlyphPositioning positioning;
Florin Malita90dcafc2017-11-22 10:53:33 -0500335 uint8_t extended;
halcanary4f0a23a2016-08-30 11:58:33 -0700336 uint16_t padding;
337 };
338};
Florin Malita90dcafc2017-11-22 10:53:33 -0500339
340static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
341
halcanary4f0a23a2016-08-30 11:58:33 -0700342} // namespace
343
fmalita3c196de2014-09-20 05:40:22 -0700344unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
345 // GlyphPositioning values are directly mapped to scalars-per-glyph.
346 SkASSERT(pos <= 2);
347 return pos;
348}
349
halcanary33779752015-10-27 14:01:05 -0700350SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
Florin Malita3a9a7a32017-03-13 09:03:24 -0400351 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
fmalita3c196de2014-09-20 05:40:22 -0700352 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
fmalita00d5c2c2014-08-21 08:53:26 -0700353}
354
halcanary33779752015-10-27 14:01:05 -0700355bool SkTextBlobRunIterator::done() const {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400356 return !fCurrentRun;
fmalita00d5c2c2014-08-21 08:53:26 -0700357}
358
halcanary33779752015-10-27 14:01:05 -0700359void SkTextBlobRunIterator::next() {
fmalita00d5c2c2014-08-21 08:53:26 -0700360 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700361
362 if (!this->done()) {
363 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
halcanary33779752015-10-27 14:01:05 -0700364 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
fmalita3c196de2014-09-20 05:40:22 -0700365 }
fmalita00d5c2c2014-08-21 08:53:26 -0700366}
367
halcanary33779752015-10-27 14:01:05 -0700368uint32_t SkTextBlobRunIterator::glyphCount() const {
fmalita00d5c2c2014-08-21 08:53:26 -0700369 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700370 return fCurrentRun->glyphCount();
fmalita00d5c2c2014-08-21 08:53:26 -0700371}
372
halcanary33779752015-10-27 14:01:05 -0700373const uint16_t* SkTextBlobRunIterator::glyphs() const {
fmalita00d5c2c2014-08-21 08:53:26 -0700374 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700375 return fCurrentRun->glyphBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700376}
377
halcanary33779752015-10-27 14:01:05 -0700378const SkScalar* SkTextBlobRunIterator::pos() const {
fmalita00d5c2c2014-08-21 08:53:26 -0700379 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700380 return fCurrentRun->posBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700381}
382
halcanary33779752015-10-27 14:01:05 -0700383const SkPoint& SkTextBlobRunIterator::offset() const {
fmalita00d5c2c2014-08-21 08:53:26 -0700384 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700385 return fCurrentRun->offset();
fmalita00d5c2c2014-08-21 08:53:26 -0700386}
387
halcanary33779752015-10-27 14:01:05 -0700388SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
fmalita00d5c2c2014-08-21 08:53:26 -0700389 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700390 return fCurrentRun->positioning();
fmalita00d5c2c2014-08-21 08:53:26 -0700391}
392
halcanary33779752015-10-27 14:01:05 -0700393void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
fmalita37ecbaf2014-08-22 09:01:19 -0700394 SkASSERT(!this->done());
395
fmalita055f6b52015-04-09 08:49:32 -0700396 fCurrentRun->font().applyToPaint(paint);
fmalita37ecbaf2014-08-22 09:01:19 -0700397}
398
halcanary4f0a23a2016-08-30 11:58:33 -0700399uint32_t* SkTextBlobRunIterator::clusters() const {
400 SkASSERT(!this->done());
401 return fCurrentRun->clusterBuffer();
402}
403uint32_t SkTextBlobRunIterator::textSize() const {
404 SkASSERT(!this->done());
405 return fCurrentRun->textSize();
406}
407char* SkTextBlobRunIterator::text() const {
408 SkASSERT(!this->done());
409 return fCurrentRun->textBuffer();
410}
411
412
halcanary33779752015-10-27 14:01:05 -0700413bool SkTextBlobRunIterator::isLCD() const {
joshualitt9e36c1a2015-04-14 12:17:27 -0700414 return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
415}
416
fmalita3c196de2014-09-20 05:40:22 -0700417SkTextBlobBuilder::SkTextBlobBuilder()
418 : fStorageSize(0)
419 , fStorageUsed(0)
420 , fRunCount(0)
421 , fDeferredBounds(false)
422 , fLastRun(0) {
fmalita00d5c2c2014-08-21 08:53:26 -0700423 fBounds.setEmpty();
424}
425
426SkTextBlobBuilder::~SkTextBlobBuilder() {
halcanary96fcdcc2015-08-27 07:41:13 -0700427 if (nullptr != fStorage.get()) {
fmalita3c196de2014-09-20 05:40:22 -0700428 // We are abandoning runs and must destruct the associated font data.
429 // The easiest way to accomplish that is to use the blob destructor.
fmalita37283c22016-09-13 10:00:23 -0700430 this->make();
fmalita3c196de2014-09-20 05:40:22 -0700431 }
fmalita00d5c2c2014-08-21 08:53:26 -0700432}
433
fmalita3dc40ac2015-01-28 10:56:06 -0800434SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
435 SkRect bounds;
fmalita055f6b52015-04-09 08:49:32 -0700436 SkPaint paint;
437 run.font().applyToPaint(&paint);
fmalitab0b45d32015-10-09 14:46:28 -0700438
439 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
440 paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
441 return bounds.makeOffset(run.offset().x(), run.offset().y());
442 }
443
444 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
445 paint.getTextWidths(run.glyphBuffer(),
446 run.glyphCount() * sizeof(uint16_t),
Ben Wagnera93a14a2017-08-28 10:34:05 -0400447 nullptr,
fmalitab0b45d32015-10-09 14:46:28 -0700448 glyphBounds.get());
449
450 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
451 SkTextBlob::kHorizontal_Positioning == run.positioning());
452 // kFull_Positioning => [ x, y, x, y... ]
453 // kHorizontal_Positioning => [ x, x, x... ]
454 // (const y applied by runBounds.offset(run->offset()) later)
455 const SkScalar horizontalConstY = 0;
456 const SkScalar* glyphPosX = run.posBuffer();
457 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
458 glyphPosX + 1 : &horizontalConstY;
459 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
460 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
461 posXInc : 0;
462
463 bounds.setEmpty();
464 for (unsigned i = 0; i < run.glyphCount(); ++i) {
465 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
466 glyphPosX += posXInc;
467 glyphPosY += posYInc;
468 }
469
470 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
fmalita3dc40ac2015-01-28 10:56:06 -0800471
472 return bounds.makeOffset(run.offset().x(), run.offset().y());
473}
474
475SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
fmalita3dc40ac2015-01-28 10:56:06 -0800476 SkASSERT(run.glyphCount() > 0);
fmalitaf9a40722015-01-29 12:24:24 -0800477 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
478 SkTextBlob::kHorizontal_Positioning == run.positioning());
fmalita3dc40ac2015-01-28 10:56:06 -0800479
fmalitab0b45d32015-10-09 14:46:28 -0700480 SkPaint paint;
481 run.font().applyToPaint(&paint);
482 const SkRect fontBounds = paint.getFontBounds();
483 if (fontBounds.isEmpty()) {
484 // Empty font bounds are likely a font bug. TightBounds has a better chance of
485 // producing useful results in this case.
486 return TightRunBounds(run);
487 }
488
489 // Compute the glyph position bbox.
fmalitaf9a40722015-01-29 12:24:24 -0800490 SkRect bounds;
491 switch (run.positioning()) {
492 case SkTextBlob::kHorizontal_Positioning: {
493 const SkScalar* glyphPos = run.posBuffer();
494 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
495
496 SkScalar minX = *glyphPos;
497 SkScalar maxX = *glyphPos;
498 for (unsigned i = 1; i < run.glyphCount(); ++i) {
499 SkScalar x = glyphPos[i];
500 minX = SkMinScalar(x, minX);
501 maxX = SkMaxScalar(x, maxX);
502 }
503
504 bounds.setLTRB(minX, 0, maxX, 0);
505 } break;
506 case SkTextBlob::kFull_Positioning: {
507 const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
508 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
509
510 bounds.setBounds(glyphPosPts, run.glyphCount());
511 } break;
512 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400513 SK_ABORT("unsupported positioning mode");
fmalita3dc40ac2015-01-28 10:56:06 -0800514 }
515
516 // Expand by typeface glyph bounds.
fmalita3dc40ac2015-01-28 10:56:06 -0800517 bounds.fLeft += fontBounds.left();
518 bounds.fTop += fontBounds.top();
519 bounds.fRight += fontBounds.right();
520 bounds.fBottom += fontBounds.bottom();
521
522 // Offset by run position.
523 return bounds.makeOffset(run.offset().x(), run.offset().y());
524}
525
fmalita00d5c2c2014-08-21 08:53:26 -0700526void SkTextBlobBuilder::updateDeferredBounds() {
fmalita3c196de2014-09-20 05:40:22 -0700527 SkASSERT(!fDeferredBounds || fRunCount > 0);
fmalita00d5c2c2014-08-21 08:53:26 -0700528
529 if (!fDeferredBounds) {
530 return;
531 }
532
fmalita19653d12014-10-16 11:53:30 -0700533 SkASSERT(fLastRun >= sizeof(SkTextBlob));
534 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
535 fLastRun);
fmalita19653d12014-10-16 11:53:30 -0700536
fmalitaf9a40722015-01-29 12:24:24 -0800537 // FIXME: we should also use conservative bounds for kDefault_Positioning.
538 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
539 TightRunBounds(*run) : ConservativeRunBounds(*run);
fmalita19653d12014-10-16 11:53:30 -0700540 fBounds.join(runBounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700541 fDeferredBounds = false;
542}
543
fmalita3c196de2014-09-20 05:40:22 -0700544void SkTextBlobBuilder::reserve(size_t size) {
Florin Malitad923a712017-11-22 10:11:12 -0500545 SkSafeMath safe;
546
fmalita3c196de2014-09-20 05:40:22 -0700547 // We don't currently pre-allocate, but maybe someday...
Florin Malitad923a712017-11-22 10:11:12 -0500548 if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
fmalita3c196de2014-09-20 05:40:22 -0700549 return;
550 }
fmalita00d5c2c2014-08-21 08:53:26 -0700551
fmalita3c196de2014-09-20 05:40:22 -0700552 if (0 == fRunCount) {
halcanary96fcdcc2015-08-27 07:41:13 -0700553 SkASSERT(nullptr == fStorage.get());
fmalita3c196de2014-09-20 05:40:22 -0700554 SkASSERT(0 == fStorageSize);
555 SkASSERT(0 == fStorageUsed);
556
557 // the first allocation also includes blob storage
Florin Malitad923a712017-11-22 10:11:12 -0500558 fStorageUsed = sizeof(SkTextBlob);
fmalita3c196de2014-09-20 05:40:22 -0700559 }
560
Florin Malitad923a712017-11-22 10:11:12 -0500561 fStorageSize = safe.add(fStorageUsed, size);
562
fmalita3c196de2014-09-20 05:40:22 -0700563 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
Florin Malitad923a712017-11-22 10:11:12 -0500564 // Also, this is counting on the underlying realloc to throw when passed max().
565 fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
fmalita3c196de2014-09-20 05:40:22 -0700566}
567
568bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
Florin Malitad923a712017-11-22 10:11:12 -0500569 uint32_t count, SkPoint offset) {
fmalita3c196de2014-09-20 05:40:22 -0700570 if (0 == fLastRun) {
571 SkASSERT(0 == fRunCount);
572 return false;
573 }
574
575 SkASSERT(fLastRun >= sizeof(SkTextBlob));
576 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
577 fLastRun);
578 SkASSERT(run->glyphCount() > 0);
579
halcanary4f0a23a2016-08-30 11:58:33 -0700580 if (run->textSize() != 0) {
581 return false;
582 }
583
fmalita3c196de2014-09-20 05:40:22 -0700584 if (run->positioning() != positioning
585 || run->font() != font
586 || (run->glyphCount() + count < run->glyphCount())) {
587 return false;
fmalita00d5c2c2014-08-21 08:53:26 -0700588 }
589
590 // we can merge same-font/same-positioning runs in the following cases:
591 // * fully positioned run following another fully positioned run
592 // * horizontally postioned run following another horizontally positioned run with the same
593 // y-offset
fmalita3c196de2014-09-20 05:40:22 -0700594 if (SkTextBlob::kFull_Positioning != positioning
595 && (SkTextBlob::kHorizontal_Positioning != positioning
596 || run->offset().y() != offset.y())) {
597 return false;
fmalita00d5c2c2014-08-21 08:53:26 -0700598 }
599
Florin Malitad923a712017-11-22 10:11:12 -0500600 SkSafeMath safe;
601 size_t sizeDelta =
602 SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
603 SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe);
604 if (!safe) {
605 return false;
606 }
607
fmalita3c196de2014-09-20 05:40:22 -0700608 this->reserve(sizeDelta);
fmalita00d5c2c2014-08-21 08:53:26 -0700609
fmalita3c196de2014-09-20 05:40:22 -0700610 // reserve may have realloced
611 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
612 uint32_t preMergeCount = run->glyphCount();
613 run->grow(count);
614
615 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
616 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
617 fCurrentRunBuffer.pos = run->posBuffer()
618 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
619
620 fStorageUsed += sizeDelta;
621
622 SkASSERT(fStorageUsed <= fStorageSize);
623 run->validate(fStorage.get() + fStorageUsed);
624
625 return true;
fmalita00d5c2c2014-08-21 08:53:26 -0700626}
627
628void SkTextBlobBuilder::allocInternal(const SkPaint &font,
629 SkTextBlob::GlyphPositioning positioning,
Florin Malita1e18aa62017-11-19 10:22:22 -0500630 int count, int textSize, SkPoint offset,
631 const SkRect* bounds) {
632 if (count <= 0 || textSize < 0 || font.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
633 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
634 return;
635 }
636
halcanary4f0a23a2016-08-30 11:58:33 -0700637 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
fmalita19653d12014-10-16 11:53:30 -0700638 this->updateDeferredBounds();
fmalita00d5c2c2014-08-21 08:53:26 -0700639
Florin Malitad923a712017-11-22 10:11:12 -0500640 SkSafeMath safe;
641 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
642 if (!safe) {
643 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
644 return;
645 }
646
fmalita3c196de2014-09-20 05:40:22 -0700647 this->reserve(runSize);
fmalita00d5c2c2014-08-21 08:53:26 -0700648
fmalita3c196de2014-09-20 05:40:22 -0700649 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
650 SkASSERT(fStorageUsed + runSize <= fStorageSize);
fmalita00d5c2c2014-08-21 08:53:26 -0700651
fmalita3c196de2014-09-20 05:40:22 -0700652 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
halcanary4f0a23a2016-08-30 11:58:33 -0700653 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
fmalita3c196de2014-09-20 05:40:22 -0700654 fCurrentRunBuffer.glyphs = run->glyphBuffer();
655 fCurrentRunBuffer.pos = run->posBuffer();
halcanary4f0a23a2016-08-30 11:58:33 -0700656 fCurrentRunBuffer.utf8text = run->textBuffer();
657 fCurrentRunBuffer.clusters = run->clusterBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700658
fmalita3c196de2014-09-20 05:40:22 -0700659 fLastRun = fStorageUsed;
660 fStorageUsed += runSize;
661 fRunCount++;
662
663 SkASSERT(fStorageUsed <= fStorageSize);
664 run->validate(fStorage.get() + fStorageUsed);
665 }
halcanary4f0a23a2016-08-30 11:58:33 -0700666 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
667 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
fmalita00d5c2c2014-08-21 08:53:26 -0700668 if (!fDeferredBounds) {
bsalomon49f085d2014-09-05 13:34:00 -0700669 if (bounds) {
fmalita00d5c2c2014-08-21 08:53:26 -0700670 fBounds.join(*bounds);
671 } else {
672 fDeferredBounds = true;
673 }
674 }
675}
676
halcanary4f0a23a2016-08-30 11:58:33 -0700677const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count,
678 SkScalar x, SkScalar y,
679 int textByteCount,
680 SkString lang,
681 const SkRect* bounds) {
682 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700683 return fCurrentRunBuffer;
684}
685
halcanary4f0a23a2016-08-30 11:58:33 -0700686const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
687 SkScalar y,
688 int textByteCount,
689 SkString lang,
690 const SkRect* bounds) {
691 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
fmalita00d5c2c2014-08-21 08:53:26 -0700692 bounds);
693
694 return fCurrentRunBuffer;
695}
696
halcanary4f0a23a2016-08-30 11:58:33 -0700697const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
698 int textByteCount,
699 SkString lang,
700 const SkRect *bounds) {
Ben Wagnerdff47af2017-08-30 16:24:22 -0400701 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700702
703 return fCurrentRunBuffer;
704}
705
reed2ab90572016-08-10 14:16:41 -0700706sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400707 if (!fRunCount) {
708 // We don't instantiate empty blobs.
709 SkASSERT(!fStorage.get());
710 SkASSERT(fStorageUsed == 0);
711 SkASSERT(fStorageSize == 0);
712 SkASSERT(fLastRun == 0);
713 SkASSERT(fBounds.isEmpty());
714 return nullptr;
715 }
fmalita00d5c2c2014-08-21 08:53:26 -0700716
fmalita3c196de2014-09-20 05:40:22 -0700717 this->updateDeferredBounds();
fmalita00d5c2c2014-08-21 08:53:26 -0700718
Florin Malita3a9a7a32017-03-13 09:03:24 -0400719 // Tag the last run as such.
720 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
721 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
fmalita00d5c2c2014-08-21 08:53:26 -0700722
Florin Malita3a9a7a32017-03-13 09:03:24 -0400723 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
fmalita92d976c2015-10-05 11:09:57 -0700724 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
725
fmalita3c196de2014-09-20 05:40:22 -0700726 SkDEBUGCODE(
Florin Malitad923a712017-11-22 10:11:12 -0500727 SkSafeMath safe;
fmalita3c196de2014-09-20 05:40:22 -0700728 size_t validateSize = sizeof(SkTextBlob);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400729 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
730 run = SkTextBlob::RunRecord::Next(run)) {
halcanary4f0a23a2016-08-30 11:58:33 -0700731 validateSize += SkTextBlob::RunRecord::StorageSize(
Florin Malitad923a712017-11-22 10:11:12 -0500732 run->fCount, run->textSize(), run->positioning(), &safe);
fmalita92d976c2015-10-05 11:09:57 -0700733 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400734 fRunCount--;
fmalita3c196de2014-09-20 05:40:22 -0700735 }
736 SkASSERT(validateSize == fStorageUsed);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400737 SkASSERT(fRunCount == 0);
Florin Malitad923a712017-11-22 10:11:12 -0500738 SkASSERT(safe);
fmalita3c196de2014-09-20 05:40:22 -0700739 )
740
fmalita3c196de2014-09-20 05:40:22 -0700741 fStorageUsed = 0;
742 fStorageSize = 0;
743 fRunCount = 0;
744 fLastRun = 0;
745 fBounds.setEmpty();
746
reed2ab90572016-08-10 14:16:41 -0700747 return sk_sp<SkTextBlob>(blob);
fmalita00d5c2c2014-08-21 08:53:26 -0700748}
Mike Reedb99bedd2017-07-11 10:27:40 -0400749
750///////////////////////////////////////////////////////////////////////////////////////////////////
751
752void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
753 buffer.writeRect(fBounds);
754
755 SkPaint runPaint;
756 SkTextBlobRunIterator it(this);
757 while (!it.done()) {
758 SkASSERT(it.glyphCount() > 0);
759
760 buffer.write32(it.glyphCount());
761 PositioningAndExtended pe;
762 pe.intValue = 0;
763 pe.positioning = it.positioning();
764 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
765
766 uint32_t textSize = it.textSize();
767 pe.extended = textSize > 0;
768 buffer.write32(pe.intValue);
769 if (pe.extended) {
770 buffer.write32(textSize);
771 }
772 buffer.writePoint(it.offset());
773 // This should go away when switching to SkFont
774 it.applyFontToPaint(&runPaint);
775 buffer.writePaint(runPaint);
776
777 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
778 buffer.writeByteArray(it.pos(),
779 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
780 if (pe.extended) {
781 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
782 buffer.writeByteArray(it.text(), it.textSize());
783 }
784
785 it.next();
786 }
787
788 // Marker for the last run (0 is not a valid glyph count).
789 buffer.write32(0);
790}
791
792sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
793 const int runCount = reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)
794 ? reader.read32() : std::numeric_limits<int>::max();
795 if (runCount < 0) {
796 return nullptr;
797 }
798
799 SkRect bounds;
800 reader.readRect(&bounds);
801
802 SkTextBlobBuilder blobBuilder;
803 for (int i = 0; i < runCount; ++i) {
804 int glyphCount = reader.read32();
805 if (glyphCount == 0 &&
806 !reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)) {
807 // End-of-runs marker.
808 break;
809 }
810
811 PositioningAndExtended pe;
812 pe.intValue = reader.read32();
813 GlyphPositioning pos = pe.positioning;
814 if (glyphCount <= 0 || pos > kFull_Positioning) {
815 return nullptr;
816 }
Florin Malita1e18aa62017-11-19 10:22:22 -0500817 int textSize = pe.extended ? reader.read32() : 0;
818 if (textSize < 0) {
819 return nullptr;
820 }
Mike Reedb99bedd2017-07-11 10:27:40 -0400821
822 SkPoint offset;
823 reader.readPoint(&offset);
824 SkPaint font;
825 reader.readPaint(&font);
826
Florin Malita1e18aa62017-11-19 10:22:22 -0500827 if (!reader.isValid()) {
828 return nullptr;
829 }
830
Mike Reedb99bedd2017-07-11 10:27:40 -0400831 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
832 switch (pos) {
833 case kDefault_Positioning:
834 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
835 textSize, SkString(), &bounds);
836 break;
837 case kHorizontal_Positioning:
838 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
839 textSize, SkString(), &bounds);
840 break;
841 case kFull_Positioning:
842 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
843 break;
844 default:
845 return nullptr;
846 }
847
Florin Malita90dcafc2017-11-22 10:53:33 -0500848 if (!buf->glyphs ||
849 !buf->pos ||
850 (pe.extended && (!buf->clusters || !buf->utf8text))) {
851 return nullptr;
852 }
853
Mike Reedb99bedd2017-07-11 10:27:40 -0400854 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
855 !reader.readByteArray(buf->pos,
856 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
857 return nullptr;
858 }
859
860 if (pe.extended) {
861 if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) ||
862 !reader.readByteArray(buf->utf8text, textSize)) {
863 return nullptr;
864 }
865 }
866 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400867
Mike Reedb99bedd2017-07-11 10:27:40 -0400868 return blobBuilder.make();
869}
870
871class SkTypefaceCatalogerWriteBuffer : public SkBinaryWriteBuffer {
872public:
Mike Reedaaa30562017-07-21 11:53:23 -0400873 SkTypefaceCatalogerWriteBuffer(SkTypefaceCatalogerProc proc, void* ctx)
Mike Reedb99bedd2017-07-11 10:27:40 -0400874 : SkBinaryWriteBuffer(SkBinaryWriteBuffer::kCrossProcess_Flag)
Mike Reedaaa30562017-07-21 11:53:23 -0400875 , fCatalogerProc(proc)
876 , fCatalogerCtx(ctx)
Mike Reedb99bedd2017-07-11 10:27:40 -0400877 {}
878
879 void writeTypeface(SkTypeface* typeface) override {
Mike Reedaaa30562017-07-21 11:53:23 -0400880 fCatalogerProc(typeface, fCatalogerCtx);
Mike Reedb99bedd2017-07-11 10:27:40 -0400881 this->write32(typeface ? typeface->uniqueID() : 0);
882 }
883
Mike Reedaaa30562017-07-21 11:53:23 -0400884 SkTypefaceCatalogerProc fCatalogerProc;
885 void* fCatalogerCtx;
Mike Reedb99bedd2017-07-11 10:27:40 -0400886};
887
Mike Reedaaa30562017-07-21 11:53:23 -0400888sk_sp<SkData> SkTextBlob::serialize(SkTypefaceCatalogerProc proc, void* ctx) const {
889 SkTypefaceCatalogerWriteBuffer buffer(proc, ctx);
Mike Reedb99bedd2017-07-11 10:27:40 -0400890 this->flatten(buffer);
891
892 size_t total = buffer.bytesWritten();
893 sk_sp<SkData> data = SkData::MakeUninitialized(total);
894 buffer.writeToMemory(data->writable_data());
895 return data;
896}
897
Mike Reede84b4822017-11-15 12:11:01 -0500898class SkTypefaceResolverReadBuffer : public SkValidatingReadBuffer {
Mike Reedb99bedd2017-07-11 10:27:40 -0400899public:
Mike Reedaaa30562017-07-21 11:53:23 -0400900 SkTypefaceResolverReadBuffer(const void* data, size_t size, SkTypefaceResolverProc proc,
901 void* ctx)
Mike Reede84b4822017-11-15 12:11:01 -0500902 : SkValidatingReadBuffer(data, size)
Mike Reedaaa30562017-07-21 11:53:23 -0400903 , fResolverProc(proc)
904 , fResolverCtx(ctx)
Mike Reedb99bedd2017-07-11 10:27:40 -0400905 {}
906
907 sk_sp<SkTypeface> readTypeface() override {
Florin Malita1e18aa62017-11-19 10:22:22 -0500908 auto id = this->readUInt();
909 return this->isValid() ? fResolverProc(id, fResolverCtx) : nullptr;
Mike Reedb99bedd2017-07-11 10:27:40 -0400910 }
911
Mike Reedaaa30562017-07-21 11:53:23 -0400912 SkTypefaceResolverProc fResolverProc;
913 void* fResolverCtx;
Mike Reedb99bedd2017-07-11 10:27:40 -0400914};
915
916sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
Mike Reedaaa30562017-07-21 11:53:23 -0400917 SkTypefaceResolverProc proc, void* ctx) {
918 SkTypefaceResolverReadBuffer buffer(data, length, proc, ctx);
Mike Reedb99bedd2017-07-11 10:27:40 -0400919 return SkTextBlob::MakeFromBuffer(buffer);
920}