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 #include "SkTextBlobRunIterator.h"
9
10 #include "SkReadBuffer.h"
11 #include "SkSafeMath.h"
12 #include "SkTypeface.h"
13 #include "SkWriteBuffer.h"
14
15 #include <limits>
16
17 #if SK_SUPPORT_GPU
18 #include "text/GrTextBlobCache.h"
19 #endif
20
21 namespace {
22
23 // TODO(fmalita): replace with SkFont.
24 class RunFont : SkNoncopyable {
25 public:
RunFont(const SkPaint & paint)26 RunFont(const SkPaint& paint)
27 : fSize(paint.getTextSize())
28 , fScaleX(paint.getTextScaleX())
29 , fTypeface(SkSafeRef(paint.getTypeface()))
30 , fSkewX(paint.getTextSkewX())
31 , fAlign(paint.getTextAlign())
32 , fHinting(paint.getHinting())
33 , fFlags(paint.getFlags() & kFlagsMask) { }
34
applyToPaint(SkPaint * paint) const35 void applyToPaint(SkPaint* paint) const {
36 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
37 paint->setTypeface(fTypeface);
38 paint->setTextSize(fSize);
39 paint->setTextScaleX(fScaleX);
40 paint->setTextSkewX(fSkewX);
41 paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
42 paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
43
44 paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
45 }
46
operator ==(const RunFont & other) const47 bool operator==(const RunFont& other) const {
48 return fTypeface == other.fTypeface
49 && fSize == other.fSize
50 && fScaleX == other.fScaleX
51 && fSkewX == other.fSkewX
52 && fAlign == other.fAlign
53 && fHinting == other.fHinting
54 && fFlags == other.fFlags;
55 }
56
operator !=(const RunFont & other) const57 bool operator!=(const RunFont& other) const {
58 return !(*this == other);
59 }
60
flags() const61 uint32_t flags() const { return fFlags; }
62
63 private:
64 const static uint32_t kFlagsMask =
65 SkPaint::kAntiAlias_Flag |
66 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 |
74 SkPaint::kGenA8FromLCD_Flag;
75
76 SkScalar fSize;
77 SkScalar fScaleX;
78
79 // Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
80 // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
81 sk_sp<SkTypeface> fTypeface;
82 SkScalar fSkewX;
83
84 static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
85 uint32_t fAlign : 2;
86 static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
87 uint32_t fHinting : 2;
88 static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
89 uint32_t fFlags : 16;
90
91 typedef SkNoncopyable INHERITED;
92 };
93
94 struct RunFontStorageEquivalent {
95 SkScalar fSize, fScaleX;
96 void* fTypeface;
97 SkScalar fSkewX;
98 uint32_t fFlags;
99 };
100 static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
101
102 } // anonymous namespace
103
104 //
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.
113 //
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.
123
124 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
125
126 namespace {
127 struct RunRecordStorageEquivalent {
128 RunFont fFont;
129 SkPoint fOffset;
130 uint32_t fCount;
131 uint32_t fFlags;
132 SkDEBUGCODE(unsigned fMagic;)
133 };
134 }
135
136 class SkTextBlob::RunRecord {
137 public:
RunRecord(uint32_t count,uint32_t textSize,const SkPoint & offset,const SkPaint & font,GlyphPositioning pos)138 RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
139 : fFont(font)
140 , fCount(count)
141 , fOffset(offset)
142 , fFlags(pos) {
143 SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
144
145 SkDEBUGCODE(fMagic = kRunRecordMagic);
146 if (textSize > 0) {
147 fFlags |= kExtended_Flag;
148 *this->textSizePtr() = textSize;
149 }
150 }
151
glyphCount() const152 uint32_t glyphCount() const {
153 return fCount;
154 }
155
offset() const156 const SkPoint& offset() const {
157 return fOffset;
158 }
159
font() const160 const RunFont& font() const {
161 return fFont;
162 }
163
positioning() const164 GlyphPositioning positioning() const {
165 return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
166 }
167
glyphBuffer() const168 uint16_t* glyphBuffer() const {
169 static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
170 // Glyphs are stored immediately following the record.
171 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
172 }
173
posBuffer() const174 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
textSize() const180 uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
181
clusterBuffer() const182 uint32_t* clusterBuffer() const {
183 // clusters follow the textSize.
184 return isExtended() ? 1 + this->textSizePtr() : nullptr;
185 }
186
textBuffer() const187 char* textBuffer() const {
188 return isExtended()
189 ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
190 : nullptr;
191 }
192
StorageSize(uint32_t glyphCount,uint32_t textSize,SkTextBlob::GlyphPositioning positioning,SkSafeMath * safe)193 static size_t StorageSize(uint32_t glyphCount, uint32_t textSize,
194 SkTextBlob::GlyphPositioning positioning,
195 SkSafeMath* safe) {
196 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
197
198 auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
199 posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
200
201 // RunRecord object + (aligned) glyph buffer + position buffer
202 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);
210 }
211
212 return safe->alignUp(size, sizeof(void*));
213 }
214
First(const SkTextBlob * blob)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
Next(const RunRecord * run)220 static const RunRecord* Next(const RunRecord* run) {
221 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
222 }
223
validate(const uint8_t * storageTop) const224 void validate(const uint8_t* storageTop) const {
225 SkASSERT(kRunRecordMagic == fMagic);
226 SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
227
228 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
229 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
230 <= (SkScalar*)NextUnchecked(this));
231 if (isExtended()) {
232 SkASSERT(textSize() > 0);
233 SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
234 SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
235 SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
236 }
237 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
238 "runrecord_should_stay_packed");
239 }
240
241 private:
242 friend class SkTextBlobBuilder;
243
244 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
NextUnchecked(const RunRecord * run)250 static const RunRecord* NextUnchecked(const RunRecord* run) {
251 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;
257 }
258
PosCount(uint32_t glyphCount,SkTextBlob::GlyphPositioning positioning,SkSafeMath * safe)259 static size_t PosCount(uint32_t glyphCount,
260 SkTextBlob::GlyphPositioning positioning,
261 SkSafeMath* safe) {
262 return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
263 }
264
textSizePtr() const265 uint32_t* textSizePtr() const {
266 // textSize follows the position buffer.
267 SkASSERT(isExtended());
268 SkSafeMath safe;
269 auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
270 SkASSERT(safe);
271 return res;
272 }
273
grow(uint32_t count)274 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.
280 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
281 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
282
283 // memmove, as the buffers may overlap
284 memmove(posBuffer(), initialPosBuffer, copySize);
285 }
286
isExtended() const287 bool isExtended() const {
288 return fFlags & kExtended_Flag;
289 }
290
291 RunFont fFont;
292 uint32_t fCount;
293 SkPoint fOffset;
294 uint32_t fFlags;
295
296 SkDEBUGCODE(unsigned fMagic;)
297 };
298
299 static int32_t gNextID = 1;
next_id()300 static 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
SkTextBlob(const SkRect & bounds)308 SkTextBlob::SkTextBlob(const SkRect& bounds)
309 : fBounds(bounds)
310 , fUniqueID(next_id())
311 , fCacheID(SK_InvalidUniqueID) {}
312
~SkTextBlob()313 SkTextBlob::~SkTextBlob() {
314 #if SK_SUPPORT_GPU
315 if (SK_InvalidUniqueID != fCacheID.load()) {
316 GrTextBlobCache::PostPurgeBlobMessage(fUniqueID, fCacheID);
317 }
318 #endif
319
320 const auto* run = RunRecord::First(this);
321 do {
322 const auto* nextRun = RunRecord::Next(run);
323 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
324 run->~RunRecord();
325 run = nextRun;
326 } while (run);
327 }
328
329 namespace {
330
331 union PositioningAndExtended {
332 int32_t intValue;
333 struct {
334 SkTextBlob::GlyphPositioning positioning;
335 uint8_t extended;
336 uint16_t padding;
337 };
338 };
339
340 static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
341
342 } // namespace
343
ScalarsPerGlyph(GlyphPositioning pos)344 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
345 // GlyphPositioning values are directly mapped to scalars-per-glyph.
346 SkASSERT(pos <= 2);
347 return pos;
348 }
349
SkTextBlobRunIterator(const SkTextBlob * blob)350 SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
351 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
352 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
353 }
354
done() const355 bool SkTextBlobRunIterator::done() const {
356 return !fCurrentRun;
357 }
358
next()359 void SkTextBlobRunIterator::next() {
360 SkASSERT(!this->done());
361
362 if (!this->done()) {
363 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
364 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
365 }
366 }
367
glyphCount() const368 uint32_t SkTextBlobRunIterator::glyphCount() const {
369 SkASSERT(!this->done());
370 return fCurrentRun->glyphCount();
371 }
372
glyphs() const373 const uint16_t* SkTextBlobRunIterator::glyphs() const {
374 SkASSERT(!this->done());
375 return fCurrentRun->glyphBuffer();
376 }
377
pos() const378 const SkScalar* SkTextBlobRunIterator::pos() const {
379 SkASSERT(!this->done());
380 return fCurrentRun->posBuffer();
381 }
382
offset() const383 const SkPoint& SkTextBlobRunIterator::offset() const {
384 SkASSERT(!this->done());
385 return fCurrentRun->offset();
386 }
387
positioning() const388 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
389 SkASSERT(!this->done());
390 return fCurrentRun->positioning();
391 }
392
applyFontToPaint(SkPaint * paint) const393 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
394 SkASSERT(!this->done());
395
396 fCurrentRun->font().applyToPaint(paint);
397 }
398
clusters() const399 uint32_t* SkTextBlobRunIterator::clusters() const {
400 SkASSERT(!this->done());
401 return fCurrentRun->clusterBuffer();
402 }
textSize() const403 uint32_t SkTextBlobRunIterator::textSize() const {
404 SkASSERT(!this->done());
405 return fCurrentRun->textSize();
406 }
text() const407 char* SkTextBlobRunIterator::text() const {
408 SkASSERT(!this->done());
409 return fCurrentRun->textBuffer();
410 }
411
412
isLCD() const413 bool SkTextBlobRunIterator::isLCD() const {
414 return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
415 }
416
SkTextBlobBuilder()417 SkTextBlobBuilder::SkTextBlobBuilder()
418 : fStorageSize(0)
419 , fStorageUsed(0)
420 , fRunCount(0)
421 , fDeferredBounds(false)
422 , fLastRun(0) {
423 fBounds.setEmpty();
424 }
425
~SkTextBlobBuilder()426 SkTextBlobBuilder::~SkTextBlobBuilder() {
427 if (nullptr != fStorage.get()) {
428 // We are abandoning runs and must destruct the associated font data.
429 // The easiest way to accomplish that is to use the blob destructor.
430 this->make();
431 }
432 }
433
TightRunBounds(const SkTextBlob::RunRecord & run)434 SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
435 SkRect bounds;
436 SkPaint paint;
437 run.font().applyToPaint(&paint);
438
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),
447 nullptr,
448 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));
471
472 return bounds.makeOffset(run.offset().x(), run.offset().y());
473 }
474
ConservativeRunBounds(const SkTextBlob::RunRecord & run)475 SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
476 SkASSERT(run.glyphCount() > 0);
477 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
478 SkTextBlob::kHorizontal_Positioning == run.positioning());
479
480 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.
490 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:
513 SK_ABORT("unsupported positioning mode");
514 }
515
516 // Expand by typeface glyph bounds.
517 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
updateDeferredBounds()526 void SkTextBlobBuilder::updateDeferredBounds() {
527 SkASSERT(!fDeferredBounds || fRunCount > 0);
528
529 if (!fDeferredBounds) {
530 return;
531 }
532
533 SkASSERT(fLastRun >= sizeof(SkTextBlob));
534 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
535 fLastRun);
536
537 // FIXME: we should also use conservative bounds for kDefault_Positioning.
538 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
539 TightRunBounds(*run) : ConservativeRunBounds(*run);
540 fBounds.join(runBounds);
541 fDeferredBounds = false;
542 }
543
reserve(size_t size)544 void SkTextBlobBuilder::reserve(size_t size) {
545 SkSafeMath safe;
546
547 // We don't currently pre-allocate, but maybe someday...
548 if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
549 return;
550 }
551
552 if (0 == fRunCount) {
553 SkASSERT(nullptr == fStorage.get());
554 SkASSERT(0 == fStorageSize);
555 SkASSERT(0 == fStorageUsed);
556
557 // the first allocation also includes blob storage
558 fStorageUsed = sizeof(SkTextBlob);
559 }
560
561 fStorageSize = safe.add(fStorageUsed, size);
562
563 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
564 // Also, this is counting on the underlying realloc to throw when passed max().
565 fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
566 }
567
mergeRun(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,uint32_t count,SkPoint offset)568 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
569 uint32_t count, SkPoint offset) {
570 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
580 if (run->textSize() != 0) {
581 return false;
582 }
583
584 if (run->positioning() != positioning
585 || run->font() != font
586 || (run->glyphCount() + count < run->glyphCount())) {
587 return false;
588 }
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
594 if (SkTextBlob::kFull_Positioning != positioning
595 && (SkTextBlob::kHorizontal_Positioning != positioning
596 || run->offset().y() != offset.y())) {
597 return false;
598 }
599
600 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
608 this->reserve(sizeDelta);
609
610 // 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;
626 }
627
allocInternal(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,int textSize,SkPoint offset,const SkRect * bounds)628 void SkTextBlobBuilder::allocInternal(const SkPaint &font,
629 SkTextBlob::GlyphPositioning positioning,
630 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
637 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
638 this->updateDeferredBounds();
639
640 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
647 this->reserve(runSize);
648
649 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
650 SkASSERT(fStorageUsed + runSize <= fStorageSize);
651
652 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
653 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
654 fCurrentRunBuffer.glyphs = run->glyphBuffer();
655 fCurrentRunBuffer.pos = run->posBuffer();
656 fCurrentRunBuffer.utf8text = run->textBuffer();
657 fCurrentRunBuffer.clusters = run->clusterBuffer();
658
659 fLastRun = fStorageUsed;
660 fStorageUsed += runSize;
661 fRunCount++;
662
663 SkASSERT(fStorageUsed <= fStorageSize);
664 run->validate(fStorage.get() + fStorageUsed);
665 }
666 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
667 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
668 if (!fDeferredBounds) {
669 if (bounds) {
670 fBounds.join(*bounds);
671 } else {
672 fDeferredBounds = true;
673 }
674 }
675 }
676
allocRunText(const SkPaint & font,int count,SkScalar x,SkScalar y,int textByteCount,SkString lang,const SkRect * bounds)677 const 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);
683 return fCurrentRunBuffer;
684 }
685
allocRunTextPosH(const SkPaint & font,int count,SkScalar y,int textByteCount,SkString lang,const SkRect * bounds)686 const 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),
692 bounds);
693
694 return fCurrentRunBuffer;
695 }
696
allocRunTextPos(const SkPaint & font,int count,int textByteCount,SkString lang,const SkRect * bounds)697 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
698 int textByteCount,
699 SkString lang,
700 const SkRect *bounds) {
701 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
702
703 return fCurrentRunBuffer;
704 }
705
make()706 sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
707 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 }
716
717 this->updateDeferredBounds();
718
719 // Tag the last run as such.
720 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
721 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
722
723 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
724 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
725
726 SkDEBUGCODE(
727 SkSafeMath safe;
728 size_t validateSize = sizeof(SkTextBlob);
729 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
730 run = SkTextBlob::RunRecord::Next(run)) {
731 validateSize += SkTextBlob::RunRecord::StorageSize(
732 run->fCount, run->textSize(), run->positioning(), &safe);
733 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
734 fRunCount--;
735 }
736 SkASSERT(validateSize == fStorageUsed);
737 SkASSERT(fRunCount == 0);
738 SkASSERT(safe);
739 )
740
741 fStorageUsed = 0;
742 fStorageSize = 0;
743 fRunCount = 0;
744 fLastRun = 0;
745 fBounds.setEmpty();
746
747 return sk_sp<SkTextBlob>(blob);
748 }
749
750 ///////////////////////////////////////////////////////////////////////////////////////////////////
751
flatten(SkWriteBuffer & buffer) const752 void 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
MakeFromBuffer(SkReadBuffer & reader)792 sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
793 SkRect bounds;
794 reader.readRect(&bounds);
795
796 SkTextBlobBuilder blobBuilder;
797 for (;;) {
798 int glyphCount = reader.read32();
799 if (glyphCount == 0) {
800 // End-of-runs marker.
801 break;
802 }
803
804 PositioningAndExtended pe;
805 pe.intValue = reader.read32();
806 GlyphPositioning pos = pe.positioning;
807 if (glyphCount <= 0 || pos > kFull_Positioning) {
808 return nullptr;
809 }
810 int textSize = pe.extended ? reader.read32() : 0;
811 if (textSize < 0) {
812 return nullptr;
813 }
814
815 SkPoint offset;
816 reader.readPoint(&offset);
817 SkPaint font;
818 reader.readPaint(&font);
819
820 if (!reader.isValid()) {
821 return nullptr;
822 }
823
824 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
825 switch (pos) {
826 case kDefault_Positioning:
827 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
828 textSize, SkString(), &bounds);
829 break;
830 case kHorizontal_Positioning:
831 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
832 textSize, SkString(), &bounds);
833 break;
834 case kFull_Positioning:
835 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
836 break;
837 default:
838 return nullptr;
839 }
840
841 if (!buf->glyphs ||
842 !buf->pos ||
843 (pe.extended && (!buf->clusters || !buf->utf8text))) {
844 return nullptr;
845 }
846
847 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
848 !reader.readByteArray(buf->pos,
849 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
850 return nullptr;
851 }
852
853 if (pe.extended) {
854 if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) ||
855 !reader.readByteArray(buf->utf8text, textSize)) {
856 return nullptr;
857 }
858 }
859 }
860
861 return blobBuilder.make();
862 }
863
serialize(const SkSerialProcs & procs) const864 sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
865 SkBinaryWriteBuffer buffer;
866 buffer.setSerialProcs(procs);
867 this->flatten(buffer);
868
869 size_t total = buffer.bytesWritten();
870 sk_sp<SkData> data = SkData::MakeUninitialized(total);
871 buffer.writeToMemory(data->writable_data());
872 return data;
873 }
874
serialize() const875 sk_sp<SkData> SkTextBlob::serialize() const {
876 return this->serialize(SkSerialProcs());
877 }
878
Deserialize(const void * data,size_t length,const SkDeserialProcs & procs)879 sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
880 const SkDeserialProcs& procs) {
881 SkReadBuffer buffer(data, length);
882 buffer.setDeserialProcs(procs);
883 return SkTextBlob::MakeFromBuffer(buffer);
884 }
885
Deserialize(const void * data,size_t length)886 sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length) {
887 return SkTextBlob::Deserialize(data, length, SkDeserialProcs());
888 }
889
890 ///////////////////////////////////////////////////////////////////////////////////////////////////
891
892 namespace {
893 struct CatalogState {
894 SkTypefaceCatalogerProc fProc;
895 void* fCtx;
896 };
897
catalog_typeface_proc(SkTypeface * face,void * ctx)898 sk_sp<SkData> catalog_typeface_proc(SkTypeface* face, void* ctx) {
899 CatalogState* state = static_cast<CatalogState*>(ctx);
900 state->fProc(face, state->fCtx);
901 uint32_t id = face->uniqueID();
902 return SkData::MakeWithCopy(&id, sizeof(uint32_t));
903 }
904 }
905
serialize(SkTypefaceCatalogerProc proc,void * ctx) const906 sk_sp<SkData> SkTextBlob::serialize(SkTypefaceCatalogerProc proc, void* ctx) const {
907 CatalogState state = { proc, ctx };
908 SkSerialProcs procs;
909 procs.fTypefaceProc = catalog_typeface_proc;
910 procs.fTypefaceCtx = &state;
911 return this->serialize(procs);
912 }
913
914 namespace {
915 struct ResolverState {
916 SkTypefaceResolverProc fProc;
917 void* fCtx;
918 };
919
resolver_typeface_proc(const void * data,size_t length,void * ctx)920 sk_sp<SkTypeface> resolver_typeface_proc(const void* data, size_t length, void* ctx) {
921 if (length != 4) {
922 return nullptr;
923 }
924
925 ResolverState* state = static_cast<ResolverState*>(ctx);
926 uint32_t id;
927 memcpy(&id, data, length);
928 return state->fProc(id, state->fCtx);
929 }
930 }
931
Deserialize(const void * data,size_t length,SkTypefaceResolverProc proc,void * ctx)932 sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
933 SkTypefaceResolverProc proc, void* ctx) {
934 ResolverState state = { proc, ctx };
935 SkDeserialProcs procs;
936 procs.fTypefaceProc = resolver_typeface_proc;
937 procs.fTypefaceCtx = &state;
938 return Deserialize(data, length, procs);
939 }
940