From: halcanary Date: Tue, 30 Aug 2016 18:58:33 +0000 (-0700) Subject: SkTextBlob: Begin implementing Extended TextBlob API X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~106^2~647 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4f0a23a8d54f5eb0fdacfff7c109b9045b548978;p=platform%2Fupstream%2FlibSkiaSharp.git SkTextBlob: Begin implementing Extended TextBlob API BUG=skia:5434 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2084533004 Review-Url: https://codereview.chromium.org/2084533004 --- diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 7cfa667..9ba8510 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -213,10 +213,11 @@ private: // V45: Add invNormRotation to SkLightingShader. // V46: Add drawTextRSXform // V47: Add occluder rect to SkBlurMaskFilter + // V48: Read and write extended SkTextBlobs. // Only SKPs within the min/current picture version range (inclusive) can be read. static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. - static const uint32_t CURRENT_PICTURE_VERSION = 47; + static const uint32_t CURRENT_PICTURE_VERSION = 48; static_assert(MIN_PICTURE_VERSION <= 41, "Remove kFontFileName and related code from SkFontDescriptor.cpp."); diff --git a/include/core/SkTextBlob.h b/include/core/SkTextBlob.h index 1addb6f..01263af 100644 --- a/include/core/SkTextBlob.h +++ b/include/core/SkTextBlob.h @@ -10,6 +10,7 @@ #include "../private/SkTemplates.h" #include "SkPaint.h" +#include "SkString.h" #include "SkRefCnt.h" class SkReadBuffer; @@ -49,7 +50,7 @@ public: return MakeFromBuffer(buffer).release(); } - enum GlyphPositioning { + enum GlyphPositioning : uint8_t { kDefault_Positioning = 0, // Default glyph advances -- zero scalars per glyph. kHorizontal_Positioning = 1, // Horizontal positioning -- one scalar per glyph. kFull_Positioning = 2 // Point positioning -- two scalars per glyph. @@ -112,11 +113,29 @@ public: /** * Glyph and position buffers associated with a run. * - * A run is a sequence of glyphs sharing the same font metrics and positioning mode. + * A run is a sequence of glyphs sharing the same font metrics + * and positioning mode. + * + * If textByteCount is 0, utf8text and clusters will be NULL (no + * character information will be associated with the glyphs). + * + * utf8text will point to a buffer of size textByteCount bytes. + * + * clusters (if not NULL) will point to an array of size count. + * For each glyph, give the byte-offset into the text for the + * first byte in the first character in that glyph's cluster. + * Each value in the array should be an integer less than + * textByteCount. Values in the array should either be + * monotonically increasing (left-to-right text) or monotonically + * decreasing (right-to-left text). This definiton is conviently + * the same as used by Harfbuzz's hb_glyph_info_t::cluster field, + * except that Harfbuzz interleaves glyphs and clusters. */ struct RunBuffer { SkGlyphID* glyphs; SkScalar* pos; + char* utf8text; + uint32_t* clusters; }; /** @@ -126,14 +145,27 @@ public: * @param font The font to be used for this run. * @param count Number of glyphs. * @param x,y Position within the blob. + * @param textByteCount length of the original UTF-8 text that + * corresponds to this sequence of glyphs. If 0, + * text will not be included in the textblob. + * @param lang Language code, currently unimplemented. * @param bounds Optional run bounding box. If known in advance (!= NULL), it will * be used when computing the blob bounds, to avoid re-measuring. * * @return A writable glyph buffer, valid until the next allocRun() or * build() call. The buffer is guaranteed to hold @count@ glyphs. */ + const RunBuffer& allocRunText(const SkPaint& font, + int count, + SkScalar x, + SkScalar y, + int textByteCount, + SkString lang, + const SkRect* bounds = NULL); const RunBuffer& allocRun(const SkPaint& font, int count, SkScalar x, SkScalar y, - const SkRect* bounds = NULL); + const SkRect* bounds = NULL) { + return this->allocRunText(font, count, x, y, 0, SkString(), bounds); + } /** * Allocates a new horizontally-positioned run and returns its writable glyph and position @@ -142,14 +174,23 @@ public: * @param font The font to be used for this run. * @param count Number of glyphs. * @param y Vertical offset within the blob. + * @param textByteCount length of the original UTF-8 text that + * corresponds to this sequence of glyphs. If 0, + * text will not be included in the textblob. + * @param lang Language code, currently unimplemented. * @param bounds Optional run bounding box. If known in advance (!= NULL), it will * be used when computing the blob bounds, to avoid re-measuring. * * @return Writable glyph and position buffers, valid until the next allocRun() * or build() call. The buffers are guaranteed to hold @count@ elements. */ + const RunBuffer& allocRunTextPosH(const SkPaint& font, int count, SkScalar y, + int textByteCount, SkString lang, + const SkRect* bounds = NULL); const RunBuffer& allocRunPosH(const SkPaint& font, int count, SkScalar y, - const SkRect* bounds = NULL); + const SkRect* bounds = NULL) { + return this->allocRunTextPosH(font, count, y, 0, SkString(), bounds); + } /** * Allocates a new fully-positioned run and returns its writable glyph and position @@ -157,6 +198,10 @@ public: * * @param font The font to be used for this run. * @param count Number of glyphs. + * @param textByteCount length of the original UTF-8 text that + * corresponds to this sequence of glyphs. If 0, + * text will not be included in the textblob. + * @param lang Language code, currently unimplemented. * @param bounds Optional run bounding box. If known in advance (!= NULL), it will * be used when computing the blob bounds, to avoid re-measuring. * @@ -164,12 +209,18 @@ public: * or build() call. The glyph buffer and position buffer are * guaranteed to hold @count@ and 2 * @count@ elements, respectively. */ - const RunBuffer& allocRunPos(const SkPaint& font, int count, const SkRect* bounds = NULL); + const RunBuffer& allocRunTextPos(const SkPaint& font, int count, + int textByteCount, SkString lang, + const SkRect* bounds = NULL); + const RunBuffer& allocRunPos(const SkPaint& font, int count, + const SkRect* bounds = NULL) { + return this->allocRunTextPos(font, count, 0, SkString(), bounds); + } private: void reserve(size_t size); void allocInternal(const SkPaint& font, SkTextBlob::GlyphPositioning positioning, - int count, SkPoint offset, const SkRect* bounds); + int count, int textBytes, SkPoint offset, const SkRect* bounds); bool mergeRun(const SkPaint& font, SkTextBlob::GlyphPositioning positioning, int count, SkPoint offset); void updateDeferredBounds(); diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp index 3c7345f..35adc24 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(const_cast(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(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(reinterpret_cast(run) - + StorageSize(run->glyphCount(), run->positioning())); + return reinterpret_cast( + reinterpret_cast(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::MakeFromBuffer(SkReadBuffer& reader) { SkTextBlobBuilder blobBuilder; for (int i = 0; i < runCount; ++i) { int glyphCount = reader.read32(); - GlyphPositioning pos = static_cast(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::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::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 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(blob) + fStorageUsed); run = SkTextBlob::RunRecord::Next(run); } diff --git a/src/core/SkTextBlobRunIterator.h b/src/core/SkTextBlobRunIterator.h index 42bbac0..2f1477b 100644 --- a/src/core/SkTextBlobRunIterator.h +++ b/src/core/SkTextBlobRunIterator.h @@ -28,6 +28,10 @@ public: const SkPoint& offset() const; void applyFontToPaint(SkPaint*) const; SkTextBlob::GlyphPositioning positioning() const; + uint32_t* clusters() const; + uint32_t textSize() const; + char* text() const; + bool isLCD() const; private: diff --git a/tests/TextBlobTest.cpp b/tests/TextBlobTest.cpp index 6107024..82bbb21 100644 --- a/tests/TextBlobTest.cpp +++ b/tests/TextBlobTest.cpp @@ -349,3 +349,40 @@ DEF_TEST(TextBlob_builder, reporter) { DEF_TEST(TextBlob_paint, reporter) { TextBlobTester::TestPaintProps(reporter); } + +DEF_TEST(TextBlob_extended, reporter) { + SkTextBlobBuilder textBlobBuilder; + SkPaint paint; + const char text1[] = "Foo"; + const char text2[] = "Bar"; + + int glyphCount = paint.textToGlyphs(text1, strlen(text1), nullptr); + SkAutoTMalloc glyphs(glyphCount); + (void)paint.textToGlyphs(text1, strlen(text1), glyphs.get()); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + auto run = textBlobBuilder.allocRunText( + paint, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr); + memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount); + memcpy(run.utf8text, text2, strlen(text2)); + for (int i = 0; i < glyphCount; ++i) { + run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2))); + } + sk_sp blob(textBlobBuilder.build()); + REPORTER_ASSERT(reporter, blob); + + for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { + REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount); + for (uint32_t i = 0; i < it.glyphCount(); ++i) { + REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]); + } + REPORTER_ASSERT(reporter, SkTextBlob::kDefault_Positioning == it.positioning()); + REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset()); + REPORTER_ASSERT(reporter, it.textSize() > 0); + REPORTER_ASSERT(reporter, it.clusters()); + for (uint32_t i = 0; i < it.glyphCount(); ++i) { + REPORTER_ASSERT(reporter, i == it.clusters()[i]); + } + REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize())); + } +} diff --git a/tools/SkShaper_harfbuzz.cpp b/tools/SkShaper_harfbuzz.cpp index 29dd1b0..2c4d211 100644 --- a/tools/SkShaper_harfbuzz.cpp +++ b/tools/SkShaper_harfbuzz.cpp @@ -105,7 +105,9 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder, hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, NULL); hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, NULL); - auto runBuffer = builder->allocRunPos(paint, len); + auto runBuffer = builder->allocRunTextPos( + paint, SkToInt(len), SkToInt(textBytes), SkString()); + memcpy(runBuffer.utf8text, utf8text, textBytes); double x = point.x(); double y = point.y(); @@ -115,12 +117,14 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder, for (unsigned i = 0; i < len; i++) { runBuffer.glyphs[i] = info[i].codepoint; + runBuffer.clusters[i] = info[i].cluster; reinterpret_cast(runBuffer.pos)[i] = SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX), SkDoubleToScalar(y - pos[i].y_offset * textSizeY)); x += pos[i].x_advance * textSizeX; y += pos[i].y_advance * textSizeY; } + hb_buffer_clear_contents(buffer); return (SkScalar)x; } diff --git a/tools/using_skia_and_harfbuzz.cpp b/tools/using_skia_and_harfbuzz.cpp index f17a26d..d405c71 100644 --- a/tools/using_skia_and_harfbuzz.cpp +++ b/tools/using_skia_and_harfbuzz.cpp @@ -151,7 +151,7 @@ public: } SkTextBlobBuilder textBlobBuilder; shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, SkPoint{0, 0}); - sk_sp blob(textBlobBuilder.build()); + sk_sp blob = textBlobBuilder.make(); pageCanvas->drawTextBlob( blob.get(), SkDoubleToScalar(current_x), SkDoubleToScalar(current_y), glyph_paint);