Chromium Code Reviews| Index: src/core/SkTextBlob.cpp |
| diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp |
| index 1cbb2b6d6dabc9750c620b98cd1c8ec64c5adc84..7397fbeb62209395fa38f5977826f40744cee3c2 100644 |
| --- a/src/core/SkTextBlob.cpp |
| +++ b/src/core/SkTextBlob.cpp |
| @@ -110,11 +110,12 @@ SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;) |
| class SkTextBlob::RunRecord { |
| public: |
| - RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos) |
| + RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos, bool extended) |
| : fFont(font) |
| , fCount(count) |
| , fOffset(offset) |
| - , fPositioning(pos) { |
| + , fPositioning(pos) |
| + , fExtended(extended) { |
| SkDEBUGCODE(fMagic = kRunRecordMagic); |
| } |
| @@ -135,6 +136,7 @@ public: |
| } |
| uint16_t* glyphBuffer() const { |
| + static_assert(SkIsAlignPtr(sizeof(RunRecord)), ""); |
| // Glyph are stored immediately following the record. |
|
f(malita)
2016/06/27 20:21:55
nit: "Glyphs", while we're here
hal.canary
2016/06/28 15:39:13
Done.
|
| return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1); |
| } |
| @@ -145,11 +147,42 @@ public: |
| SkAlign4(fCount * sizeof(uint16_t))); |
| } |
| - static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) { |
| + uint32_t textSize() const { |
|
f(malita)
2016/06/27 20:21:55
maybe uint32_t* textSizePtr() for reuse?
hal.canary
2016/06/28 15:39:12
Done.
|
| + // textSize follows the position buffer. |
| + if (!fExtended) { return 0; } |
| + return *(uint32_t*)(&this->posBuffer()[ScalarsPerGlyph(fPositioning) * fCount]); |
|
f(malita)
2016/06/27 20:21:55
nit: reinterpret_cast instead of old-style cast?
hal.canary
2016/06/28 15:39:12
Done.
|
| + } |
| + |
| + void setTextSize(uint32_t size) { |
|
f(malita)
2016/06/27 20:21:55
I think we can get rid of the setter, see suggesti
hal.canary
2016/06/28 15:39:12
Done.
|
| + SkASSERT(fExtended); |
| + *(uint32_t*)(&this->posBuffer()[ScalarsPerGlyph(fPositioning) * fCount]) = size; |
|
f(malita)
2016/06/27 20:21:55
nit: reinterpret_cast
hal.canary
2016/06/28 15:39:12
Done.
|
| + |
| + } |
| + uint32_t* clusterBuffer() const { |
| + // clusters follow the textSize. |
| + if (!fExtended) { return nullptr; } |
| + return (uint32_t*)(&this->posBuffer()[ScalarsPerGlyph(fPositioning) * fCount]) + 1; |
|
f(malita)
2016/06/27 20:21:55
return textSizePtr() + 1 ?
hal.canary
2016/06/28 15:39:12
Done.
|
| + } |
| + |
| + char* textBuffer() const { |
| + if (!fExtended) { return nullptr; } |
| + return (char*)((uint32_t*)(&this->posBuffer()[ScalarsPerGlyph(fPositioning) * fCount]) + 1 + fCount); |
|
f(malita)
2016/06/27 20:21:55
return reinterpret_cast<char*>(clusterBuffer() + f
hal.canary
2016/06/28 15:39:13
Done.
|
| + } |
| + |
| + static size_t StorageSize(int glyphCount, int textSize, SkTextBlob::GlyphPositioning positioning) { |
| // RunRecord object + (aligned) glyph buffer + position buffer |
| - return SkAlignPtr(sizeof(SkTextBlob::RunRecord) |
| - + SkAlign4(glyphCount* sizeof(uint16_t)) |
| - + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning)); |
| + if (0 == textSize) { |
| + return SkAlignPtr(sizeof(SkTextBlob::RunRecord) |
| + + SkAlign4(glyphCount* sizeof(uint16_t)) |
| + + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning)); |
| + } else { |
| + return SkAlignPtr(sizeof(SkTextBlob::RunRecord) |
| + + SkAlign4(glyphCount* sizeof(uint16_t)) |
| + + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning) |
| + + sizeof(uint32_t) |
| + + sizeof(uint32_t) * glyphCount |
| + + textSize); |
| + } |
|
f(malita)
2016/06/27 20:21:55
nit: maybe somewhat DRY-er
size_t size = ...;
hal.canary
2016/06/28 15:39:12
Done.
|
| } |
| static const RunRecord* First(const SkTextBlob* blob) { |
| @@ -158,13 +191,15 @@ 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)); |
|
f(malita)
2016/06/27 20:21:55
We should also add asserts for the new buffers.
hal.canary
2016/06/28 15:39:12
Done.
|
| } |
| @@ -189,6 +224,7 @@ private: |
| uint32_t fCount; |
| SkPoint fOffset; |
| GlyphPositioning fPositioning; |
| + bool fExtended; |
| SkDEBUGCODE(unsigned fMagic;) |
| }; |
|
f(malita)
2016/06/27 20:21:55
Similar to RunFont above:
namespace {
struct Run
hal.canary
2016/06/28 15:39:12
Done.
|
| @@ -206,6 +242,11 @@ SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds) |
| : fRunCount(runCount) |
| , fBounds(bounds) |
| , fUniqueID(next_id()) { |
| + #ifdef SK_DEBUG |
| + static_assert(sizeof(SkTextBlob::RunRecord) == SkAlignPtr(sizeof(void*) + 29 + 4), ""); |
| + #else |
| + static_assert(sizeof(SkTextBlob::RunRecord) == SkAlignPtr(sizeof(void*) + 29), ""); |
| + #endif |
|
f(malita)
2016/06/27 20:21:55
Not needed with RunRecordStorageEquivalent above.
hal.canary
2016/06/28 15:39:12
Done.
|
| } |
| SkTextBlob::~SkTextBlob() { |
| @@ -218,6 +259,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 +282,15 @@ 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. |
| + pe.extended = it.isExtended(); |
| + buffer.write32(pe.intValue); |
| + if (pe.extended) { |
| + buffer.write32(it.textSize()); |
| + } |
| buffer.writePoint(it.offset()); |
| // This should go away when switching to SkFont |
| it.applyFontToPaint(&runPaint); |
| @@ -239,6 +299,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.clusterBuffer(), sizeof(uint32_t) * it.glyphCount()); |
| + buffer.writeByteArray(it.textBuffer(), it.textSize()); |
| + } |
| it.next(); |
| SkDEBUGCODE(runCount--); |
| @@ -258,10 +322,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 +339,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 +358,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 +427,24 @@ void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const { |
| fCurrentRun->font().applyToPaint(paint); |
| } |
| +bool SkTextBlobRunIterator::isExtended() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->textSize() > 0; |
| +} |
| +uint32_t* SkTextBlobRunIterator::clusterBuffer() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->clusterBuffer(); |
| +} |
| +uint32_t SkTextBlobRunIterator::textSize() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->textSize(); |
| +} |
| +char* SkTextBlobRunIterator::textBuffer() const { |
| + SkASSERT(!this->done()); |
| + return fCurrentRun->textBuffer(); |
| +} |
| + |
| + |
| bool SkTextBlobRunIterator::isLCD() const { |
| return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag); |
| } |
| @@ -513,6 +608,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 +628,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 +652,29 @@ 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, offset, font, positioning, textSize > 0); |
| + if (textSize > 0) { |
| + run->setTextSize(textSize); |
|
f(malita)
2016/06/27 20:21:55
Why not pass textSize to the RunRecord ctor? Then
hal.canary
2016/06/28 15:39:13
Done.
|
| + } |
| fCurrentRunBuffer.glyphs = run->glyphBuffer(); |
| fCurrentRunBuffer.pos = run->posBuffer(); |
| + fCurrentRunBuffer.utf8text = run->textBuffer(); |
| + fCurrentRunBuffer.clusters = run->clusterBuffer(); |
| fLastRun = fStorageUsed; |
| fStorageUsed += runSize; |
| @@ -578,6 +682,9 @@ void SkTextBlobBuilder::allocInternal(const SkPaint &font, |
| SkASSERT(fStorageUsed <= fStorageSize); |
| run->validate(fStorage.get() + fStorageUsed); |
| + } else { |
| + fCurrentRunBuffer.utf8text = nullptr; |
| + fCurrentRunBuffer.clusters = nullptr; |
|
f(malita)
2016/06/27 20:21:55
I don't think we need to clear these: textSize ==
hal.canary
2016/06/28 15:39:12
Done.
|
| } |
| if (!fDeferredBounds) { |
| @@ -589,26 +696,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 +743,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); |
| } |