Home | History | Annotate | Download | only in core
      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:
     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 
     28     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 
     40     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 
     50     bool operator!=(const RunFont& other) const {
     51         return !(*this == other);
     52     }
     53 
     54     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:
    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 
    121     uint32_t glyphCount() const {
    122         return fCount;
    123     }
    124 
    125     const SkPoint& offset() const {
    126         return fOffset;
    127     }
    128 
    129     const RunFont& font() const {
    130         return fFont;
    131     }
    132 
    133     GlyphPositioning positioning() const {
    134         return fPositioning;
    135     }
    136 
    137     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 
    142     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 
    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 
    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 
    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 
    165     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 
    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;
    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 
    205 SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds)
    206     : fRunCount(runCount)
    207     , fBounds(bounds)
    208     , fUniqueID(next_id()) {
    209 }
    210 
    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 
    221 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 
    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 
    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 
    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 
    308 bool SkTextBlobRunIterator::done() const {
    309     return fRemainingRuns <= 0;
    310 }
    311 
    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 
    322 uint32_t SkTextBlobRunIterator::glyphCount() const {
    323     SkASSERT(!this->done());
    324     return fCurrentRun->glyphCount();
    325 }
    326 
    327 const uint16_t* SkTextBlobRunIterator::glyphs() const {
    328     SkASSERT(!this->done());
    329     return fCurrentRun->glyphBuffer();
    330 }
    331 
    332 const SkScalar* SkTextBlobRunIterator::pos() const {
    333     SkASSERT(!this->done());
    334     return fCurrentRun->posBuffer();
    335 }
    336 
    337 const SkPoint& SkTextBlobRunIterator::offset() const {
    338     SkASSERT(!this->done());
    339     return fCurrentRun->offset();
    340 }
    341 
    342 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
    343     SkASSERT(!this->done());
    344     return fCurrentRun->positioning();
    345 }
    346 
    347 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
    348     SkASSERT(!this->done());
    349 
    350     fCurrentRun->font().applyToPaint(paint);
    351 }
    352 
    353 bool SkTextBlobRunIterator::isLCD() const {
    354     return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
    355 }
    356 
    357 SkTextBlobBuilder::SkTextBlobBuilder()
    358     : fStorageSize(0)
    359     , fStorageUsed(0)
    360     , fRunCount(0)
    361     , fDeferredBounds(false)
    362     , fLastRun(0) {
    363     fBounds.setEmpty();
    364 }
    365 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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