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 #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:
     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 
     32     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 
     44     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 
     54     bool operator!=(const RunFont& other) const {
     55         return !(*this == other);
     56     }
     57 
     58     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:
    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 
    149     uint32_t glyphCount() const {
    150         return fCount;
    151     }
    152 
    153     const SkPoint& offset() const {
    154         return fOffset;
    155     }
    156 
    157     const RunFont& font() const {
    158         return fFont;
    159     }
    160 
    161     GlyphPositioning positioning() const {
    162         return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
    163     }
    164 
    165     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 
    171     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 
    177     uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
    178 
    179     uint32_t* clusterBuffer() const {
    180         // clusters follow the textSize.
    181         return isExtended() ? 1 + this->textSizePtr() : nullptr;
    182     }
    183 
    184     char* textBuffer() const {
    185         return isExtended()
    186             ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
    187             : nullptr;
    188     }
    189 
    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 
    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 
    210     static const RunRecord* Next(const RunRecord* run) {
    211         return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
    212     }
    213 
    214     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 
    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 
    246     static size_t PosCount(int glyphCount,
    247                            SkTextBlob::GlyphPositioning positioning) {
    248         return glyphCount * ScalarsPerGlyph(positioning);
    249     }
    250 
    251     uint32_t* textSizePtr() const {
    252         // textSize follows the position buffer.
    253         SkASSERT(isExtended());
    254         return (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning())]);
    255     }
    256 
    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 
    270     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;
    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 
    291 SkTextBlob::SkTextBlob(const SkRect& bounds)
    292     : fBounds(bounds)
    293     , fUniqueID(next_id())
    294     , fAddedToCache(false) {}
    295 
    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 
    323 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 
    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 
    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 
    435 SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
    436     : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
    437     SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
    438 }
    439 
    440 bool SkTextBlobRunIterator::done() const {
    441     return !fCurrentRun;
    442 }
    443 
    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 
    453 uint32_t SkTextBlobRunIterator::glyphCount() const {
    454     SkASSERT(!this->done());
    455     return fCurrentRun->glyphCount();
    456 }
    457 
    458 const uint16_t* SkTextBlobRunIterator::glyphs() const {
    459     SkASSERT(!this->done());
    460     return fCurrentRun->glyphBuffer();
    461 }
    462 
    463 const SkScalar* SkTextBlobRunIterator::pos() const {
    464     SkASSERT(!this->done());
    465     return fCurrentRun->posBuffer();
    466 }
    467 
    468 const SkPoint& SkTextBlobRunIterator::offset() const {
    469     SkASSERT(!this->done());
    470     return fCurrentRun->offset();
    471 }
    472 
    473 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
    474     SkASSERT(!this->done());
    475     return fCurrentRun->positioning();
    476 }
    477 
    478 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
    479     SkASSERT(!this->done());
    480 
    481     fCurrentRun->font().applyToPaint(paint);
    482 }
    483 
    484 uint32_t* SkTextBlobRunIterator::clusters() const {
    485     SkASSERT(!this->done());
    486     return fCurrentRun->clusterBuffer();
    487 }
    488 uint32_t SkTextBlobRunIterator::textSize() const {
    489     SkASSERT(!this->done());
    490     return fCurrentRun->textSize();
    491 }
    492 char* SkTextBlobRunIterator::text() const {
    493     SkASSERT(!this->done());
    494     return fCurrentRun->textBuffer();
    495 }
    496 
    497 
    498 bool SkTextBlobRunIterator::isLCD() const {
    499     return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
    500 }
    501 
    502 SkTextBlobBuilder::SkTextBlobBuilder()
    503     : fStorageSize(0)
    504     , fStorageUsed(0)
    505     , fRunCount(0)
    506     , fDeferredBounds(false)
    507     , fLastRun(0) {
    508     fBounds.setEmpty();
    509 }
    510 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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