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
flatten(SkWriteBuffer & buffer) const323 void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
324 buffer.writeRect(fBounds);
325
326 SkPaint runPaint;
327 SkTextBlobRunIterator it(this);
328 while (!it.done()) {
329 SkASSERT(it.glyphCount() > 0);
330
331 buffer.write32(it.glyphCount());
332 PositioningAndExtended pe;
333 pe.intValue = 0;
334 pe.positioning = it.positioning();
335 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
336
337 uint32_t textSize = it.textSize();
338 pe.extended = textSize > 0;
339 buffer.write32(pe.intValue);
340 if (pe.extended) {
341 buffer.write32(textSize);
342 }
343 buffer.writePoint(it.offset());
344 // This should go away when switching to SkFont
345 it.applyFontToPaint(&runPaint);
346 buffer.writePaint(runPaint);
347
348 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
349 buffer.writeByteArray(it.pos(),
350 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
351 if (pe.extended) {
352 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
353 buffer.writeByteArray(it.text(), it.textSize());
354 }
355
356 it.next();
357 }
358
359 // Marker for the last run (0 is not a valid glyph count).
360 buffer.write32(0);
361 }
362
MakeFromBuffer(SkReadBuffer & reader)363 sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
364 const int runCount = reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)
365 ? reader.read32() : std::numeric_limits<int>::max();
366 if (runCount < 0) {
367 return nullptr;
368 }
369
370 SkRect bounds;
371 reader.readRect(&bounds);
372
373 SkTextBlobBuilder blobBuilder;
374 for (int i = 0; i < runCount; ++i) {
375 int glyphCount = reader.read32();
376 if (glyphCount == 0 &&
377 !reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)) {
378 // End-of-runs marker.
379 break;
380 }
381
382 PositioningAndExtended pe;
383 pe.intValue = reader.read32();
384 GlyphPositioning pos = pe.positioning;
385 if (glyphCount <= 0 || pos > kFull_Positioning) {
386 return nullptr;
387 }
388 uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0;
389
390 SkPoint offset;
391 reader.readPoint(&offset);
392 SkPaint font;
393 reader.readPaint(&font);
394
395 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
396 switch (pos) {
397 case kDefault_Positioning:
398 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
399 textSize, SkString(), &bounds);
400 break;
401 case kHorizontal_Positioning:
402 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
403 textSize, SkString(), &bounds);
404 break;
405 case kFull_Positioning:
406 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
407 break;
408 default:
409 return nullptr;
410 }
411
412 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
413 !reader.readByteArray(buf->pos,
414 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
415 return nullptr;
416 }
417
418 if (pe.extended) {
419 if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) ||
420 !reader.readByteArray(buf->utf8text, textSize)) {
421 return nullptr;
422 }
423 }
424 }
425
426 return blobBuilder.make();
427 }
428
ScalarsPerGlyph(GlyphPositioning pos)429 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
430 // GlyphPositioning values are directly mapped to scalars-per-glyph.
431 SkASSERT(pos <= 2);
432 return pos;
433 }
434
SkTextBlobRunIterator(const SkTextBlob * blob)435 SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
436 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
437 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
438 }
439
done() const440 bool SkTextBlobRunIterator::done() const {
441 return !fCurrentRun;
442 }
443
next()444 void SkTextBlobRunIterator::next() {
445 SkASSERT(!this->done());
446
447 if (!this->done()) {
448 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
449 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
450 }
451 }
452
glyphCount() const453 uint32_t SkTextBlobRunIterator::glyphCount() const {
454 SkASSERT(!this->done());
455 return fCurrentRun->glyphCount();
456 }
457
glyphs() const458 const uint16_t* SkTextBlobRunIterator::glyphs() const {
459 SkASSERT(!this->done());
460 return fCurrentRun->glyphBuffer();
461 }
462
pos() const463 const SkScalar* SkTextBlobRunIterator::pos() const {
464 SkASSERT(!this->done());
465 return fCurrentRun->posBuffer();
466 }
467
offset() const468 const SkPoint& SkTextBlobRunIterator::offset() const {
469 SkASSERT(!this->done());
470 return fCurrentRun->offset();
471 }
472
positioning() const473 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
474 SkASSERT(!this->done());
475 return fCurrentRun->positioning();
476 }
477
applyFontToPaint(SkPaint * paint) const478 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
479 SkASSERT(!this->done());
480
481 fCurrentRun->font().applyToPaint(paint);
482 }
483
clusters() const484 uint32_t* SkTextBlobRunIterator::clusters() const {
485 SkASSERT(!this->done());
486 return fCurrentRun->clusterBuffer();
487 }
textSize() const488 uint32_t SkTextBlobRunIterator::textSize() const {
489 SkASSERT(!this->done());
490 return fCurrentRun->textSize();
491 }
text() const492 char* SkTextBlobRunIterator::text() const {
493 SkASSERT(!this->done());
494 return fCurrentRun->textBuffer();
495 }
496
497
isLCD() const498 bool SkTextBlobRunIterator::isLCD() const {
499 return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
500 }
501
SkTextBlobBuilder()502 SkTextBlobBuilder::SkTextBlobBuilder()
503 : fStorageSize(0)
504 , fStorageUsed(0)
505 , fRunCount(0)
506 , fDeferredBounds(false)
507 , fLastRun(0) {
508 fBounds.setEmpty();
509 }
510
~SkTextBlobBuilder()511 SkTextBlobBuilder::~SkTextBlobBuilder() {
512 if (nullptr != fStorage.get()) {
513 // We are abandoning runs and must destruct the associated font data.
514 // The easiest way to accomplish that is to use the blob destructor.
515 this->make();
516 }
517 }
518
TightRunBounds(const SkTextBlob::RunRecord & run)519 SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
520 SkRect bounds;
521 SkPaint paint;
522 run.font().applyToPaint(&paint);
523
524 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
525 paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
526 return bounds.makeOffset(run.offset().x(), run.offset().y());
527 }
528
529 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
530 paint.getTextWidths(run.glyphBuffer(),
531 run.glyphCount() * sizeof(uint16_t),
532 NULL,
533 glyphBounds.get());
534
535 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
536 SkTextBlob::kHorizontal_Positioning == run.positioning());
537 // kFull_Positioning => [ x, y, x, y... ]
538 // kHorizontal_Positioning => [ x, x, x... ]
539 // (const y applied by runBounds.offset(run->offset()) later)
540 const SkScalar horizontalConstY = 0;
541 const SkScalar* glyphPosX = run.posBuffer();
542 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
543 glyphPosX + 1 : &horizontalConstY;
544 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
545 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
546 posXInc : 0;
547
548 bounds.setEmpty();
549 for (unsigned i = 0; i < run.glyphCount(); ++i) {
550 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
551 glyphPosX += posXInc;
552 glyphPosY += posYInc;
553 }
554
555 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
556
557 return bounds.makeOffset(run.offset().x(), run.offset().y());
558 }
559
ConservativeRunBounds(const SkTextBlob::RunRecord & run)560 SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
561 SkASSERT(run.glyphCount() > 0);
562 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
563 SkTextBlob::kHorizontal_Positioning == run.positioning());
564
565 SkPaint paint;
566 run.font().applyToPaint(&paint);
567 const SkRect fontBounds = paint.getFontBounds();
568 if (fontBounds.isEmpty()) {
569 // Empty font bounds are likely a font bug. TightBounds has a better chance of
570 // producing useful results in this case.
571 return TightRunBounds(run);
572 }
573
574 // Compute the glyph position bbox.
575 SkRect bounds;
576 switch (run.positioning()) {
577 case SkTextBlob::kHorizontal_Positioning: {
578 const SkScalar* glyphPos = run.posBuffer();
579 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
580
581 SkScalar minX = *glyphPos;
582 SkScalar maxX = *glyphPos;
583 for (unsigned i = 1; i < run.glyphCount(); ++i) {
584 SkScalar x = glyphPos[i];
585 minX = SkMinScalar(x, minX);
586 maxX = SkMaxScalar(x, maxX);
587 }
588
589 bounds.setLTRB(minX, 0, maxX, 0);
590 } break;
591 case SkTextBlob::kFull_Positioning: {
592 const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
593 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
594
595 bounds.setBounds(glyphPosPts, run.glyphCount());
596 } break;
597 default:
598 SkFAIL("unsupported positioning mode");
599 }
600
601 // Expand by typeface glyph bounds.
602 bounds.fLeft += fontBounds.left();
603 bounds.fTop += fontBounds.top();
604 bounds.fRight += fontBounds.right();
605 bounds.fBottom += fontBounds.bottom();
606
607 // Offset by run position.
608 return bounds.makeOffset(run.offset().x(), run.offset().y());
609 }
610
updateDeferredBounds()611 void SkTextBlobBuilder::updateDeferredBounds() {
612 SkASSERT(!fDeferredBounds || fRunCount > 0);
613
614 if (!fDeferredBounds) {
615 return;
616 }
617
618 SkASSERT(fLastRun >= sizeof(SkTextBlob));
619 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
620 fLastRun);
621
622 // FIXME: we should also use conservative bounds for kDefault_Positioning.
623 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
624 TightRunBounds(*run) : ConservativeRunBounds(*run);
625 fBounds.join(runBounds);
626 fDeferredBounds = false;
627 }
628
reserve(size_t size)629 void SkTextBlobBuilder::reserve(size_t size) {
630 // We don't currently pre-allocate, but maybe someday...
631 if (fStorageUsed + size <= fStorageSize) {
632 return;
633 }
634
635 if (0 == fRunCount) {
636 SkASSERT(nullptr == fStorage.get());
637 SkASSERT(0 == fStorageSize);
638 SkASSERT(0 == fStorageUsed);
639
640 // the first allocation also includes blob storage
641 fStorageUsed += sizeof(SkTextBlob);
642 }
643
644 fStorageSize = fStorageUsed + size;
645 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
646 fStorage.realloc(fStorageSize);
647 }
648
mergeRun(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,SkPoint offset)649 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
650 int count, SkPoint offset) {
651 if (0 == fLastRun) {
652 SkASSERT(0 == fRunCount);
653 return false;
654 }
655
656 SkASSERT(fLastRun >= sizeof(SkTextBlob));
657 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
658 fLastRun);
659 SkASSERT(run->glyphCount() > 0);
660
661 if (run->textSize() != 0) {
662 return false;
663 }
664
665 if (run->positioning() != positioning
666 || run->font() != font
667 || (run->glyphCount() + count < run->glyphCount())) {
668 return false;
669 }
670
671 // we can merge same-font/same-positioning runs in the following cases:
672 // * fully positioned run following another fully positioned run
673 // * horizontally postioned run following another horizontally positioned run with the same
674 // y-offset
675 if (SkTextBlob::kFull_Positioning != positioning
676 && (SkTextBlob::kHorizontal_Positioning != positioning
677 || run->offset().y() != offset.y())) {
678 return false;
679 }
680
681 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) -
682 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning);
683 this->reserve(sizeDelta);
684
685 // reserve may have realloced
686 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
687 uint32_t preMergeCount = run->glyphCount();
688 run->grow(count);
689
690 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
691 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
692 fCurrentRunBuffer.pos = run->posBuffer()
693 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
694
695 fStorageUsed += sizeDelta;
696
697 SkASSERT(fStorageUsed <= fStorageSize);
698 run->validate(fStorage.get() + fStorageUsed);
699
700 return true;
701 }
702
allocInternal(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,int textSize,SkPoint offset,const SkRect * bounds)703 void SkTextBlobBuilder::allocInternal(const SkPaint &font,
704 SkTextBlob::GlyphPositioning positioning,
705 int count, int textSize, SkPoint offset, const SkRect* bounds) {
706 SkASSERT(count > 0);
707 SkASSERT(textSize >= 0);
708 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
709 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
710 this->updateDeferredBounds();
711
712 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning);
713 this->reserve(runSize);
714
715 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
716 SkASSERT(fStorageUsed + runSize <= fStorageSize);
717
718 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
719 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
720 fCurrentRunBuffer.glyphs = run->glyphBuffer();
721 fCurrentRunBuffer.pos = run->posBuffer();
722 fCurrentRunBuffer.utf8text = run->textBuffer();
723 fCurrentRunBuffer.clusters = run->clusterBuffer();
724
725 fLastRun = fStorageUsed;
726 fStorageUsed += runSize;
727 fRunCount++;
728
729 SkASSERT(fStorageUsed <= fStorageSize);
730 run->validate(fStorage.get() + fStorageUsed);
731 }
732 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
733 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
734 if (!fDeferredBounds) {
735 if (bounds) {
736 fBounds.join(*bounds);
737 } else {
738 fDeferredBounds = true;
739 }
740 }
741 }
742
allocRunText(const SkPaint & font,int count,SkScalar x,SkScalar y,int textByteCount,SkString lang,const SkRect * bounds)743 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count,
744 SkScalar x, SkScalar y,
745 int textByteCount,
746 SkString lang,
747 const SkRect* bounds) {
748 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
749 return fCurrentRunBuffer;
750 }
751
allocRunTextPosH(const SkPaint & font,int count,SkScalar y,int textByteCount,SkString lang,const SkRect * bounds)752 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
753 SkScalar y,
754 int textByteCount,
755 SkString lang,
756 const SkRect* bounds) {
757 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
758 bounds);
759
760 return fCurrentRunBuffer;
761 }
762
allocRunTextPos(const SkPaint & font,int count,int textByteCount,SkString lang,const SkRect * bounds)763 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
764 int textByteCount,
765 SkString lang,
766 const SkRect *bounds) {
767 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
768
769 return fCurrentRunBuffer;
770 }
771
make()772 sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
773 if (!fRunCount) {
774 // We don't instantiate empty blobs.
775 SkASSERT(!fStorage.get());
776 SkASSERT(fStorageUsed == 0);
777 SkASSERT(fStorageSize == 0);
778 SkASSERT(fLastRun == 0);
779 SkASSERT(fBounds.isEmpty());
780 return nullptr;
781 }
782
783 this->updateDeferredBounds();
784
785 // Tag the last run as such.
786 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
787 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
788
789 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
790 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
791
792 SkDEBUGCODE(
793 size_t validateSize = sizeof(SkTextBlob);
794 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
795 run = SkTextBlob::RunRecord::Next(run)) {
796 validateSize += SkTextBlob::RunRecord::StorageSize(
797 run->fCount, run->textSize(), run->positioning());
798 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
799 fRunCount--;
800 }
801 SkASSERT(validateSize == fStorageUsed);
802 SkASSERT(fRunCount == 0);
803 )
804
805 fStorageUsed = 0;
806 fStorageSize = 0;
807 fRunCount = 0;
808 fLastRun = 0;
809 fBounds.setEmpty();
810
811 return sk_sp<SkTextBlob>(blob);
812 }
813