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 "SkTextBlob.h"
      9 
     10 #include "SkReadBuffer.h"
     11 #include "SkWriteBuffer.h"
     12 
     13 //
     14 // Textblob data is laid out into externally-managed storage as follows:
     15 //
     16 //    -----------------------------------------------------------------------------
     17 //   | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
     18 //    -----------------------------------------------------------------------------
     19 //
     20 //  Each run record describes a text blob run, and can be used to determine the (implicit)
     21 //  location of the following record.
     22 
     23 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
     24 
     25 class SkTextBlob::RunRecord {
     26 public:
     27     RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
     28         : fCount(count)
     29         , fOffset(offset)
     30         , fFont(font)
     31         , fPositioning(pos) {
     32         SkDEBUGCODE(fMagic = kRunRecordMagic);
     33     }
     34 
     35     uint32_t glyphCount() const {
     36         return fCount;
     37     }
     38 
     39     const SkPoint& offset() const {
     40         return fOffset;
     41     }
     42 
     43     const SkPaint& font() const {
     44         return fFont;
     45     }
     46 
     47     GlyphPositioning positioning() const {
     48         return fPositioning;
     49     }
     50 
     51     uint16_t* glyphBuffer() const {
     52         // Glyph are stored immediately following the record.
     53         return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
     54     }
     55 
     56     SkScalar* posBuffer() const {
     57         // Position scalars follow the (aligned) glyph buffer.
     58         return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
     59                                            SkAlign4(fCount * sizeof(uint16_t)));
     60     }
     61 
     62     static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) {
     63         // RunRecord object + (aligned) glyph buffer + position buffer
     64         return SkAlignPtr(sizeof(SkTextBlob::RunRecord)
     65                         + SkAlign4(glyphCount* sizeof(uint16_t))
     66                         + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning));
     67     }
     68 
     69     static const RunRecord* First(const SkTextBlob* blob) {
     70         // The first record (if present) is stored following the blob object.
     71         return reinterpret_cast<const RunRecord*>(blob + 1);
     72     }
     73 
     74     static const RunRecord* Next(const RunRecord* run) {
     75         return reinterpret_cast<const RunRecord*>(reinterpret_cast<const uint8_t*>(run)
     76             + StorageSize(run->glyphCount(), run->positioning()));
     77     }
     78 
     79     void validate(uint8_t* storageTop) const {
     80         SkASSERT(kRunRecordMagic == fMagic);
     81         SkASSERT((uint8_t*)Next(this) <= storageTop);
     82         SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
     83         SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(fPositioning) <= (SkScalar*)Next(this));
     84     }
     85 
     86 private:
     87     friend class SkTextBlobBuilder;
     88 
     89     void grow(uint32_t count) {
     90         SkScalar* initialPosBuffer = posBuffer();
     91         uint32_t initialCount = fCount;
     92         fCount += count;
     93 
     94         // Move the initial pos scalars to their new location.
     95         size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(fPositioning);
     96         SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)Next(this));
     97 
     98         // memmove, as the buffers may overlap
     99         memmove(posBuffer(), initialPosBuffer, copySize);
    100     }
    101 
    102     uint32_t         fCount;
    103     SkPoint          fOffset;
    104     SkPaint          fFont;
    105     GlyphPositioning fPositioning;
    106 
    107     SkDEBUGCODE(unsigned fMagic;)
    108 };
    109 
    110 SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds)
    111     : fRunCount(runCount)
    112     , fBounds(bounds) {
    113 }
    114 
    115 SkTextBlob::~SkTextBlob() {
    116     const RunRecord* run = RunRecord::First(this);
    117     for (int i = 0; i < fRunCount; ++i) {
    118         const RunRecord* nextRun = RunRecord::Next(run);
    119         SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
    120         run->~RunRecord();
    121         run = nextRun;
    122     }
    123 }
    124 
    125 void SkTextBlob::internal_dispose() const {
    126     // SkTextBlobs use externally-managed storage.
    127     this->internal_dispose_restore_refcnt_to_1();
    128     this->~SkTextBlob();
    129     sk_free(const_cast<SkTextBlob*>(this));
    130 }
    131 
    132 uint32_t SkTextBlob::uniqueID() const {
    133     static int32_t  gTextBlobGenerationID; // = 0;
    134 
    135     // loop in case our global wraps around, as we never want to return SK_InvalidGenID
    136     while (SK_InvalidGenID == fUniqueID) {
    137         fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1;
    138     }
    139 
    140     return fUniqueID;
    141 }
    142 
    143 void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
    144     int runCount = fRunCount;
    145 
    146     buffer.write32(runCount);
    147     buffer.writeRect(fBounds);
    148 
    149     SkPaint runPaint;
    150     RunIterator it(this);
    151     while (!it.done()) {
    152         SkASSERT(it.glyphCount() > 0);
    153 
    154         buffer.write32(it.glyphCount());
    155         buffer.write32(it.positioning());
    156         buffer.writePoint(it.offset());
    157         // This should go away when switching to SkFont
    158         it.applyFontToPaint(&runPaint);
    159         buffer.writePaint(runPaint);
    160 
    161         buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
    162         buffer.writeByteArray(it.pos(),
    163             it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
    164 
    165         it.next();
    166         SkDEBUGCODE(runCount--);
    167     }
    168     SkASSERT(0 == runCount);
    169 }
    170 
    171 const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) {
    172     int runCount = reader.read32();
    173     if (runCount < 0) {
    174         return NULL;
    175     }
    176 
    177     SkRect bounds;
    178     reader.readRect(&bounds);
    179 
    180     SkTextBlobBuilder blobBuilder;
    181     for (int i = 0; i < runCount; ++i) {
    182         int glyphCount = reader.read32();
    183         GlyphPositioning pos = static_cast<GlyphPositioning>(reader.read32());
    184         if (glyphCount <= 0 || pos > kFull_Positioning) {
    185             return NULL;
    186         }
    187 
    188         SkPoint offset;
    189         reader.readPoint(&offset);
    190         SkPaint font;
    191         reader.readPaint(&font);
    192 
    193         const SkTextBlobBuilder::RunBuffer* buf = NULL;
    194         switch (pos) {
    195         case kDefault_Positioning:
    196             buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds);
    197             break;
    198         case kHorizontal_Positioning:
    199             buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds);
    200             break;
    201         case kFull_Positioning:
    202             buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds);
    203             break;
    204         default:
    205             return NULL;
    206         }
    207 
    208         if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
    209             !reader.readByteArray(buf->pos,
    210                                   glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
    211             return NULL;
    212         }
    213     }
    214 
    215     return blobBuilder.build();
    216 }
    217 
    218 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
    219     // GlyphPositioning values are directly mapped to scalars-per-glyph.
    220     SkASSERT(pos <= 2);
    221     return pos;
    222 }
    223 
    224 SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob)
    225     : fCurrentRun(RunRecord::First(blob))
    226     , fRemainingRuns(blob->fRunCount) {
    227     SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
    228 }
    229 
    230 bool SkTextBlob::RunIterator::done() const {
    231     return fRemainingRuns <= 0;
    232 }
    233 
    234 void SkTextBlob::RunIterator::next() {
    235     SkASSERT(!this->done());
    236 
    237     if (!this->done()) {
    238         SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
    239         fCurrentRun = RunRecord::Next(fCurrentRun);
    240         fRemainingRuns--;
    241     }
    242 }
    243 
    244 uint32_t SkTextBlob::RunIterator::glyphCount() const {
    245     SkASSERT(!this->done());
    246     return fCurrentRun->glyphCount();
    247 }
    248 
    249 const uint16_t* SkTextBlob::RunIterator::glyphs() const {
    250     SkASSERT(!this->done());
    251     return fCurrentRun->glyphBuffer();
    252 }
    253 
    254 const SkScalar* SkTextBlob::RunIterator::pos() const {
    255     SkASSERT(!this->done());
    256     return fCurrentRun->posBuffer();
    257 }
    258 
    259 const SkPoint& SkTextBlob::RunIterator::offset() const {
    260     SkASSERT(!this->done());
    261     return fCurrentRun->offset();
    262 }
    263 
    264 SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const {
    265     SkASSERT(!this->done());
    266     return fCurrentRun->positioning();
    267 }
    268 
    269 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const {
    270     SkASSERT(!this->done());
    271 
    272     const SkPaint& font = fCurrentRun->font();
    273 
    274     paint->setTypeface(font.getTypeface());
    275     paint->setTextEncoding(font.getTextEncoding());
    276     paint->setTextSize(font.getTextSize());
    277     paint->setTextScaleX(font.getTextScaleX());
    278     paint->setTextSkewX(font.getTextSkewX());
    279     paint->setHinting(font.getHinting());
    280 
    281     uint32_t flagsMask = SkPaint::kAntiAlias_Flag
    282                        | SkPaint::kUnderlineText_Flag
    283                        | SkPaint::kStrikeThruText_Flag
    284                        | SkPaint::kFakeBoldText_Flag
    285                        | SkPaint::kLinearText_Flag
    286                        | SkPaint::kSubpixelText_Flag
    287                        | SkPaint::kDevKernText_Flag
    288                        | SkPaint::kLCDRenderText_Flag
    289                        | SkPaint::kEmbeddedBitmapText_Flag
    290                        | SkPaint::kAutoHinting_Flag
    291                        | SkPaint::kVerticalText_Flag
    292                        | SkPaint::kGenA8FromLCD_Flag
    293                        | SkPaint::kDistanceFieldTextTEMP_Flag;
    294     paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsMask));
    295 }
    296 
    297 SkTextBlobBuilder::SkTextBlobBuilder()
    298     : fStorageSize(0)
    299     , fStorageUsed(0)
    300     , fRunCount(0)
    301     , fDeferredBounds(false)
    302     , fLastRun(0) {
    303     fBounds.setEmpty();
    304 }
    305 
    306 SkTextBlobBuilder::~SkTextBlobBuilder() {
    307     if (NULL != fStorage.get()) {
    308         // We are abandoning runs and must destruct the associated font data.
    309         // The easiest way to accomplish that is to use the blob destructor.
    310         build()->unref();
    311     }
    312 }
    313 
    314 void SkTextBlobBuilder::updateDeferredBounds() {
    315     SkASSERT(!fDeferredBounds || fRunCount > 0);
    316 
    317     if (!fDeferredBounds) {
    318         return;
    319     }
    320 
    321     // FIXME: measure the current run & union bounds
    322     fDeferredBounds = false;
    323 }
    324 
    325 void SkTextBlobBuilder::reserve(size_t size) {
    326     // We don't currently pre-allocate, but maybe someday...
    327     if (fStorageUsed + size <= fStorageSize) {
    328         return;
    329     }
    330 
    331     if (0 == fRunCount) {
    332         SkASSERT(NULL == fStorage.get());
    333         SkASSERT(0 == fStorageSize);
    334         SkASSERT(0 == fStorageUsed);
    335 
    336         // the first allocation also includes blob storage
    337         fStorageUsed += sizeof(SkTextBlob);
    338     }
    339 
    340     fStorageSize = fStorageUsed + size;
    341     // FYI: This relies on everything we store being relocatable, particularly SkPaint.
    342     fStorage.realloc(fStorageSize);
    343 }
    344 
    345 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
    346                                  int count, SkPoint offset) {
    347     if (0 == fLastRun) {
    348         SkASSERT(0 == fRunCount);
    349         return false;
    350     }
    351 
    352     SkASSERT(fLastRun >= sizeof(SkTextBlob));
    353     SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
    354                                                                           fLastRun);
    355     SkASSERT(run->glyphCount() > 0);
    356 
    357     if (run->positioning() != positioning
    358         || run->font() != font
    359         || (run->glyphCount() + count < run->glyphCount())) {
    360         return false;
    361     }
    362 
    363     // we can merge same-font/same-positioning runs in the following cases:
    364     //   * fully positioned run following another fully positioned run
    365     //   * horizontally postioned run following another horizontally positioned run with the same
    366     //     y-offset
    367     if (SkTextBlob::kFull_Positioning != positioning
    368         && (SkTextBlob::kHorizontal_Positioning != positioning
    369             || run->offset().y() != offset.y())) {
    370         return false;
    371     }
    372 
    373     size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) -
    374                        SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning);
    375     this->reserve(sizeDelta);
    376 
    377     // reserve may have realloced
    378     run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
    379     uint32_t preMergeCount = run->glyphCount();
    380     run->grow(count);
    381 
    382     // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
    383     fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
    384     fCurrentRunBuffer.pos = run->posBuffer()
    385                           + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
    386 
    387     fStorageUsed += sizeDelta;
    388 
    389     SkASSERT(fStorageUsed <= fStorageSize);
    390     run->validate(fStorage.get() + fStorageUsed);
    391 
    392     return true;
    393 }
    394 
    395 void SkTextBlobBuilder::allocInternal(const SkPaint &font,
    396                                       SkTextBlob::GlyphPositioning positioning,
    397                                       int count, SkPoint offset, const SkRect* bounds) {
    398     SkASSERT(count > 0);
    399     SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
    400 
    401     if (!this->mergeRun(font, positioning, count, offset)) {
    402         updateDeferredBounds();
    403 
    404         size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning);
    405         this->reserve(runSize);
    406 
    407         SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
    408         SkASSERT(fStorageUsed + runSize <= fStorageSize);
    409 
    410         SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
    411                                          SkTextBlob::RunRecord(count, offset, font, positioning);
    412 
    413         fCurrentRunBuffer.glyphs = run->glyphBuffer();
    414         fCurrentRunBuffer.pos = run->posBuffer();
    415 
    416         fLastRun = fStorageUsed;
    417         fStorageUsed += runSize;
    418         fRunCount++;
    419 
    420         SkASSERT(fStorageUsed <= fStorageSize);
    421         run->validate(fStorage.get() + fStorageUsed);
    422     }
    423 
    424     if (!fDeferredBounds) {
    425         if (bounds) {
    426             fBounds.join(*bounds);
    427         } else {
    428             fDeferredBounds = true;
    429         }
    430     }
    431 }
    432 
    433 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkPaint& font, int count,
    434                                                                 SkScalar x, SkScalar y,
    435                                                                 const SkRect* bounds) {
    436     this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, SkPoint::Make(x, y), bounds);
    437 
    438     return fCurrentRunBuffer;
    439 }
    440 
    441 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count,
    442                                                                     SkScalar y,
    443                                                                     const SkRect* bounds) {
    444     this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y),
    445                         bounds);
    446 
    447     return fCurrentRunBuffer;
    448 }
    449 
    450 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint& font, int count,
    451                                                                    const SkRect *bounds) {
    452     this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds);
    453 
    454     return fCurrentRunBuffer;
    455 }
    456 
    457 const SkTextBlob* SkTextBlobBuilder::build() {
    458     SkASSERT((fRunCount > 0) == (NULL != fStorage.get()));
    459 
    460     this->updateDeferredBounds();
    461 
    462     if (0 == fRunCount) {
    463         SkASSERT(NULL == fStorage.get());
    464         fStorageUsed = sizeof(SkTextBlob);
    465         fStorage.realloc(fStorageUsed);
    466     }
    467 
    468     SkDEBUGCODE(
    469         size_t validateSize = sizeof(SkTextBlob);
    470         const SkTextBlob::RunRecord* run =
    471             SkTextBlob::RunRecord::First(reinterpret_cast<const SkTextBlob*>(fStorage.get()));
    472         for (int i = 0; i < fRunCount; ++i) {
    473             validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run->fPositioning);
    474             run->validate(fStorage.get() + fStorageUsed);
    475             run = SkTextBlob::RunRecord::Next(run);
    476         }
    477         SkASSERT(validateSize == fStorageUsed);
    478     )
    479 
    480     const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBounds);
    481     SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
    482 
    483     fStorageUsed = 0;
    484     fStorageSize = 0;
    485     fRunCount = 0;
    486     fLastRun = 0;
    487     fBounds.setEmpty();
    488 
    489     return blob;
    490 }
    491 
    492