• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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