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 "SkTypeface.h"
12 #include "SkWriteBuffer.h"
13
14 #if SK_SUPPORT_GPU
15 #include "text/GrTextBlobCache.h"
16 #endif
17
18 namespace {
19
20 // TODO(fmalita): replace with SkFont.
21 class RunFont : SkNoncopyable {
22 public:
RunFont(const SkPaint & paint)23 RunFont(const SkPaint& paint)
24 : fSize(paint.getTextSize())
25 , fScaleX(paint.getTextScaleX())
26 , fTypeface(SkSafeRef(paint.getTypeface()))
27 , fSkewX(paint.getTextSkewX())
28 , fAlign(paint.getTextAlign())
29 , fHinting(paint.getHinting())
30 , fFlags(paint.getFlags() & kFlagsMask) { }
31
applyToPaint(SkPaint * paint) const32 void applyToPaint(SkPaint* paint) const {
33 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
34 paint->setTypeface(fTypeface);
35 paint->setTextSize(fSize);
36 paint->setTextScaleX(fScaleX);
37 paint->setTextSkewX(fSkewX);
38 paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
39 paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
40
41 paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
42 }
43
operator ==(const RunFont & other) const44 bool operator==(const RunFont& other) const {
45 return fTypeface == other.fTypeface
46 && fSize == other.fSize
47 && fScaleX == other.fScaleX
48 && fSkewX == other.fSkewX
49 && fAlign == other.fAlign
50 && fHinting == other.fHinting
51 && fFlags == other.fFlags;
52 }
53
operator !=(const RunFont & other) const54 bool operator!=(const RunFont& other) const {
55 return !(*this == other);
56 }
57
flags() const58 uint32_t flags() const { return fFlags; }
59
60 private:
61 const static uint32_t kFlagsMask =
62 SkPaint::kAntiAlias_Flag |
63 SkPaint::kFakeBoldText_Flag |
64 SkPaint::kLinearText_Flag |
65 SkPaint::kSubpixelText_Flag |
66 SkPaint::kDevKernText_Flag |
67 SkPaint::kLCDRenderText_Flag |
68 SkPaint::kEmbeddedBitmapText_Flag |
69 SkPaint::kAutoHinting_Flag |
70 SkPaint::kVerticalText_Flag |
71 SkPaint::kGenA8FromLCD_Flag;
72
73 SkScalar fSize;
74 SkScalar fScaleX;
75
76 // Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
77 // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
78 sk_sp<SkTypeface> fTypeface;
79 SkScalar fSkewX;
80
81 static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
82 uint32_t fAlign : 2;
83 static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
84 uint32_t fHinting : 2;
85 static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
86 uint32_t fFlags : 16;
87
88 typedef SkNoncopyable INHERITED;
89 };
90
91 struct RunFontStorageEquivalent {
92 SkScalar fSize, fScaleX;
93 void* fTypeface;
94 SkScalar fSkewX;
95 uint32_t fFlags;
96 };
97 static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
98
99 } // anonymous namespace
100
101 //
102 // Textblob data is laid out into externally-managed storage as follows:
103 //
104 // -----------------------------------------------------------------------------
105 // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
106 // -----------------------------------------------------------------------------
107 //
108 // Each run record describes a text blob run, and can be used to determine the (implicit)
109 // location of the following record.
110 //
111 // Extended Textblob runs have more data after the Pos[] array:
112 //
113 // -------------------------------------------------------------------------
114 // ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
115 // -------------------------------------------------------------------------
116 //
117 // To determine the length of the extended run data, the TextSize must be read.
118 //
119 // Extended Textblob runs may be mixed with non-extended runs.
120
121 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
122
123 namespace {
124 struct RunRecordStorageEquivalent {
125 RunFont fFont;
126 SkPoint fOffset;
127 uint32_t fCount;
128 uint32_t fFlags;
129 SkDEBUGCODE(unsigned fMagic;)
130 };
131 }
132
133 class SkTextBlob::RunRecord {
134 public:
RunRecord(uint32_t count,uint32_t textSize,const SkPoint & offset,const SkPaint & font,GlyphPositioning pos)135 RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
136 : fFont(font)
137 , fCount(count)
138 , fOffset(offset)
139 , fFlags(pos) {
140 SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
141
142 SkDEBUGCODE(fMagic = kRunRecordMagic);
143 if (textSize > 0) {
144 fFlags |= kExtended_Flag;
145 *this->textSizePtr() = textSize;
146 }
147 }
148
glyphCount() const149 uint32_t glyphCount() const {
150 return fCount;
151 }
152
offset() const153 const SkPoint& offset() const {
154 return fOffset;
155 }
156
font() const157 const RunFont& font() const {
158 return fFont;
159 }
160
positioning() const161 GlyphPositioning positioning() const {
162 return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
163 }
164
glyphBuffer() const165 uint16_t* glyphBuffer() const {
166 static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
167 // Glyphs are stored immediately following the record.
168 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
169 }
170
posBuffer() const171 SkScalar* posBuffer() const {
172 // Position scalars follow the (aligned) glyph buffer.
173 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
174 SkAlign4(fCount * sizeof(uint16_t)));
175 }
176
textSize() const177 uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
178
clusterBuffer() const179 uint32_t* clusterBuffer() const {
180 // clusters follow the textSize.
181 return isExtended() ? 1 + this->textSizePtr() : nullptr;
182 }
183
textBuffer() const184 char* textBuffer() const {
185 return isExtended()
186 ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
187 : nullptr;
188 }
189
StorageSize(int glyphCount,int textSize,SkTextBlob::GlyphPositioning positioning)190 static size_t StorageSize(int glyphCount, int textSize,
191 SkTextBlob::GlyphPositioning positioning) {
192 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
193 // RunRecord object + (aligned) glyph buffer + position buffer
194 size_t size = sizeof(SkTextBlob::RunRecord)
195 + SkAlign4(glyphCount* sizeof(uint16_t))
196 + PosCount(glyphCount, positioning) * sizeof(SkScalar);
197 if (textSize > 0) { // Extended run.
198 size += sizeof(uint32_t)
199 + sizeof(uint32_t) * glyphCount
200 + textSize;
201 }
202 return SkAlignPtr(size);
203 }
204
First(const SkTextBlob * blob)205 static const RunRecord* First(const SkTextBlob* blob) {
206 // The first record (if present) is stored following the blob object.
207 return reinterpret_cast<const RunRecord*>(blob + 1);
208 }
209
Next(const RunRecord * run)210 static const RunRecord* Next(const RunRecord* run) {
211 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
212 }
213
validate(const uint8_t * storageTop) const214 void validate(const uint8_t* storageTop) const {
215 SkASSERT(kRunRecordMagic == fMagic);
216 SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
217
218 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
219 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
220 <= (SkScalar*)NextUnchecked(this));
221 if (isExtended()) {
222 SkASSERT(textSize() > 0);
223 SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
224 SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
225 SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
226 }
227 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
228 "runrecord_should_stay_packed");
229 }
230
231 private:
232 friend class SkTextBlobBuilder;
233
234 enum Flags {
235 kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning
236 kLast_Flag = 0x04, // set for the last blob run
237 kExtended_Flag = 0x08, // set for runs with text/cluster info
238 };
239
NextUnchecked(const RunRecord * run)240 static const RunRecord* NextUnchecked(const RunRecord* run) {
241 return reinterpret_cast<const RunRecord*>(
242 reinterpret_cast<const uint8_t*>(run)
243 + StorageSize(run->glyphCount(), run->textSize(), run->positioning()));
244 }
245
PosCount(int glyphCount,SkTextBlob::GlyphPositioning positioning)246 static size_t PosCount(int glyphCount,
247 SkTextBlob::GlyphPositioning positioning) {
248 return glyphCount * ScalarsPerGlyph(positioning);
249 }
250
textSizePtr() const251 uint32_t* textSizePtr() const {
252 // textSize follows the position buffer.
253 SkASSERT(isExtended());
254 return (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning())]);
255 }
256
grow(uint32_t count)257 void grow(uint32_t count) {
258 SkScalar* initialPosBuffer = posBuffer();
259 uint32_t initialCount = fCount;
260 fCount += count;
261
262 // Move the initial pos scalars to their new location.
263 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
264 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
265
266 // memmove, as the buffers may overlap
267 memmove(posBuffer(), initialPosBuffer, copySize);
268 }
269
isExtended() const270 bool isExtended() const {
271 return fFlags & kExtended_Flag;
272 }
273
274 RunFont fFont;
275 uint32_t fCount;
276 SkPoint fOffset;
277 uint32_t fFlags;
278
279 SkDEBUGCODE(unsigned fMagic;)
280 };
281
282 static int32_t gNextID = 1;
next_id()283 static int32_t next_id() {
284 int32_t id;
285 do {
286 id = sk_atomic_inc(&gNextID);
287 } while (id == SK_InvalidGenID);
288 return id;
289 }
290
SkTextBlob(const SkRect & bounds)291 SkTextBlob::SkTextBlob(const SkRect& bounds)
292 : fBounds(bounds)
293 , fUniqueID(next_id())
294 , fAddedToCache(false) {}
295
~SkTextBlob()296 SkTextBlob::~SkTextBlob() {
297 #if SK_SUPPORT_GPU
298 if (fAddedToCache.load()) {
299 GrTextBlobCache::PostPurgeBlobMessage(fUniqueID);
300 }
301 #endif
302
303 const auto* run = RunRecord::First(this);
304 do {
305 const auto* nextRun = RunRecord::Next(run);
306 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
307 run->~RunRecord();
308 run = nextRun;
309 } while (run);
310 }
311
312 namespace {
313 union PositioningAndExtended {
314 int32_t intValue;
315 struct {
316 SkTextBlob::GlyphPositioning positioning;
317 bool extended;
318 uint16_t padding;
319 };
320 };
321 } // namespace
322
ScalarsPerGlyph(GlyphPositioning pos)323 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
324 // GlyphPositioning values are directly mapped to scalars-per-glyph.
325 SkASSERT(pos <= 2);
326 return pos;
327 }
328
SkTextBlobRunIterator(const SkTextBlob * blob)329 SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
330 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
331 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
332 }
333
done() const334 bool SkTextBlobRunIterator::done() const {
335 return !fCurrentRun;
336 }
337
next()338 void SkTextBlobRunIterator::next() {
339 SkASSERT(!this->done());
340
341 if (!this->done()) {
342 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
343 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
344 }
345 }
346
glyphCount() const347 uint32_t SkTextBlobRunIterator::glyphCount() const {
348 SkASSERT(!this->done());
349 return fCurrentRun->glyphCount();
350 }
351
glyphs() const352 const uint16_t* SkTextBlobRunIterator::glyphs() const {
353 SkASSERT(!this->done());
354 return fCurrentRun->glyphBuffer();
355 }
356
pos() const357 const SkScalar* SkTextBlobRunIterator::pos() const {
358 SkASSERT(!this->done());
359 return fCurrentRun->posBuffer();
360 }
361
offset() const362 const SkPoint& SkTextBlobRunIterator::offset() const {
363 SkASSERT(!this->done());
364 return fCurrentRun->offset();
365 }
366
positioning() const367 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
368 SkASSERT(!this->done());
369 return fCurrentRun->positioning();
370 }
371
applyFontToPaint(SkPaint * paint) const372 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
373 SkASSERT(!this->done());
374
375 fCurrentRun->font().applyToPaint(paint);
376 }
377
clusters() const378 uint32_t* SkTextBlobRunIterator::clusters() const {
379 SkASSERT(!this->done());
380 return fCurrentRun->clusterBuffer();
381 }
textSize() const382 uint32_t SkTextBlobRunIterator::textSize() const {
383 SkASSERT(!this->done());
384 return fCurrentRun->textSize();
385 }
text() const386 char* SkTextBlobRunIterator::text() const {
387 SkASSERT(!this->done());
388 return fCurrentRun->textBuffer();
389 }
390
391
isLCD() const392 bool SkTextBlobRunIterator::isLCD() const {
393 return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
394 }
395
SkTextBlobBuilder()396 SkTextBlobBuilder::SkTextBlobBuilder()
397 : fStorageSize(0)
398 , fStorageUsed(0)
399 , fRunCount(0)
400 , fDeferredBounds(false)
401 , fLastRun(0) {
402 fBounds.setEmpty();
403 }
404
~SkTextBlobBuilder()405 SkTextBlobBuilder::~SkTextBlobBuilder() {
406 if (nullptr != fStorage.get()) {
407 // We are abandoning runs and must destruct the associated font data.
408 // The easiest way to accomplish that is to use the blob destructor.
409 this->make();
410 }
411 }
412
TightRunBounds(const SkTextBlob::RunRecord & run)413 SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
414 SkRect bounds;
415 SkPaint paint;
416 run.font().applyToPaint(&paint);
417
418 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
419 paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
420 return bounds.makeOffset(run.offset().x(), run.offset().y());
421 }
422
423 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
424 paint.getTextWidths(run.glyphBuffer(),
425 run.glyphCount() * sizeof(uint16_t),
426 NULL,
427 glyphBounds.get());
428
429 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
430 SkTextBlob::kHorizontal_Positioning == run.positioning());
431 // kFull_Positioning => [ x, y, x, y... ]
432 // kHorizontal_Positioning => [ x, x, x... ]
433 // (const y applied by runBounds.offset(run->offset()) later)
434 const SkScalar horizontalConstY = 0;
435 const SkScalar* glyphPosX = run.posBuffer();
436 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
437 glyphPosX + 1 : &horizontalConstY;
438 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
439 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
440 posXInc : 0;
441
442 bounds.setEmpty();
443 for (unsigned i = 0; i < run.glyphCount(); ++i) {
444 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
445 glyphPosX += posXInc;
446 glyphPosY += posYInc;
447 }
448
449 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
450
451 return bounds.makeOffset(run.offset().x(), run.offset().y());
452 }
453
ConservativeRunBounds(const SkTextBlob::RunRecord & run)454 SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
455 SkASSERT(run.glyphCount() > 0);
456 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
457 SkTextBlob::kHorizontal_Positioning == run.positioning());
458
459 SkPaint paint;
460 run.font().applyToPaint(&paint);
461 const SkRect fontBounds = paint.getFontBounds();
462 if (fontBounds.isEmpty()) {
463 // Empty font bounds are likely a font bug. TightBounds has a better chance of
464 // producing useful results in this case.
465 return TightRunBounds(run);
466 }
467
468 // Compute the glyph position bbox.
469 SkRect bounds;
470 switch (run.positioning()) {
471 case SkTextBlob::kHorizontal_Positioning: {
472 const SkScalar* glyphPos = run.posBuffer();
473 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
474
475 SkScalar minX = *glyphPos;
476 SkScalar maxX = *glyphPos;
477 for (unsigned i = 1; i < run.glyphCount(); ++i) {
478 SkScalar x = glyphPos[i];
479 minX = SkMinScalar(x, minX);
480 maxX = SkMaxScalar(x, maxX);
481 }
482
483 bounds.setLTRB(minX, 0, maxX, 0);
484 } break;
485 case SkTextBlob::kFull_Positioning: {
486 const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
487 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
488
489 bounds.setBounds(glyphPosPts, run.glyphCount());
490 } break;
491 default:
492 SkFAIL("unsupported positioning mode");
493 }
494
495 // Expand by typeface glyph bounds.
496 bounds.fLeft += fontBounds.left();
497 bounds.fTop += fontBounds.top();
498 bounds.fRight += fontBounds.right();
499 bounds.fBottom += fontBounds.bottom();
500
501 // Offset by run position.
502 return bounds.makeOffset(run.offset().x(), run.offset().y());
503 }
504
updateDeferredBounds()505 void SkTextBlobBuilder::updateDeferredBounds() {
506 SkASSERT(!fDeferredBounds || fRunCount > 0);
507
508 if (!fDeferredBounds) {
509 return;
510 }
511
512 SkASSERT(fLastRun >= sizeof(SkTextBlob));
513 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
514 fLastRun);
515
516 // FIXME: we should also use conservative bounds for kDefault_Positioning.
517 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
518 TightRunBounds(*run) : ConservativeRunBounds(*run);
519 fBounds.join(runBounds);
520 fDeferredBounds = false;
521 }
522
reserve(size_t size)523 void SkTextBlobBuilder::reserve(size_t size) {
524 // We don't currently pre-allocate, but maybe someday...
525 if (fStorageUsed + size <= fStorageSize) {
526 return;
527 }
528
529 if (0 == fRunCount) {
530 SkASSERT(nullptr == fStorage.get());
531 SkASSERT(0 == fStorageSize);
532 SkASSERT(0 == fStorageUsed);
533
534 // the first allocation also includes blob storage
535 fStorageUsed += sizeof(SkTextBlob);
536 }
537
538 fStorageSize = fStorageUsed + size;
539 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
540 fStorage.realloc(fStorageSize);
541 }
542
mergeRun(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,SkPoint offset)543 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
544 int count, SkPoint offset) {
545 if (0 == fLastRun) {
546 SkASSERT(0 == fRunCount);
547 return false;
548 }
549
550 SkASSERT(fLastRun >= sizeof(SkTextBlob));
551 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
552 fLastRun);
553 SkASSERT(run->glyphCount() > 0);
554
555 if (run->textSize() != 0) {
556 return false;
557 }
558
559 if (run->positioning() != positioning
560 || run->font() != font
561 || (run->glyphCount() + count < run->glyphCount())) {
562 return false;
563 }
564
565 // we can merge same-font/same-positioning runs in the following cases:
566 // * fully positioned run following another fully positioned run
567 // * horizontally postioned run following another horizontally positioned run with the same
568 // y-offset
569 if (SkTextBlob::kFull_Positioning != positioning
570 && (SkTextBlob::kHorizontal_Positioning != positioning
571 || run->offset().y() != offset.y())) {
572 return false;
573 }
574
575 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) -
576 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning);
577 this->reserve(sizeDelta);
578
579 // reserve may have realloced
580 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
581 uint32_t preMergeCount = run->glyphCount();
582 run->grow(count);
583
584 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
585 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
586 fCurrentRunBuffer.pos = run->posBuffer()
587 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
588
589 fStorageUsed += sizeDelta;
590
591 SkASSERT(fStorageUsed <= fStorageSize);
592 run->validate(fStorage.get() + fStorageUsed);
593
594 return true;
595 }
596
allocInternal(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,int textSize,SkPoint offset,const SkRect * bounds)597 void SkTextBlobBuilder::allocInternal(const SkPaint &font,
598 SkTextBlob::GlyphPositioning positioning,
599 int count, int textSize, SkPoint offset, const SkRect* bounds) {
600 SkASSERT(count > 0);
601 SkASSERT(textSize >= 0);
602 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
603 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
604 this->updateDeferredBounds();
605
606 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning);
607 this->reserve(runSize);
608
609 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
610 SkASSERT(fStorageUsed + runSize <= fStorageSize);
611
612 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
613 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
614 fCurrentRunBuffer.glyphs = run->glyphBuffer();
615 fCurrentRunBuffer.pos = run->posBuffer();
616 fCurrentRunBuffer.utf8text = run->textBuffer();
617 fCurrentRunBuffer.clusters = run->clusterBuffer();
618
619 fLastRun = fStorageUsed;
620 fStorageUsed += runSize;
621 fRunCount++;
622
623 SkASSERT(fStorageUsed <= fStorageSize);
624 run->validate(fStorage.get() + fStorageUsed);
625 }
626 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
627 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
628 if (!fDeferredBounds) {
629 if (bounds) {
630 fBounds.join(*bounds);
631 } else {
632 fDeferredBounds = true;
633 }
634 }
635 }
636
allocRunText(const SkPaint & font,int count,SkScalar x,SkScalar y,int textByteCount,SkString lang,const SkRect * bounds)637 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count,
638 SkScalar x, SkScalar y,
639 int textByteCount,
640 SkString lang,
641 const SkRect* bounds) {
642 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
643 return fCurrentRunBuffer;
644 }
645
allocRunTextPosH(const SkPaint & font,int count,SkScalar y,int textByteCount,SkString lang,const SkRect * bounds)646 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
647 SkScalar y,
648 int textByteCount,
649 SkString lang,
650 const SkRect* bounds) {
651 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
652 bounds);
653
654 return fCurrentRunBuffer;
655 }
656
allocRunTextPos(const SkPaint & font,int count,int textByteCount,SkString lang,const SkRect * bounds)657 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
658 int textByteCount,
659 SkString lang,
660 const SkRect *bounds) {
661 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
662
663 return fCurrentRunBuffer;
664 }
665
make()666 sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
667 if (!fRunCount) {
668 // We don't instantiate empty blobs.
669 SkASSERT(!fStorage.get());
670 SkASSERT(fStorageUsed == 0);
671 SkASSERT(fStorageSize == 0);
672 SkASSERT(fLastRun == 0);
673 SkASSERT(fBounds.isEmpty());
674 return nullptr;
675 }
676
677 this->updateDeferredBounds();
678
679 // Tag the last run as such.
680 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
681 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
682
683 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
684 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
685
686 SkDEBUGCODE(
687 size_t validateSize = sizeof(SkTextBlob);
688 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
689 run = SkTextBlob::RunRecord::Next(run)) {
690 validateSize += SkTextBlob::RunRecord::StorageSize(
691 run->fCount, run->textSize(), run->positioning());
692 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
693 fRunCount--;
694 }
695 SkASSERT(validateSize == fStorageUsed);
696 SkASSERT(fRunCount == 0);
697 )
698
699 fStorageUsed = 0;
700 fStorageSize = 0;
701 fRunCount = 0;
702 fLastRun = 0;
703 fBounds.setEmpty();
704
705 return sk_sp<SkTextBlob>(blob);
706 }
707
708 ///////////////////////////////////////////////////////////////////////////////////////////////////
709
flatten(SkWriteBuffer & buffer) const710 void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
711 buffer.writeRect(fBounds);
712
713 SkPaint runPaint;
714 SkTextBlobRunIterator it(this);
715 while (!it.done()) {
716 SkASSERT(it.glyphCount() > 0);
717
718 buffer.write32(it.glyphCount());
719 PositioningAndExtended pe;
720 pe.intValue = 0;
721 pe.positioning = it.positioning();
722 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
723
724 uint32_t textSize = it.textSize();
725 pe.extended = textSize > 0;
726 buffer.write32(pe.intValue);
727 if (pe.extended) {
728 buffer.write32(textSize);
729 }
730 buffer.writePoint(it.offset());
731 // This should go away when switching to SkFont
732 it.applyFontToPaint(&runPaint);
733 buffer.writePaint(runPaint);
734
735 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
736 buffer.writeByteArray(it.pos(),
737 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
738 if (pe.extended) {
739 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
740 buffer.writeByteArray(it.text(), it.textSize());
741 }
742
743 it.next();
744 }
745
746 // Marker for the last run (0 is not a valid glyph count).
747 buffer.write32(0);
748 }
749
MakeFromBuffer(SkReadBuffer & reader)750 sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
751 const int runCount = reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)
752 ? reader.read32() : std::numeric_limits<int>::max();
753 if (runCount < 0) {
754 return nullptr;
755 }
756
757 SkRect bounds;
758 reader.readRect(&bounds);
759
760 SkTextBlobBuilder blobBuilder;
761 for (int i = 0; i < runCount; ++i) {
762 int glyphCount = reader.read32();
763 if (glyphCount == 0 &&
764 !reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)) {
765 // End-of-runs marker.
766 break;
767 }
768
769 PositioningAndExtended pe;
770 pe.intValue = reader.read32();
771 GlyphPositioning pos = pe.positioning;
772 if (glyphCount <= 0 || pos > kFull_Positioning) {
773 return nullptr;
774 }
775 uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0;
776
777 SkPoint offset;
778 reader.readPoint(&offset);
779 SkPaint font;
780 reader.readPaint(&font);
781
782 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
783 switch (pos) {
784 case kDefault_Positioning:
785 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
786 textSize, SkString(), &bounds);
787 break;
788 case kHorizontal_Positioning:
789 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
790 textSize, SkString(), &bounds);
791 break;
792 case kFull_Positioning:
793 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
794 break;
795 default:
796 return nullptr;
797 }
798
799 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
800 !reader.readByteArray(buf->pos,
801 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
802 return nullptr;
803 }
804
805 if (pe.extended) {
806 if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) ||
807 !reader.readByteArray(buf->utf8text, textSize)) {
808 return nullptr;
809 }
810 }
811 }
812
813 return blobBuilder.make();
814 }
815
816 class SkTypefaceCatalogerWriteBuffer : public SkBinaryWriteBuffer {
817 public:
SkTypefaceCatalogerWriteBuffer(const SkTypefaceCataloger & cataloger)818 SkTypefaceCatalogerWriteBuffer(const SkTypefaceCataloger& cataloger)
819 : SkBinaryWriteBuffer(SkBinaryWriteBuffer::kCrossProcess_Flag)
820 , fCataloger(cataloger)
821 {}
822
writeTypeface(SkTypeface * typeface)823 void writeTypeface(SkTypeface* typeface) override {
824 fCataloger(typeface);
825 this->write32(typeface ? typeface->uniqueID() : 0);
826 }
827
828 const SkTypefaceCataloger& fCataloger;
829 };
830
serialize(const SkTypefaceCataloger & cataloger) const831 sk_sp<SkData> SkTextBlob::serialize(const SkTypefaceCataloger& cataloger) const {
832 SkTypefaceCatalogerWriteBuffer buffer(cataloger);
833 this->flatten(buffer);
834
835 size_t total = buffer.bytesWritten();
836 sk_sp<SkData> data = SkData::MakeUninitialized(total);
837 buffer.writeToMemory(data->writable_data());
838 return data;
839 }
840
841 class SkTypefaceResolverReadBuffer : public SkReadBuffer {
842 public:
SkTypefaceResolverReadBuffer(const void * data,size_t size,const SkTypefaceResolver & resolver)843 SkTypefaceResolverReadBuffer(const void* data, size_t size, const SkTypefaceResolver& resolver)
844 : SkReadBuffer(data, size)
845 , fResolver(resolver)
846 {}
847
readTypeface()848 sk_sp<SkTypeface> readTypeface() override {
849 return fResolver(this->read32());
850 }
851
852 const SkTypefaceResolver& fResolver;
853 };
854
Deserialize(const void * data,size_t length,const SkTypefaceResolver & resolver)855 sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
856 const SkTypefaceResolver& resolver) {
857 SkTypefaceResolverReadBuffer buffer(data, length, resolver);
858 return SkTextBlob::MakeFromBuffer(buffer);
859 }
860