Index: src/core/SkTextBlob.cpp |
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp |
index 3c7345f9a04df85e8b816fc21b7dbddaeff40d64..35adc24a34ff6610aa335d542c04c89f040c3291 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,31 @@ public: |
SkAlign4(fCount * sizeof(uint16_t))); |
} |
- static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) { |
+ uint32_t textSize() const { return fExtended ? *this->textSizePtr() : 0; } |
+ |
+ 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 +203,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 +255,7 @@ private: |
uint32_t fCount; |
SkPoint fOffset; |
GlyphPositioning fPositioning; |
+ bool fExtended; |
SkDEBUGCODE(unsigned fMagic;) |
}; |
@@ -218,6 +285,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 +308,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 +327,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 +350,14 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(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 +367,15 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(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 +386,13 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(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.make(); |
@@ -350,6 +455,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 +632,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 +652,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 +676,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 +703,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 +714,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 +761,8 @@ sk_sp<SkTextBlob> SkTextBlobBuilder::make() { |
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); |
} |