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 namespace {
15
16 // TODO(fmalita): replace with SkFont.
17 class RunFont : SkNoncopyable {
18 public:
RunFont(const SkPaint & paint)19 RunFont(const SkPaint& paint)
20 : fSize(paint.getTextSize())
21 , fScaleX(paint.getTextScaleX())
22 , fTypeface(SkSafeRef(paint.getTypeface()))
23 , fSkewX(paint.getTextSkewX())
24 , fAlign(paint.getTextAlign())
25 , fHinting(paint.getHinting())
26 , fFlags(paint.getFlags() & kFlagsMask) { }
27
applyToPaint(SkPaint * paint) const28 void applyToPaint(SkPaint* paint) const {
29 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
30 paint->setTypeface(fTypeface.get());
31 paint->setTextSize(fSize);
32 paint->setTextScaleX(fScaleX);
33 paint->setTextSkewX(fSkewX);
34 paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
35 paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
36
37 paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
38 }
39
operator ==(const RunFont & other) const40 bool operator==(const RunFont& other) const {
41 return fTypeface == other.fTypeface
42 && fSize == other.fSize
43 && fScaleX == other.fScaleX
44 && fSkewX == other.fSkewX
45 && fAlign == other.fAlign
46 && fHinting == other.fHinting
47 && fFlags == other.fFlags;
48 }
49
operator !=(const RunFont & other) const50 bool operator!=(const RunFont& other) const {
51 return !(*this == other);
52 }
53
flags() const54 uint32_t flags() const { return fFlags; }
55
56 private:
57 const static uint32_t kFlagsMask =
58 SkPaint::kAntiAlias_Flag |
59 SkPaint::kUnderlineText_Flag |
60 SkPaint::kStrikeThruText_Flag |
61 SkPaint::kFakeBoldText_Flag |
62 SkPaint::kLinearText_Flag |
63 SkPaint::kSubpixelText_Flag |
64 SkPaint::kDevKernText_Flag |
65 SkPaint::kLCDRenderText_Flag |
66 SkPaint::kEmbeddedBitmapText_Flag |
67 SkPaint::kAutoHinting_Flag |
68 SkPaint::kVerticalText_Flag |
69 SkPaint::kGenA8FromLCD_Flag;
70
71 SkScalar fSize;
72 SkScalar fScaleX;
73
74 // Keep this SkAutoTUnref off the first position, to avoid interfering with SkNoncopyable
75 // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
76 SkAutoTUnref<SkTypeface> fTypeface;
77 SkScalar fSkewX;
78
79 static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
80 uint32_t fAlign : 2;
81 static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
82 uint32_t fHinting : 2;
83 static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
84 uint32_t fFlags : 16;
85
86 typedef SkNoncopyable INHERITED;
87 };
88
89 struct RunFontStorageEquivalent {
90 SkScalar fSize, fScaleX;
91 void* fTypeface;
92 SkScalar fSkewX;
93 uint32_t fFlags;
94 };
95 static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
96
97 } // anonymous namespace
98
99 //
100 // Textblob data is laid out into externally-managed storage as follows:
101 //
102 // -----------------------------------------------------------------------------
103 // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
104 // -----------------------------------------------------------------------------
105 //
106 // Each run record describes a text blob run, and can be used to determine the (implicit)
107 // location of the following record.
108
109 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
110
111 class SkTextBlob::RunRecord {
112 public:
RunRecord(uint32_t count,const SkPoint & offset,const SkPaint & font,GlyphPositioning pos)113 RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
114 : fFont(font)
115 , fCount(count)
116 , fOffset(offset)
117 , fPositioning(pos) {
118 SkDEBUGCODE(fMagic = kRunRecordMagic);
119 }
120
glyphCount() const121 uint32_t glyphCount() const {
122 return fCount;
123 }
124
offset() const125 const SkPoint& offset() const {
126 return fOffset;
127 }
128
font() const129 const RunFont& font() const {
130 return fFont;
131 }
132
positioning() const133 GlyphPositioning positioning() const {
134 return fPositioning;
135 }
136
glyphBuffer() const137 uint16_t* glyphBuffer() const {
138 // Glyph are stored immediately following the record.
139 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
140 }
141
posBuffer() const142 SkScalar* posBuffer() const {
143 // Position scalars follow the (aligned) glyph buffer.
144 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
145 SkAlign4(fCount * sizeof(uint16_t)));
146 }
147
StorageSize(int glyphCount,SkTextBlob::GlyphPositioning positioning)148 static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) {
149 // RunRecord object + (aligned) glyph buffer + position buffer
150 return SkAlignPtr(sizeof(SkTextBlob::RunRecord)
151 + SkAlign4(glyphCount* sizeof(uint16_t))
152 + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning));
153 }
154
First(const SkTextBlob * blob)155 static const RunRecord* First(const SkTextBlob* blob) {
156 // The first record (if present) is stored following the blob object.
157 return reinterpret_cast<const RunRecord*>(blob + 1);
158 }
159
Next(const RunRecord * run)160 static const RunRecord* Next(const RunRecord* run) {
161 return reinterpret_cast<const RunRecord*>(reinterpret_cast<const uint8_t*>(run)
162 + StorageSize(run->glyphCount(), run->positioning()));
163 }
164
validate(const uint8_t * storageTop) const165 void validate(const uint8_t* storageTop) const {
166 SkASSERT(kRunRecordMagic == fMagic);
167 SkASSERT((uint8_t*)Next(this) <= storageTop);
168 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
169 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(fPositioning) <= (SkScalar*)Next(this));
170 }
171
172 private:
173 friend class SkTextBlobBuilder;
174
grow(uint32_t count)175 void grow(uint32_t count) {
176 SkScalar* initialPosBuffer = posBuffer();
177 uint32_t initialCount = fCount;
178 fCount += count;
179
180 // Move the initial pos scalars to their new location.
181 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(fPositioning);
182 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)Next(this));
183
184 // memmove, as the buffers may overlap
185 memmove(posBuffer(), initialPosBuffer, copySize);
186 }
187
188 RunFont fFont;
189 uint32_t fCount;
190 SkPoint fOffset;
191 GlyphPositioning fPositioning;
192
193 SkDEBUGCODE(unsigned fMagic;)
194 };
195
196 static int32_t gNextID = 1;
next_id()197 static int32_t next_id() {
198 int32_t id;
199 do {
200 id = sk_atomic_inc(&gNextID);
201 } while (id == SK_InvalidGenID);
202 return id;
203 }
204
SkTextBlob(int runCount,const SkRect & bounds)205 SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds)
206 : fRunCount(runCount)
207 , fBounds(bounds)
208 , fUniqueID(next_id()) {
209 }
210
~SkTextBlob()211 SkTextBlob::~SkTextBlob() {
212 const RunRecord* run = RunRecord::First(this);
213 for (int i = 0; i < fRunCount; ++i) {
214 const RunRecord* nextRun = RunRecord::Next(run);
215 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
216 run->~RunRecord();
217 run = nextRun;
218 }
219 }
220
flatten(SkWriteBuffer & buffer) const221 void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
222 int runCount = fRunCount;
223
224 buffer.write32(runCount);
225 buffer.writeRect(fBounds);
226
227 SkPaint runPaint;
228 SkTextBlobRunIterator it(this);
229 while (!it.done()) {
230 SkASSERT(it.glyphCount() > 0);
231
232 buffer.write32(it.glyphCount());
233 buffer.write32(it.positioning());
234 buffer.writePoint(it.offset());
235 // This should go away when switching to SkFont
236 it.applyFontToPaint(&runPaint);
237 buffer.writePaint(runPaint);
238
239 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
240 buffer.writeByteArray(it.pos(),
241 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
242
243 it.next();
244 SkDEBUGCODE(runCount--);
245 }
246 SkASSERT(0 == runCount);
247 }
248
CreateFromBuffer(SkReadBuffer & reader)249 const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) {
250 int runCount = reader.read32();
251 if (runCount < 0) {
252 return nullptr;
253 }
254
255 SkRect bounds;
256 reader.readRect(&bounds);
257
258 SkTextBlobBuilder blobBuilder;
259 for (int i = 0; i < runCount; ++i) {
260 int glyphCount = reader.read32();
261 GlyphPositioning pos = static_cast<GlyphPositioning>(reader.read32());
262 if (glyphCount <= 0 || pos > kFull_Positioning) {
263 return nullptr;
264 }
265
266 SkPoint offset;
267 reader.readPoint(&offset);
268 SkPaint font;
269 reader.readPaint(&font);
270
271 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
272 switch (pos) {
273 case kDefault_Positioning:
274 buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds);
275 break;
276 case kHorizontal_Positioning:
277 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds);
278 break;
279 case kFull_Positioning:
280 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds);
281 break;
282 default:
283 return nullptr;
284 }
285
286 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
287 !reader.readByteArray(buf->pos,
288 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
289 return nullptr;
290 }
291 }
292
293 return blobBuilder.build();
294 }
295
ScalarsPerGlyph(GlyphPositioning pos)296 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
297 // GlyphPositioning values are directly mapped to scalars-per-glyph.
298 SkASSERT(pos <= 2);
299 return pos;
300 }
301
SkTextBlobRunIterator(const SkTextBlob * blob)302 SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
303 : fCurrentRun(SkTextBlob::RunRecord::First(blob))
304 , fRemainingRuns(blob->fRunCount) {
305 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
306 }
307
done() const308 bool SkTextBlobRunIterator::done() const {
309 return fRemainingRuns <= 0;
310 }
311
next()312 void SkTextBlobRunIterator::next() {
313 SkASSERT(!this->done());
314
315 if (!this->done()) {
316 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
317 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
318 fRemainingRuns--;
319 }
320 }
321
glyphCount() const322 uint32_t SkTextBlobRunIterator::glyphCount() const {
323 SkASSERT(!this->done());
324 return fCurrentRun->glyphCount();
325 }
326
glyphs() const327 const uint16_t* SkTextBlobRunIterator::glyphs() const {
328 SkASSERT(!this->done());
329 return fCurrentRun->glyphBuffer();
330 }
331
pos() const332 const SkScalar* SkTextBlobRunIterator::pos() const {
333 SkASSERT(!this->done());
334 return fCurrentRun->posBuffer();
335 }
336
offset() const337 const SkPoint& SkTextBlobRunIterator::offset() const {
338 SkASSERT(!this->done());
339 return fCurrentRun->offset();
340 }
341
positioning() const342 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
343 SkASSERT(!this->done());
344 return fCurrentRun->positioning();
345 }
346
applyFontToPaint(SkPaint * paint) const347 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
348 SkASSERT(!this->done());
349
350 fCurrentRun->font().applyToPaint(paint);
351 }
352
isLCD() const353 bool SkTextBlobRunIterator::isLCD() const {
354 return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
355 }
356
SkTextBlobBuilder()357 SkTextBlobBuilder::SkTextBlobBuilder()
358 : fStorageSize(0)
359 , fStorageUsed(0)
360 , fRunCount(0)
361 , fDeferredBounds(false)
362 , fLastRun(0) {
363 fBounds.setEmpty();
364 }
365
~SkTextBlobBuilder()366 SkTextBlobBuilder::~SkTextBlobBuilder() {
367 if (nullptr != fStorage.get()) {
368 // We are abandoning runs and must destruct the associated font data.
369 // The easiest way to accomplish that is to use the blob destructor.
370 build()->unref();
371 }
372 }
373
TightRunBounds(const SkTextBlob::RunRecord & run)374 SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
375 SkRect bounds;
376 SkPaint paint;
377 run.font().applyToPaint(&paint);
378
379 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
380 paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
381 return bounds.makeOffset(run.offset().x(), run.offset().y());
382 }
383
384 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
385 paint.getTextWidths(run.glyphBuffer(),
386 run.glyphCount() * sizeof(uint16_t),
387 NULL,
388 glyphBounds.get());
389
390 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
391 SkTextBlob::kHorizontal_Positioning == run.positioning());
392 // kFull_Positioning => [ x, y, x, y... ]
393 // kHorizontal_Positioning => [ x, x, x... ]
394 // (const y applied by runBounds.offset(run->offset()) later)
395 const SkScalar horizontalConstY = 0;
396 const SkScalar* glyphPosX = run.posBuffer();
397 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
398 glyphPosX + 1 : &horizontalConstY;
399 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
400 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
401 posXInc : 0;
402
403 bounds.setEmpty();
404 for (unsigned i = 0; i < run.glyphCount(); ++i) {
405 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
406 glyphPosX += posXInc;
407 glyphPosY += posYInc;
408 }
409
410 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
411
412 return bounds.makeOffset(run.offset().x(), run.offset().y());
413 }
414
ConservativeRunBounds(const SkTextBlob::RunRecord & run)415 SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
416 SkASSERT(run.glyphCount() > 0);
417 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
418 SkTextBlob::kHorizontal_Positioning == run.positioning());
419
420 SkPaint paint;
421 run.font().applyToPaint(&paint);
422 const SkRect fontBounds = paint.getFontBounds();
423 if (fontBounds.isEmpty()) {
424 // Empty font bounds are likely a font bug. TightBounds has a better chance of
425 // producing useful results in this case.
426 return TightRunBounds(run);
427 }
428
429 // Compute the glyph position bbox.
430 SkRect bounds;
431 switch (run.positioning()) {
432 case SkTextBlob::kHorizontal_Positioning: {
433 const SkScalar* glyphPos = run.posBuffer();
434 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
435
436 SkScalar minX = *glyphPos;
437 SkScalar maxX = *glyphPos;
438 for (unsigned i = 1; i < run.glyphCount(); ++i) {
439 SkScalar x = glyphPos[i];
440 minX = SkMinScalar(x, minX);
441 maxX = SkMaxScalar(x, maxX);
442 }
443
444 bounds.setLTRB(minX, 0, maxX, 0);
445 } break;
446 case SkTextBlob::kFull_Positioning: {
447 const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
448 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
449
450 bounds.setBounds(glyphPosPts, run.glyphCount());
451 } break;
452 default:
453 SkFAIL("unsupported positioning mode");
454 }
455
456 // Expand by typeface glyph bounds.
457 bounds.fLeft += fontBounds.left();
458 bounds.fTop += fontBounds.top();
459 bounds.fRight += fontBounds.right();
460 bounds.fBottom += fontBounds.bottom();
461
462 // Offset by run position.
463 return bounds.makeOffset(run.offset().x(), run.offset().y());
464 }
465
updateDeferredBounds()466 void SkTextBlobBuilder::updateDeferredBounds() {
467 SkASSERT(!fDeferredBounds || fRunCount > 0);
468
469 if (!fDeferredBounds) {
470 return;
471 }
472
473 SkASSERT(fLastRun >= sizeof(SkTextBlob));
474 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
475 fLastRun);
476
477 // FIXME: we should also use conservative bounds for kDefault_Positioning.
478 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
479 TightRunBounds(*run) : ConservativeRunBounds(*run);
480 fBounds.join(runBounds);
481 fDeferredBounds = false;
482 }
483
reserve(size_t size)484 void SkTextBlobBuilder::reserve(size_t size) {
485 // We don't currently pre-allocate, but maybe someday...
486 if (fStorageUsed + size <= fStorageSize) {
487 return;
488 }
489
490 if (0 == fRunCount) {
491 SkASSERT(nullptr == fStorage.get());
492 SkASSERT(0 == fStorageSize);
493 SkASSERT(0 == fStorageUsed);
494
495 // the first allocation also includes blob storage
496 fStorageUsed += sizeof(SkTextBlob);
497 }
498
499 fStorageSize = fStorageUsed + size;
500 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
501 fStorage.realloc(fStorageSize);
502 }
503
mergeRun(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,SkPoint offset)504 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
505 int count, SkPoint offset) {
506 if (0 == fLastRun) {
507 SkASSERT(0 == fRunCount);
508 return false;
509 }
510
511 SkASSERT(fLastRun >= sizeof(SkTextBlob));
512 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
513 fLastRun);
514 SkASSERT(run->glyphCount() > 0);
515
516 if (run->positioning() != positioning
517 || run->font() != font
518 || (run->glyphCount() + count < run->glyphCount())) {
519 return false;
520 }
521
522 // we can merge same-font/same-positioning runs in the following cases:
523 // * fully positioned run following another fully positioned run
524 // * horizontally postioned run following another horizontally positioned run with the same
525 // y-offset
526 if (SkTextBlob::kFull_Positioning != positioning
527 && (SkTextBlob::kHorizontal_Positioning != positioning
528 || run->offset().y() != offset.y())) {
529 return false;
530 }
531
532 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) -
533 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning);
534 this->reserve(sizeDelta);
535
536 // reserve may have realloced
537 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
538 uint32_t preMergeCount = run->glyphCount();
539 run->grow(count);
540
541 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
542 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
543 fCurrentRunBuffer.pos = run->posBuffer()
544 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
545
546 fStorageUsed += sizeDelta;
547
548 SkASSERT(fStorageUsed <= fStorageSize);
549 run->validate(fStorage.get() + fStorageUsed);
550
551 return true;
552 }
553
allocInternal(const SkPaint & font,SkTextBlob::GlyphPositioning positioning,int count,SkPoint offset,const SkRect * bounds)554 void SkTextBlobBuilder::allocInternal(const SkPaint &font,
555 SkTextBlob::GlyphPositioning positioning,
556 int count, SkPoint offset, const SkRect* bounds) {
557 SkASSERT(count > 0);
558 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
559
560 if (!this->mergeRun(font, positioning, count, offset)) {
561 this->updateDeferredBounds();
562
563 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning);
564 this->reserve(runSize);
565
566 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
567 SkASSERT(fStorageUsed + runSize <= fStorageSize);
568
569 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
570 SkTextBlob::RunRecord(count, offset, font, positioning);
571
572 fCurrentRunBuffer.glyphs = run->glyphBuffer();
573 fCurrentRunBuffer.pos = run->posBuffer();
574
575 fLastRun = fStorageUsed;
576 fStorageUsed += runSize;
577 fRunCount++;
578
579 SkASSERT(fStorageUsed <= fStorageSize);
580 run->validate(fStorage.get() + fStorageUsed);
581 }
582
583 if (!fDeferredBounds) {
584 if (bounds) {
585 fBounds.join(*bounds);
586 } else {
587 fDeferredBounds = true;
588 }
589 }
590 }
591
allocRun(const SkPaint & font,int count,SkScalar x,SkScalar y,const SkRect * bounds)592 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkPaint& font, int count,
593 SkScalar x, SkScalar y,
594 const SkRect* bounds) {
595 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, SkPoint::Make(x, y), bounds);
596
597 return fCurrentRunBuffer;
598 }
599
allocRunPosH(const SkPaint & font,int count,SkScalar y,const SkRect * bounds)600 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count,
601 SkScalar y,
602 const SkRect* bounds) {
603 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y),
604 bounds);
605
606 return fCurrentRunBuffer;
607 }
608
allocRunPos(const SkPaint & font,int count,const SkRect * bounds)609 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint& font, int count,
610 const SkRect *bounds) {
611 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds);
612
613 return fCurrentRunBuffer;
614 }
615
build()616 const SkTextBlob* SkTextBlobBuilder::build() {
617 SkASSERT((fRunCount > 0) == (nullptr != fStorage.get()));
618
619 this->updateDeferredBounds();
620
621 if (0 == fRunCount) {
622 SkASSERT(nullptr == fStorage.get());
623 fStorageUsed = sizeof(SkTextBlob);
624 fStorage.realloc(fStorageUsed);
625 }
626
627 const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBounds);
628 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
629
630 SkDEBUGCODE(
631 size_t validateSize = sizeof(SkTextBlob);
632 const SkTextBlob::RunRecord* run = SkTextBlob::RunRecord::First(blob);
633 for (int i = 0; i < fRunCount; ++i) {
634 validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run->fPositioning);
635 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
636 run = SkTextBlob::RunRecord::Next(run);
637 }
638 SkASSERT(validateSize == fStorageUsed);
639 )
640
641 fStorageUsed = 0;
642 fStorageSize = 0;
643 fRunCount = 0;
644 fLastRun = 0;
645 fBounds.setEmpty();
646
647 return blob;
648 }
649
650