Chromium Code Reviews| Index: src/core/SkTextBlob.cpp |
| diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp |
| index 1cbb2b6d6dabc9750c620b98cd1c8ec64c5adc84..9b79e5bf6cb434ab27756e5a2ecfb7891d7fcce6 100644 |
| --- a/src/core/SkTextBlob.cpp |
| +++ b/src/core/SkTextBlob.cpp |
| @@ -105,17 +105,41 @@ static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_shou |
| // |
| // Each run record describes a text blob run, and can be used to determine the (implicit) |
| // location of the following record. |
| +// |
| +// Extended Textblob runs have more data after the Pos[] array: |
| +// |
| +// ------------------------------------------------------------------------- |
| +// ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ... |
| +// ------------------------------------------------------------------------- |
| +// |
| +// To determine the length of the extended run data, the TextSize must be read. |
| +// |
| +// Extended Textblob runs may be mixed with non-extended runs. |
| SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;) |
| +namespace { |
| +struct RunRecordStorageEquivalent { |
| + RunFont fFont; |
| + SkPoint fOffset; |
| + uint32_t fCount; |
| + uint32_t fFlags; |
| + SkDEBUGCODE(unsigned fMagic;) |
| +}; |
| +} |
| + |
| class SkTextBlob::RunRecord { |
| public: |
| - RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos) |
| + RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos) |
| : fFont(font) |
| , fCount(count) |
| , fOffset(offset) |
| - , fPositioning(pos) { |
| + , fPositioning(pos) |
| + , fExtended(textSize > 0) { |
| SkDEBUGCODE(fMagic = kRunRecordMagic); |
| + if (textSize > 0) { |
| + *this->textSizePtr() = textSize; |
| + } |
| } |
| uint32_t glyphCount() const { |
| @@ -135,7 +159,8 @@ public: |
| } |
| uint16_t* glyphBuffer() const { |
| - // Glyph are stored immediately following the record. |
| + static_assert(SkIsAlignPtr(sizeof(RunRecord)), ""); |
| + // Glyphs are stored immediately following the record. |
| return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1); |
| } |
| @@ -145,11 +170,36 @@ public: |
| SkAlign4(fCount * sizeof(uint16_t))); |
| } |
| - static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) { |
| + uint32_t textSize() const { return fExtended ? *this->textSizePtr() : 0; } |
| + |
| + void setTextSize(uint32_t size) { |
|
f(malita)
2016/06/28 15:57:44
We can delete the setter now.
|
| + SkASSERT(fExtended); |
| + *this->textSizePtr() = size; |
| + } |
| + |
| + uint32_t* clusterBuffer() const { |
| + // clusters follow the textSize. |
| + return fExtended ? 1 + this->textSizePtr() : nullptr; |
| + } |
| + |
| + char* textBuffer() const { |
| + if (!fExtended) { return nullptr; } |
| + return reinterpret_cast<char*>(this->clusterBuffer() + fCount); |
| + } |
| + |
| + static size_t StorageSize(int glyphCount, int textSize, |
| + SkTextBlob::GlyphPositioning positioning) { |
| + static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment"); |
| // RunRecord object + (aligned) glyph buffer + position buffer |
| - return SkAlignPtr(sizeof(SkTextBlob::RunRecord) |
| - + SkAlign4(glyphCount* sizeof(uint16_t)) |
| - + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning)); |
| + size_t size = sizeof(SkTextBlob::RunRecord) |
| + + SkAlign4(glyphCount* sizeof(uint16_t)) |
| + + PosCount(glyphCount, positioning) * sizeof(SkScalar); |
| + if (textSize > 0) { // Extended run. |
| + size += sizeof(uint32_t) |
| + + sizeof(uint32_t) * glyphCount |
| + + textSize; |
| + } |
| + return SkAlignPtr(size); |
| } |
| static const RunRecord* First(const SkTextBlob* blob) { |
| @@ -158,20 +208,41 @@ public: |
| } |
| static const RunRecord* Next(const RunRecord* run) { |
| - return reinterpret_cast<const RunRecord*>(reinterpret_cast<const uint8_t*>(run) |
| - + StorageSize(run->glyphCount(), run->positioning())); |
| + return reinterpret_cast<const RunRecord*>( |
| + reinterpret_cast<const uint8_t*>(run) |
| + + StorageSize(run->glyphCount(), run->textSize(), run->positioning())); |
| } |
| void validate(const uint8_t* storageTop) const { |
| SkASSERT(kRunRecordMagic == fMagic); |
| SkASSERT((uint8_t*)Next(this) <= storageTop); |
| + |
| SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); |
| SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(fPositioning) <= (SkScalar*)Next(this)); |
| + if (fExtended) { |
| + SkASSERT(textSize() > 0); |
| + SkASSERT(textSizePtr() < (uint32_t*)Next(this)); |
| + SkASSERT(clusterBuffer() < (uint32_t*)Next(this)); |
| + SkASSERT(textBuffer() + textSize() <= (char*)Next(this)); |
| + } |
| + static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent), |
| + "runrecord_should_stay_packed"); |
| } |
| private: |
| friend class SkTextBlobBuilder; |
| + static size_t PosCount(int glyphCount, |
| + SkTextBlob::GlyphPositioning positioning) { |
| + return glyphCount * ScalarsPerGlyph(positioning); |
| + } |
| + |
| + uint32_t* textSizePtr() const { |
| + // textSize follows the position buffer. |
| + SkASSERT(fExtended); |
| + return (uint32_t*)(&this->posBuffer()[PosCount(fCount, fPositioning)]); |
| + } |
| + |
| void grow(uint32_t count) { |
| SkScalar* initialPosBuffer = posBuffer(); |
| uint32_t initialCount = fCount; |
| @@ -189,6 +260,7 @@ private: |
| uint32_t fCount; |
| SkPoint fOffset; |
| GlyphPositioning fPositioning; |
| + bool fExtended; |
| SkDEBUGCODE(unsigned fMagic;) |
| }; |
| @@ -218,6 +290,17 @@ SkTextBlob::~SkTextBlob() { |
| } |
| } |
| +namespace { |
| +union PositioningAndExtended { |
| + int32_t intValue; |
| + struct { |
| + SkTextBlob::GlyphPositioning positioning; |
| + bool extended; |
| + uint16_t padding; |
| + }; |
| +}; |
| +} // namespace |
| + |
| void SkTextBlob::flatten(SkWriteBuffer& buffer) const { |
| int runCount = fRunCount; |
| @@ -230,7 +313,17 @@ void SkTextBlob::flatten(SkWriteBuffer& buffer) const { |
| SkASSERT(it.glyphCount() > 0); |
| buffer.write32(it.glyphCount()); |
| - buffer.write32(it.positioning()); |
| + PositioningAndExtended pe; |
| + pe.intValue = 0; |
| + pe.positioning = it.positioning(); |
| + SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat. |
| + |
| + uint32_t textSize = it.textSize(); |
| + pe.extended = textSize > 0; |
| + buffer.write32(pe.intValue); |
| + if (pe.extended) { |
| + buffer.write32(textSize); |
| + } |
| buffer.writePoint(it.offset()); |
| // This should go away when switching to SkFont |
| it.applyFontToPaint(&runPaint); |
| @@ -239,6 +332,10 @@ void SkTextBlob::flatten(SkWriteBuffer& buffer) const { |
| buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t)); |
| buffer.writeByteArray(it.pos(), |
| it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning())); |
| + if (pe.extended) { |
| + buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount()); |
| + buffer.writeByteArray(it.text(), it.textSize()); |
| + } |
| it.next(); |
| SkDEBUGCODE(runCount--); |
| @@ -258,10 +355,14 @@ const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) { |
| SkTextBlobBuilder blobBuilder; |
| for (int i = 0; i < runCount; ++i) { |
| int glyphCount = reader.read32(); |
| - GlyphPositioning pos = static_cast<GlyphPositioning>(reader.read32()); |
| + |
| + PositioningAndExtended pe; |
| + pe.intValue = reader.read32(); |
| + GlyphPositioning pos = pe.positioning; |
| if (glyphCount <= 0 || pos > kFull_Positioning) { |
| return nullptr; |
| } |
| + uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0; |
| SkPoint offset; |
| reader.readPoint(&offset); |
| @@ -271,13 +372,15 @@ const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) { |
| const SkTextBlobBuilder::RunBuffer* buf = nullptr; |
| switch (pos) { |
| case kDefault_Positioning: |
| - buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds); |
| + buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(), |
| + textSize, SkString(), &bounds); |
| break; |
| case kHorizontal_Positioning: |
| - buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds); |
| + buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(), |
| + textSize, SkString(), &bounds); |
| break; |
| case kFull_Positioning: |
| - buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds); |
| + buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds); |
| break; |
| default: |
| return nullptr; |
| @@ -288,6 +391,13 @@ const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) { |
| glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) { |
| return nullptr; |
| } |
| + |
| + if (pe.extended) { |
| + if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) || |
| + !reader.readByteArray(buf->utf8text, textSize)) { |
| + return nullptr; |
| + } |
| + } |
| } |
| return blobBuilder.build(); |
| @@ -350,6 +460,20 @@ void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const { |
| fCurrentRun->font().applyToPaint(paint); |
| } |
| +uint32_t* SkTextBlobRunIterator::clusters() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->clusterBuffer(); |
| +} |
| +uint32_t SkTextBlobRunIterator::textSize() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->textSize(); |
| +} |
| +char* SkTextBlobRunIterator::text() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->textBuffer(); |
| +} |
| + |
| + |
| bool SkTextBlobRunIterator::isLCD() const { |
| return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag); |
| } |
| @@ -513,6 +637,10 @@ bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni |
| fLastRun); |
| SkASSERT(run->glyphCount() > 0); |
| + if (run->textSize() != 0) { |
| + return false; |
| + } |
| + |
| if (run->positioning() != positioning |
| || run->font() != font |
| || (run->glyphCount() + count < run->glyphCount())) { |
| @@ -529,8 +657,8 @@ bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni |
| return false; |
| } |
| - size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) - |
| - SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning); |
| + size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) - |
| + SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning); |
| this->reserve(sizeDelta); |
| // reserve may have realloced |
| @@ -553,24 +681,25 @@ bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni |
| void SkTextBlobBuilder::allocInternal(const SkPaint &font, |
| SkTextBlob::GlyphPositioning positioning, |
| - int count, SkPoint offset, const SkRect* bounds) { |
| + int count, int textSize, SkPoint offset, const SkRect* bounds) { |
| SkASSERT(count > 0); |
| + SkASSERT(textSize >= 0); |
| SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); |
| - |
| - if (!this->mergeRun(font, positioning, count, offset)) { |
| + if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) { |
| this->updateDeferredBounds(); |
| - size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning); |
| + size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning); |
| this->reserve(runSize); |
| SkASSERT(fStorageUsed >= sizeof(SkTextBlob)); |
| SkASSERT(fStorageUsed + runSize <= fStorageSize); |
| SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) |
| - SkTextBlob::RunRecord(count, offset, font, positioning); |
| - |
| + SkTextBlob::RunRecord(count, textSize, offset, font, positioning); |
| fCurrentRunBuffer.glyphs = run->glyphBuffer(); |
| fCurrentRunBuffer.pos = run->posBuffer(); |
| + fCurrentRunBuffer.utf8text = run->textBuffer(); |
| + fCurrentRunBuffer.clusters = run->clusterBuffer(); |
| fLastRun = fStorageUsed; |
| fStorageUsed += runSize; |
| @@ -579,7 +708,8 @@ void SkTextBlobBuilder::allocInternal(const SkPaint &font, |
| SkASSERT(fStorageUsed <= fStorageSize); |
| run->validate(fStorage.get() + fStorageUsed); |
| } |
| - |
| + SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text); |
| + SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters); |
| if (!fDeferredBounds) { |
| if (bounds) { |
| fBounds.join(*bounds); |
| @@ -589,26 +719,31 @@ void SkTextBlobBuilder::allocInternal(const SkPaint &font, |
| } |
| } |
| -const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkPaint& font, int count, |
| - SkScalar x, SkScalar y, |
| - const SkRect* bounds) { |
| - this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, SkPoint::Make(x, y), bounds); |
| - |
| +const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count, |
| + SkScalar x, SkScalar y, |
| + int textByteCount, |
| + SkString lang, |
| + const SkRect* bounds) { |
| + this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds); |
| return fCurrentRunBuffer; |
| } |
| -const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count, |
| - SkScalar y, |
| - const SkRect* bounds) { |
| - this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y), |
| +const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count, |
| + SkScalar y, |
| + int textByteCount, |
| + SkString lang, |
| + const SkRect* bounds) { |
| + this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y), |
| bounds); |
| return fCurrentRunBuffer; |
| } |
| -const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint& font, int count, |
| - const SkRect *bounds) { |
| - this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds); |
| +const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count, |
| + int textByteCount, |
| + SkString lang, |
| + const SkRect *bounds) { |
| + this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds); |
| return fCurrentRunBuffer; |
| } |
| @@ -631,7 +766,8 @@ const SkTextBlob* SkTextBlobBuilder::build() { |
| size_t validateSize = sizeof(SkTextBlob); |
| const SkTextBlob::RunRecord* run = SkTextBlob::RunRecord::First(blob); |
| for (int i = 0; i < fRunCount; ++i) { |
| - validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run->fPositioning); |
| + validateSize += SkTextBlob::RunRecord::StorageSize( |
| + run->fCount, run->textSize(), run->fPositioning); |
| run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed); |
| run = SkTextBlob::RunRecord::Next(run); |
| } |