LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkTextBlob.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 399 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 57 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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           0 : class RunFont : SkNoncopyable {
      22             : public:
      23           0 :     RunFont(const SkPaint& paint)
      24           0 :         : fSize(paint.getTextSize())
      25           0 :         , fScaleX(paint.getTextScaleX())
      26             :         , fTypeface(SkSafeRef(paint.getTypeface()))
      27           0 :         , fSkewX(paint.getTextSkewX())
      28           0 :         , fAlign(paint.getTextAlign())
      29           0 :         , fHinting(paint.getHinting())
      30           0 :         , fFlags(paint.getFlags() & kFlagsMask) { }
      31             : 
      32           0 :     void applyToPaint(SkPaint* paint) const {
      33           0 :         paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
      34           0 :         paint->setTypeface(fTypeface);
      35           0 :         paint->setTextSize(fSize);
      36           0 :         paint->setTextScaleX(fScaleX);
      37           0 :         paint->setTextSkewX(fSkewX);
      38           0 :         paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
      39           0 :         paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
      40             : 
      41           0 :         paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
      42           0 :     }
      43             : 
      44           0 :     bool operator==(const RunFont& other) const {
      45           0 :         return fTypeface == other.fTypeface
      46           0 :             && fSize == other.fSize
      47           0 :             && fScaleX == other.fScaleX
      48           0 :             && fSkewX == other.fSkewX
      49           0 :             && fAlign == other.fAlign
      50           0 :             && fHinting == other.fHinting
      51           0 :             && fFlags == other.fFlags;
      52             :     }
      53             : 
      54           0 :     bool operator!=(const RunFont& other) const {
      55           0 :         return !(*this == other);
      56             :     }
      57             : 
      58           0 :     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           0 : class SkTextBlob::RunRecord {
     134             : public:
     135           0 :     RunRecord(uint32_t count, uint32_t textSize,  const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
     136           0 :         : fFont(font)
     137             :         , fCount(count)
     138             :         , fOffset(offset)
     139           0 :         , fFlags(pos) {
     140           0 :         SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
     141             : 
     142           0 :         SkDEBUGCODE(fMagic = kRunRecordMagic);
     143           0 :         if (textSize > 0) {
     144           0 :             fFlags |= kExtended_Flag;
     145           0 :             *this->textSizePtr() = textSize;
     146             :         }
     147           0 :     }
     148             : 
     149           0 :     uint32_t glyphCount() const {
     150           0 :         return fCount;
     151             :     }
     152             : 
     153           0 :     const SkPoint& offset() const {
     154           0 :         return fOffset;
     155             :     }
     156             : 
     157           0 :     const RunFont& font() const {
     158           0 :         return fFont;
     159             :     }
     160             : 
     161           0 :     GlyphPositioning positioning() const {
     162           0 :         return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
     163             :     }
     164             : 
     165           0 :     uint16_t* glyphBuffer() const {
     166             :         static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
     167             :         // Glyphs are stored immediately following the record.
     168           0 :         return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
     169             :     }
     170             : 
     171           0 :     SkScalar* posBuffer() const {
     172             :         // Position scalars follow the (aligned) glyph buffer.
     173           0 :         return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
     174           0 :                                            SkAlign4(fCount * sizeof(uint16_t)));
     175             :     }
     176             : 
     177           0 :     uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
     178             : 
     179           0 :     uint32_t* clusterBuffer() const {
     180             :         // clusters follow the textSize.
     181           0 :         return isExtended() ? 1 + this->textSizePtr() : nullptr;
     182             :     }
     183             : 
     184           0 :     char* textBuffer() const {
     185           0 :         return isExtended()
     186           0 :             ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
     187           0 :             : nullptr;
     188             :     }
     189             : 
     190           0 :     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           0 :                       + SkAlign4(glyphCount* sizeof(uint16_t))
     196           0 :                       + PosCount(glyphCount, positioning) * sizeof(SkScalar);
     197           0 :         if (textSize > 0) {  // Extended run.
     198           0 :             size += sizeof(uint32_t)
     199             :                 + sizeof(uint32_t) * glyphCount
     200           0 :                 + textSize;
     201             :         }
     202           0 :         return SkAlignPtr(size);
     203             :     }
     204             : 
     205           0 :     static const RunRecord* First(const SkTextBlob* blob) {
     206             :         // The first record (if present) is stored following the blob object.
     207           0 :         return reinterpret_cast<const RunRecord*>(blob + 1);
     208             :     }
     209             : 
     210           0 :     static const RunRecord* Next(const RunRecord* run) {
     211           0 :         return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
     212             :     }
     213             : 
     214           0 :     void validate(const uint8_t* storageTop) const {
     215           0 :         SkASSERT(kRunRecordMagic == fMagic);
     216           0 :         SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
     217             : 
     218           0 :         SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
     219           0 :         SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
     220             :                  <= (SkScalar*)NextUnchecked(this));
     221           0 :         if (isExtended()) {
     222           0 :             SkASSERT(textSize() > 0);
     223           0 :             SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
     224           0 :             SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
     225           0 :             SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
     226             :         }
     227             :         static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
     228             :                       "runrecord_should_stay_packed");
     229           0 :     }
     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           0 :     static const RunRecord* NextUnchecked(const RunRecord* run) {
     241             :         return reinterpret_cast<const RunRecord*>(
     242             :                 reinterpret_cast<const uint8_t*>(run)
     243           0 :                 + StorageSize(run->glyphCount(), run->textSize(), run->positioning()));
     244             :     }
     245             : 
     246           0 :     static size_t PosCount(int glyphCount,
     247             :                            SkTextBlob::GlyphPositioning positioning) {
     248           0 :         return glyphCount * ScalarsPerGlyph(positioning);
     249             :     }
     250             :     
     251           0 :     uint32_t* textSizePtr() const {
     252             :         // textSize follows the position buffer.
     253           0 :         SkASSERT(isExtended());
     254           0 :         return (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning())]);
     255             :     }
     256             : 
     257           0 :     void grow(uint32_t count) {
     258           0 :         SkScalar* initialPosBuffer = posBuffer();
     259           0 :         uint32_t initialCount = fCount;
     260           0 :         fCount += count;
     261             : 
     262             :         // Move the initial pos scalars to their new location.
     263           0 :         size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
     264           0 :         SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
     265             : 
     266             :         // memmove, as the buffers may overlap
     267           0 :         memmove(posBuffer(), initialPosBuffer, copySize);
     268           0 :     }
     269             : 
     270           0 :     bool isExtended() const {
     271           0 :         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           0 : static int32_t next_id() {
     284             :     int32_t id;
     285           0 :     do {
     286           0 :         id = sk_atomic_inc(&gNextID);
     287           0 :     } while (id == SK_InvalidGenID);
     288           0 :     return id;
     289             : }
     290             : 
     291           0 : SkTextBlob::SkTextBlob(const SkRect& bounds)
     292             :     : fBounds(bounds)
     293           0 :     , fUniqueID(next_id())
     294           0 :     , fAddedToCache(false) {}
     295             : 
     296           0 : SkTextBlob::~SkTextBlob() {
     297             : #if SK_SUPPORT_GPU
     298           0 :     if (fAddedToCache.load()) {
     299           0 :         GrTextBlobCache::PostPurgeBlobMessage(fUniqueID);
     300             :     }
     301             : #endif
     302             : 
     303           0 :     const auto* run = RunRecord::First(this);
     304           0 :     do {
     305           0 :         const auto* nextRun = RunRecord::Next(run);
     306           0 :         SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
     307           0 :         run->~RunRecord();
     308           0 :         run = nextRun;
     309           0 :     } while (run);
     310           0 : }
     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           0 : void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
     324           0 :     buffer.writeRect(fBounds);
     325             : 
     326           0 :     SkPaint runPaint;
     327           0 :     SkTextBlobRunIterator it(this);
     328           0 :     while (!it.done()) {
     329           0 :         SkASSERT(it.glyphCount() > 0);
     330             : 
     331           0 :         buffer.write32(it.glyphCount());
     332             :         PositioningAndExtended pe;
     333           0 :         pe.intValue = 0;
     334           0 :         pe.positioning = it.positioning();
     335           0 :         SkASSERT((int32_t)it.positioning() == pe.intValue);  // backwards compat.
     336             : 
     337           0 :         uint32_t textSize = it.textSize();
     338           0 :         pe.extended = textSize > 0;
     339           0 :         buffer.write32(pe.intValue);
     340           0 :         if (pe.extended) {
     341           0 :             buffer.write32(textSize);
     342             :         }
     343           0 :         buffer.writePoint(it.offset());
     344             :         // This should go away when switching to SkFont
     345           0 :         it.applyFontToPaint(&runPaint);
     346           0 :         buffer.writePaint(runPaint);
     347             : 
     348           0 :         buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
     349           0 :         buffer.writeByteArray(it.pos(),
     350           0 :             it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
     351           0 :         if (pe.extended) {
     352           0 :             buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
     353           0 :             buffer.writeByteArray(it.text(), it.textSize());
     354             :         }
     355             : 
     356           0 :         it.next();
     357             :     }
     358             : 
     359             :     // Marker for the last run (0 is not a valid glyph count).
     360           0 :     buffer.write32(0);
     361           0 : }
     362             : 
     363           0 : sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
     364           0 :     const int runCount = reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)
     365           0 :         ? reader.read32() : std::numeric_limits<int>::max();
     366           0 :     if (runCount < 0) {
     367           0 :         return nullptr;
     368             :     }
     369             : 
     370             :     SkRect bounds;
     371           0 :     reader.readRect(&bounds);
     372             : 
     373           0 :     SkTextBlobBuilder blobBuilder;
     374           0 :     for (int i = 0; i < runCount; ++i) {
     375           0 :         int glyphCount = reader.read32();
     376           0 :         if (glyphCount == 0 &&
     377           0 :             !reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)) {
     378             :             // End-of-runs marker.
     379           0 :             break;
     380             :         }
     381             : 
     382             :         PositioningAndExtended pe;
     383           0 :         pe.intValue = reader.read32();
     384           0 :         GlyphPositioning pos = pe.positioning;
     385           0 :         if (glyphCount <= 0 || pos > kFull_Positioning) {
     386           0 :             return nullptr;
     387             :         }
     388           0 :         uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0;
     389             : 
     390             :         SkPoint offset;
     391           0 :         reader.readPoint(&offset);
     392           0 :         SkPaint font;
     393           0 :         reader.readPaint(&font);
     394             : 
     395           0 :         const SkTextBlobBuilder::RunBuffer* buf = nullptr;
     396           0 :         switch (pos) {
     397             :         case kDefault_Positioning:
     398             :             buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
     399           0 :                                             textSize, SkString(), &bounds);
     400           0 :             break;
     401             :         case kHorizontal_Positioning:
     402             :             buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
     403           0 :                                                 textSize, SkString(), &bounds);
     404           0 :             break;
     405             :         case kFull_Positioning:
     406           0 :             buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
     407           0 :             break;
     408             :         default:
     409           0 :             return nullptr;
     410             :         }
     411             : 
     412           0 :         if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
     413           0 :             !reader.readByteArray(buf->pos,
     414           0 :                                   glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
     415           0 :             return nullptr;
     416             :         }
     417             : 
     418           0 :         if (pe.extended) {
     419           0 :             if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t))  ||
     420           0 :                 !reader.readByteArray(buf->utf8text, textSize)) {
     421           0 :                 return nullptr;
     422             :             }
     423             :         }
     424             :     }
     425             : 
     426           0 :     return blobBuilder.make();
     427             : }
     428             : 
     429           0 : unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
     430             :     // GlyphPositioning values are directly mapped to scalars-per-glyph.
     431           0 :     SkASSERT(pos <= 2);
     432           0 :     return pos;
     433             : }
     434             : 
     435           0 : SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
     436           0 :     : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
     437           0 :     SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
     438           0 : }
     439             : 
     440           0 : bool SkTextBlobRunIterator::done() const {
     441           0 :     return !fCurrentRun;
     442             : }
     443             : 
     444           0 : void SkTextBlobRunIterator::next() {
     445           0 :     SkASSERT(!this->done());
     446             : 
     447           0 :     if (!this->done()) {
     448           0 :         SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
     449           0 :         fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
     450             :     }
     451           0 : }
     452             : 
     453           0 : uint32_t SkTextBlobRunIterator::glyphCount() const {
     454           0 :     SkASSERT(!this->done());
     455           0 :     return fCurrentRun->glyphCount();
     456             : }
     457             : 
     458           0 : const uint16_t* SkTextBlobRunIterator::glyphs() const {
     459           0 :     SkASSERT(!this->done());
     460           0 :     return fCurrentRun->glyphBuffer();
     461             : }
     462             : 
     463           0 : const SkScalar* SkTextBlobRunIterator::pos() const {
     464           0 :     SkASSERT(!this->done());
     465           0 :     return fCurrentRun->posBuffer();
     466             : }
     467             : 
     468           0 : const SkPoint& SkTextBlobRunIterator::offset() const {
     469           0 :     SkASSERT(!this->done());
     470           0 :     return fCurrentRun->offset();
     471             : }
     472             : 
     473           0 : SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
     474           0 :     SkASSERT(!this->done());
     475           0 :     return fCurrentRun->positioning();
     476             : }
     477             : 
     478           0 : void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
     479           0 :     SkASSERT(!this->done());
     480             : 
     481           0 :     fCurrentRun->font().applyToPaint(paint);
     482           0 : }
     483             : 
     484           0 : uint32_t* SkTextBlobRunIterator::clusters() const {
     485           0 :     SkASSERT(!this->done());
     486           0 :     return fCurrentRun->clusterBuffer();
     487             : }
     488           0 : uint32_t SkTextBlobRunIterator::textSize() const {
     489           0 :     SkASSERT(!this->done());
     490           0 :     return fCurrentRun->textSize();
     491             : }
     492           0 : char* SkTextBlobRunIterator::text() const {
     493           0 :     SkASSERT(!this->done());
     494           0 :     return fCurrentRun->textBuffer();
     495             : }
     496             : 
     497             : 
     498           0 : bool SkTextBlobRunIterator::isLCD() const {
     499           0 :     return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
     500             : }
     501             : 
     502           0 : SkTextBlobBuilder::SkTextBlobBuilder()
     503             :     : fStorageSize(0)
     504             :     , fStorageUsed(0)
     505             :     , fRunCount(0)
     506             :     , fDeferredBounds(false)
     507           0 :     , fLastRun(0) {
     508           0 :     fBounds.setEmpty();
     509           0 : }
     510             : 
     511           0 : SkTextBlobBuilder::~SkTextBlobBuilder() {
     512           0 :     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           0 :         this->make();
     516             :     }
     517           0 : }
     518             : 
     519           0 : SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
     520             :     SkRect bounds;
     521           0 :     SkPaint paint;
     522           0 :     run.font().applyToPaint(&paint);
     523             : 
     524           0 :     if (SkTextBlob::kDefault_Positioning == run.positioning()) {
     525           0 :         paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
     526           0 :         return bounds.makeOffset(run.offset().x(), run.offset().y());
     527             :     }
     528             : 
     529           0 :     SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
     530           0 :     paint.getTextWidths(run.glyphBuffer(),
     531           0 :                         run.glyphCount() * sizeof(uint16_t),
     532             :                         NULL,
     533           0 :                         glyphBounds.get());
     534             : 
     535           0 :     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           0 :     const SkScalar horizontalConstY = 0;
     541           0 :     const SkScalar* glyphPosX = run.posBuffer();
     542           0 :     const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
     543           0 :                                                       glyphPosX + 1 : &horizontalConstY;
     544           0 :     const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
     545           0 :     const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
     546           0 :                                                    posXInc : 0;
     547             : 
     548           0 :     bounds.setEmpty();
     549           0 :     for (unsigned i = 0; i < run.glyphCount(); ++i) {
     550           0 :         bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
     551           0 :         glyphPosX += posXInc;
     552           0 :         glyphPosY += posYInc;
     553             :     }
     554             : 
     555           0 :     SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
     556             : 
     557           0 :     return bounds.makeOffset(run.offset().x(), run.offset().y());
     558             : }
     559             : 
     560           0 : SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
     561           0 :     SkASSERT(run.glyphCount() > 0);
     562           0 :     SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
     563             :              SkTextBlob::kHorizontal_Positioning == run.positioning());
     564             : 
     565           0 :     SkPaint paint;
     566           0 :     run.font().applyToPaint(&paint);
     567           0 :     const SkRect fontBounds = paint.getFontBounds();
     568           0 :     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           0 :         return TightRunBounds(run);
     572             :     }
     573             : 
     574             :     // Compute the glyph position bbox.
     575             :     SkRect bounds;
     576           0 :     switch (run.positioning()) {
     577             :     case SkTextBlob::kHorizontal_Positioning: {
     578           0 :         const SkScalar* glyphPos = run.posBuffer();
     579           0 :         SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
     580             : 
     581           0 :         SkScalar minX = *glyphPos;
     582           0 :         SkScalar maxX = *glyphPos;
     583           0 :         for (unsigned i = 1; i < run.glyphCount(); ++i) {
     584           0 :             SkScalar x = glyphPos[i];
     585           0 :             minX = SkMinScalar(x, minX);
     586           0 :             maxX = SkMaxScalar(x, maxX);
     587             :         }
     588             : 
     589           0 :         bounds.setLTRB(minX, 0, maxX, 0);
     590           0 :     } break;
     591             :     case SkTextBlob::kFull_Positioning: {
     592           0 :         const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
     593           0 :         SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
     594             : 
     595           0 :         bounds.setBounds(glyphPosPts, run.glyphCount());
     596           0 :     } break;
     597             :     default:
     598           0 :         SkFAIL("unsupported positioning mode");
     599             :     }
     600             : 
     601             :     // Expand by typeface glyph bounds.
     602           0 :     bounds.fLeft   += fontBounds.left();
     603           0 :     bounds.fTop    += fontBounds.top();
     604           0 :     bounds.fRight  += fontBounds.right();
     605           0 :     bounds.fBottom += fontBounds.bottom();
     606             : 
     607             :     // Offset by run position.
     608           0 :     return bounds.makeOffset(run.offset().x(), run.offset().y());
     609             : }
     610             : 
     611           0 : void SkTextBlobBuilder::updateDeferredBounds() {
     612           0 :     SkASSERT(!fDeferredBounds || fRunCount > 0);
     613             : 
     614           0 :     if (!fDeferredBounds) {
     615           0 :         return;
     616             :     }
     617             : 
     618           0 :     SkASSERT(fLastRun >= sizeof(SkTextBlob));
     619           0 :     SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
     620           0 :                                                                           fLastRun);
     621             : 
     622             :     // FIXME: we should also use conservative bounds for kDefault_Positioning.
     623           0 :     SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
     624           0 :                        TightRunBounds(*run) : ConservativeRunBounds(*run);
     625           0 :     fBounds.join(runBounds);
     626           0 :     fDeferredBounds = false;
     627             : }
     628             : 
     629           0 : void SkTextBlobBuilder::reserve(size_t size) {
     630             :     // We don't currently pre-allocate, but maybe someday...
     631           0 :     if (fStorageUsed + size <= fStorageSize) {
     632           0 :         return;
     633             :     }
     634             : 
     635           0 :     if (0 == fRunCount) {
     636           0 :         SkASSERT(nullptr == fStorage.get());
     637           0 :         SkASSERT(0 == fStorageSize);
     638           0 :         SkASSERT(0 == fStorageUsed);
     639             : 
     640             :         // the first allocation also includes blob storage
     641           0 :         fStorageUsed += sizeof(SkTextBlob);
     642             :     }
     643             : 
     644           0 :     fStorageSize = fStorageUsed + size;
     645             :     // FYI: This relies on everything we store being relocatable, particularly SkPaint.
     646           0 :     fStorage.realloc(fStorageSize);
     647             : }
     648             : 
     649           0 : bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
     650             :                                  int count, SkPoint offset) {
     651           0 :     if (0 == fLastRun) {
     652           0 :         SkASSERT(0 == fRunCount);
     653           0 :         return false;
     654             :     }
     655             : 
     656           0 :     SkASSERT(fLastRun >= sizeof(SkTextBlob));
     657           0 :     SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
     658           0 :                                                                           fLastRun);
     659           0 :     SkASSERT(run->glyphCount() > 0);
     660             : 
     661           0 :     if (run->textSize() != 0) {
     662           0 :         return false;
     663             :     }
     664             : 
     665           0 :     if (run->positioning() != positioning
     666           0 :         || run->font() != font
     667           0 :         || (run->glyphCount() + count < run->glyphCount())) {
     668           0 :         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           0 :     if (SkTextBlob::kFull_Positioning != positioning
     676           0 :         && (SkTextBlob::kHorizontal_Positioning != positioning
     677           0 :             || run->offset().y() != offset.y())) {
     678           0 :         return false;
     679             :     }
     680             : 
     681           0 :     size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) -
     682           0 :                        SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning);
     683           0 :     this->reserve(sizeDelta);
     684             : 
     685             :     // reserve may have realloced
     686           0 :     run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
     687           0 :     uint32_t preMergeCount = run->glyphCount();
     688           0 :     run->grow(count);
     689             : 
     690             :     // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
     691           0 :     fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
     692           0 :     fCurrentRunBuffer.pos = run->posBuffer()
     693           0 :                           + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
     694             : 
     695           0 :     fStorageUsed += sizeDelta;
     696             : 
     697           0 :     SkASSERT(fStorageUsed <= fStorageSize);
     698           0 :     run->validate(fStorage.get() + fStorageUsed);
     699             : 
     700           0 :     return true;
     701             : }
     702             : 
     703           0 : void SkTextBlobBuilder::allocInternal(const SkPaint &font,
     704             :                                       SkTextBlob::GlyphPositioning positioning,
     705             :                                       int count, int textSize, SkPoint offset, const SkRect* bounds) {
     706           0 :     SkASSERT(count > 0);
     707           0 :     SkASSERT(textSize >= 0);
     708           0 :     SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
     709           0 :     if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
     710           0 :         this->updateDeferredBounds();
     711             : 
     712           0 :         size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning);
     713           0 :         this->reserve(runSize);
     714             : 
     715           0 :         SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
     716           0 :         SkASSERT(fStorageUsed + runSize <= fStorageSize);
     717             : 
     718           0 :         SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
     719           0 :             SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
     720           0 :         fCurrentRunBuffer.glyphs = run->glyphBuffer();
     721           0 :         fCurrentRunBuffer.pos = run->posBuffer();
     722           0 :         fCurrentRunBuffer.utf8text = run->textBuffer();
     723           0 :         fCurrentRunBuffer.clusters = run->clusterBuffer();
     724             : 
     725           0 :         fLastRun = fStorageUsed;
     726           0 :         fStorageUsed += runSize;
     727           0 :         fRunCount++;
     728             : 
     729           0 :         SkASSERT(fStorageUsed <= fStorageSize);
     730           0 :         run->validate(fStorage.get() + fStorageUsed);
     731             :     }
     732           0 :     SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
     733           0 :     SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
     734           0 :     if (!fDeferredBounds) {
     735           0 :         if (bounds) {
     736           0 :             fBounds.join(*bounds);
     737             :         } else {
     738           0 :             fDeferredBounds = true;
     739             :         }
     740             :     }
     741           0 : }
     742             : 
     743           0 : 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           0 :     this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
     749           0 :     return fCurrentRunBuffer;
     750             : }
     751             : 
     752           0 : const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
     753             :                                                                         SkScalar y,
     754             :                                                                         int textByteCount,
     755             :                                                                         SkString lang,
     756             :                                                                         const SkRect* bounds) {
     757           0 :     this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
     758           0 :                         bounds);
     759             : 
     760           0 :     return fCurrentRunBuffer;
     761             : }
     762             : 
     763           0 : const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
     764             :                                                                        int textByteCount,
     765             :                                                                        SkString lang,
     766             :                                                                        const SkRect *bounds) {
     767           0 :    this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
     768             : 
     769           0 :     return fCurrentRunBuffer;
     770             : }
     771             : 
     772           0 : sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
     773           0 :     if (!fRunCount) {
     774             :         // We don't instantiate empty blobs.
     775           0 :         SkASSERT(!fStorage.get());
     776           0 :         SkASSERT(fStorageUsed == 0);
     777           0 :         SkASSERT(fStorageSize == 0);
     778           0 :         SkASSERT(fLastRun == 0);
     779           0 :         SkASSERT(fBounds.isEmpty());
     780           0 :         return nullptr;
     781             :     }
     782             : 
     783           0 :     this->updateDeferredBounds();
     784             : 
     785             :     // Tag the last run as such.
     786           0 :     auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
     787           0 :     lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
     788             : 
     789           0 :     SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
     790           0 :     SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
     791             : 
     792           0 :     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           0 :     fStorageUsed = 0;
     806           0 :     fStorageSize = 0;
     807           0 :     fRunCount = 0;
     808           0 :     fLastRun = 0;
     809           0 :     fBounds.setEmpty();
     810             : 
     811           0 :     return sk_sp<SkTextBlob>(blob);
     812             : }

Generated by: LCOV version 1.13