diff options
-rw-r--r-- | src/core/SkGlyphRun.cpp | 256 | ||||
-rw-r--r-- | src/core/SkGlyphRun.h | 104 | ||||
-rw-r--r-- | tests/GlyphRunTest.cpp | 66 |
3 files changed, 330 insertions, 96 deletions
diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp index 29c1dfdb78..61909656c0 100644 --- a/src/core/SkGlyphRun.cpp +++ b/src/core/SkGlyphRun.cpp @@ -19,9 +19,12 @@ #include "SkPaint.h" #include "SkPaintPriv.h" #include "SkStrikeCache.h" +#include "SkTextBlob.h" +#include "SkTextBlobRunIterator.h" #include "SkTo.h" #include "SkUtils.h" +namespace { static SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) { switch (encoding) { case SkPaint::kUTF8_TextEncoding: return SkTypeface::kUTF8_Encoding; @@ -30,8 +33,15 @@ static SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) { default: return SkTypeface::kUTF32_Encoding; } } +} // namespace + // -- SkGlyphSet ---------------------------------------------------------------------------------- +uint32_t SkGlyphSet::uniqueSize() { + // The size is how big the vector is grown since being passed into reuse. + return fUniqueGlyphIDs->size() - fStartOfUniqueIDs; +} + uint16_t SkGlyphSet::add(SkGlyphID glyphID) { static constexpr SkGlyphID kUndefGlyph{0}; @@ -44,11 +54,13 @@ uint16_t SkGlyphSet::add(SkGlyphID glyphID) { } auto index = fIndices[glyphID]; - if (index < fUniqueGlyphIDs->size() && (*fUniqueGlyphIDs)[index] == glyphID) { + + // Remember we start at the end of what ever was passed in. + if (index < this->uniqueSize() && (*fUniqueGlyphIDs)[fStartOfUniqueIDs + index] == glyphID) { return index; } - uint16_t newIndex = SkTo<uint16_t>(fUniqueGlyphIDs->size()); + uint16_t newIndex = SkTo<uint16_t>(this->uniqueSize()); fUniqueGlyphIDs->push_back(glyphID); fIndices[glyphID] = newIndex; return newIndex; @@ -58,15 +70,12 @@ void SkGlyphSet::reuse(uint32_t glyphUniverseSize, std::vector<SkGlyphID>* uniqu SkASSERT(glyphUniverseSize <= (1 << 16)); fUniverseSize = glyphUniverseSize; fUniqueGlyphIDs = uniqueGlyphIDs; - // If we're hanging onto these arrays for a long time, we don't want their size to drift - // endlessly upwards. It's unusual to see more than 256 unique glyphs used in a run, - // or a typeface with more than 4096 possible glyphs. - if (fUniqueGlyphIDs->size() > 256) { - fUniqueGlyphIDs->resize(256); - fUniqueGlyphIDs->shrink_to_fit(); - } - fUniqueGlyphIDs->clear(); + // Capture the vector end to act as the start of a new unique id vector. + fStartOfUniqueIDs = uniqueGlyphIDs->size(); + + // If we're hanging onto these arrays for a long time, we don't want their size to drift + // endlessly upwards. It's unusual to see a typeface with more than 4096 possible glyphs. if (glyphUniverseSize < 4096 && fIndices.size() > 4096) { fIndices.resize(4096); fIndices.shrink_to_fit(); @@ -77,6 +86,21 @@ void SkGlyphSet::reuse(uint32_t glyphUniverseSize, std::vector<SkGlyphID>* uniqu } // -- SkGlyphRun ----------------------------------------------------------------------------------- +SkGlyphRun::SkGlyphRun(SkSpan<uint16_t> denseIndex, + SkSpan<SkPoint> positions, + SkSpan<SkGlyphID> scratchGlyphs, + SkSpan<SkGlyphID> uniqueGlyphIDs, + SkSpan<const char> text, + SkSpan<uint32_t> clusters) + : fDenseIndex{denseIndex}, fPositions{positions} + , fTemporaryShuntGlyphIDs{scratchGlyphs} + , fUniqueGlyphIDs{uniqueGlyphIDs} + , fText{text} + , fClusters{clusters} { + SkASSERT(denseIndex.size() == positions.size()); + SkASSERT(denseIndex.size() == scratchGlyphs.size()); +} + void SkGlyphRun::temporaryShuntToDrawPosText(const SkPaint& paint, SkBaseDevice* device) { @@ -93,78 +117,108 @@ void SkGlyphRun::temporaryShuntToCallback(TemporaryShuntCallback callback) { callback(this->runSize(), bytes, pos); } +// -- SkGlyphRunList ------------------------------------------------------------------------------- +SkGlyphRunList::SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, uint64_t uniqueID) + : fUniqueID{uniqueID} + , fGlyphRuns{glyphRuns} { } // -- SkGlyphRunBuilder ---------------------------------------------------------------------------- void SkGlyphRunBuilder::prepareDrawText( const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin) { - - this->initializeDenseAndUnique(paint, bytes, byteLength); - - fScratchAdvances.resize(this->uniqueSize()); - { - auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint); - cache->getAdvances(SkSpan<SkGlyphID>{fUniqueGlyphs}, fScratchAdvances.data()); - } - - SkPoint endOfLastGlyph = origin; - - for (size_t i = 0; i < this->runSize(); i++) { - fPositions.push_back(endOfLastGlyph); - endOfLastGlyph += fScratchAdvances[fDenseIndex[i]]; + this->initialize(); + SkSpan<const char> originalText((const char*)bytes, byteLength); + if (paint.getTextEncoding() != SkPaint::kUTF8_TextEncoding) { + originalText = SkSpan<const char>(); } - - if (paint.getTextAlign() != SkPaint::kLeft_Align) { - SkVector len = endOfLastGlyph - origin; - if (paint.getTextAlign() == SkPaint::kCenter_Align) { - len.scale(SK_ScalarHalf); - } - for (size_t i = 0; i < this->runSize(); i++) { - fPositions[i] -= len; - } - } - + this->drawText(paint, bytes, byteLength, origin, originalText, SkSpan<uint32_t>()); } void SkGlyphRunBuilder::prepareDrawPosTextH(const SkPaint& paint, const void* bytes, size_t byteLength, const SkScalar* xpos, SkScalar constY) { - - this->initializeDenseAndUnique(paint, bytes, byteLength); - - for (size_t i = 0; i < runSize(); i++) { - fPositions.push_back(SkPoint::Make(xpos[i], constY)); - } + this->initialize(); + this->drawPosTextH( + paint, bytes, byteLength, xpos, constY, SkSpan<const char>(), SkSpan<uint32_t>()); } void SkGlyphRunBuilder::prepareDrawPosText(const SkPaint& paint, const void* bytes, size_t byteLength, const SkPoint* pos) { - this->initializeDenseAndUnique(paint, bytes, byteLength); + this->initialize(); + this->drawPosText(paint, bytes, byteLength, pos, SkSpan<const char>(), SkSpan<uint32_t>()); +} - for (size_t i = 0; i < runSize(); i++) { - fPositions.push_back(pos[i]); +void SkGlyphRunBuilder::prepareTextBlob( + const SkPaint& paint, const SkTextBlob& blob, SkPoint origin) { + this->initialize(); + fUniqueID = blob.uniqueID(); + + SkPaint runPaint = paint; + + for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { + // applyFontToPaint() always overwrites the exact same attributes, + // so it is safe to not re-seed the paint for this reason. + it.applyFontToPaint(&runPaint); + + // These better be glyphs + SkASSERT(runPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); + + auto text = SkSpan<const char>(it.text(), it.textSize()); + auto clusters = SkSpan<uint32_t>(it.clusters(), it.glyphCount()); + size_t glyphLen = it.glyphCount() * sizeof(SkGlyphID); + const SkPoint& offset = it.offset(); + + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: { + auto dtOrigin = origin + offset; + this->drawText(runPaint, it.glyphs(), glyphLen, dtOrigin, text, clusters); + } + break; + case SkTextBlob::kHorizontal_Positioning: { + auto constY = origin.y() + offset.y(); + this->drawPosTextH( + runPaint, it.glyphs(), glyphLen, it.pos(), constY, text, clusters); + } + break; + case SkTextBlob::kFull_Positioning: + this->drawPosText( + runPaint, it.glyphs(), glyphLen, (const SkPoint*)it.pos(), text, clusters); + break; + default: + SK_ABORT("unhandled positioning mode"); + } } } SkGlyphRun* SkGlyphRunBuilder::useGlyphRun() { - fScratchGlyphRun.~SkGlyphRun(); - new ((void*)&fScratchGlyphRun) SkGlyphRun{SkSpan<uint16_t>(fDenseIndex), - SkSpan<SkPoint>(fPositions), - SkSpan<SkGlyphID>( - fTemporaryShuntGlyphIDs, fDenseIndex.size()), - SkSpan<SkGlyphID>(fUniqueGlyphs)}; - return &fScratchGlyphRun; + auto glyphRunList = this->useGlyphRunList(); + SkASSERT(glyphRunList->size() == 1); + return &(*glyphRunList)[0]; } -void SkGlyphRunBuilder::initializeDenseAndUnique( - const SkPaint& paint, const void* bytes, size_t byteLength) { +SkGlyphRunList* SkGlyphRunBuilder::useGlyphRunList() { + new ((void*)&fScratchGlyphRunList) SkGlyphRunList{SkSpan<SkGlyphRun>(fGlyphRuns), fUniqueID}; + return &fScratchGlyphRunList; +} + +size_t SkGlyphRunBuilder::runSize() const { return fDenseIndex.size() - fLastDenseIndex; } + +size_t SkGlyphRunBuilder::uniqueSize() const { return fUniqueGlyphs.size() - fLastUniqueIndex; } +void SkGlyphRunBuilder::initialize() { + fUniqueID = 0; fDenseIndex.clear(); fPositions.clear(); fUniqueGlyphs.clear(); - fTemporaryShuntGlyphIDs = nullptr; + fGlyphRuns.clear(); + fLastDenseIndex = 0; + fLastUniqueIndex = 0; +} + +SkGlyphID* SkGlyphRunBuilder::addDenseAndUnique( + const SkPaint& paint, const void* bytes, size_t byteLength) { size_t runSize = 0; - const SkGlyphID* glyphIDs = nullptr; + SkGlyphID* glyphIDs = nullptr; auto encoding = paint.getTextEncoding(); auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint); if (encoding != SkPaint::kGlyphID_TextEncoding) { @@ -178,18 +232,98 @@ void SkGlyphRunBuilder::initializeDenseAndUnique( } } else { runSize = byteLength / 2; - glyphIDs = (const SkGlyphID*)bytes; + glyphIDs = (SkGlyphID*)bytes; } SkASSERT(glyphIDs != nullptr); - if (runSize == 0) { return; } - fTemporaryShuntGlyphIDs = glyphIDs; + if (runSize > 0) { + fGlyphSet.reuse(typeface->countGlyphs(), &fUniqueGlyphs); + for (size_t i = 0; i < runSize; i++) { + fDenseIndex.push_back(fGlyphSet.add(glyphIDs[i])); + } + } + + return glyphIDs; +} + +void SkGlyphRunBuilder::addGlyphRunToList( + SkGlyphID* temporaryShuntGlyphIDs, SkSpan<const char> text, SkSpan<uint32_t> clusters) { + + // Ignore empty runs. + if (fDenseIndex.size() != fLastDenseIndex) { + auto runSize = this->runSize(); + auto uniqueSize = this->uniqueSize(); + + fGlyphRuns.emplace_back( + SkSpan<uint16_t>(&fDenseIndex[fLastDenseIndex], runSize), + SkSpan<SkPoint>(&fPositions[fLastDenseIndex], runSize), + SkSpan<SkGlyphID>(temporaryShuntGlyphIDs, runSize), + SkSpan<SkGlyphID>(&fUniqueGlyphs[fLastDenseIndex], uniqueSize), + text, + clusters); + + fLastDenseIndex = fDenseIndex.size(); + fLastUniqueIndex = fUniqueGlyphs.size(); + } +} + +void SkGlyphRunBuilder::drawText( + const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin, + SkSpan<const char> text, SkSpan<uint32_t> clusters) { + + SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength); + + fScratchAdvances.resize(this->uniqueSize()); + { + auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint); + cache->getAdvances(SkSpan<SkGlyphID>{fUniqueGlyphs}, fScratchAdvances.data()); + } + + SkPoint endOfLastGlyph = origin; + + for (size_t i = 0; i < this->runSize(); i++) { + fPositions.push_back(endOfLastGlyph); + endOfLastGlyph += fScratchAdvances[fDenseIndex[i]]; + } + + if (paint.getTextAlign() != SkPaint::kLeft_Align) { + SkVector len = endOfLastGlyph - origin; + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + len.scale(SK_ScalarHalf); + } + for (size_t i = fLastDenseIndex; i < this->runSize(); i++) { + fPositions[i] -= len; + } + } + + this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters); +} - fGlyphSet.reuse(typeface->countGlyphs(), &fUniqueGlyphs); - for (size_t i = 0; i < runSize; i++) { - fDenseIndex.push_back(fGlyphSet.add(glyphIDs[i])); +void SkGlyphRunBuilder::drawPosTextH(const SkPaint& paint, const void* bytes, + size_t byteLength, const SkScalar* xpos, + SkScalar constY, + SkSpan<const char> text, SkSpan<uint32_t> clusters) { + + SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength); + + for (size_t i = 0; i < runSize(); i++) { + fPositions.push_back(SkPoint::Make(xpos[i], constY)); + } + + this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters); +} + +void SkGlyphRunBuilder::drawPosText(const SkPaint& paint, const void* bytes, + size_t byteLength, const SkPoint* pos, + SkSpan<const char> text, SkSpan<uint32_t> clusters) { + SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength); + + for (size_t i = 0; i < runSize(); i++) { + fPositions.push_back(pos[i]); } + + this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters); } diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h index 243d5d121c..1b41da5350 100644 --- a/src/core/SkGlyphRun.h +++ b/src/core/SkGlyphRun.h @@ -24,36 +24,32 @@ template <typename T> class SkSpan { public: SkSpan() : fPtr{nullptr}, fSize{0} {} - SkSpan(const T* ptr, size_t size) : fPtr{ptr}, fSize{size} {} - explicit SkSpan(const std::vector<T>& v) : fPtr{v.data()}, fSize{v.size()} {} - const T& operator [] (ptrdiff_t i) const { return fPtr[i]; } + SkSpan(T* ptr, size_t size) : fPtr{ptr}, fSize{size} {} + explicit SkSpan(std::vector<T>& v) : fPtr{v.data()}, fSize{v.size()} {} + SkSpan& operator=( const SkSpan& other ) = default; + T& operator [] (ptrdiff_t i) const { return fPtr[i]; } T* begin() const { return fPtr; } T* end() const { return fPtr + fSize; } const T* cbegin() const { return fPtr; } const T* cend() const { return fPtr + fSize; } - const T* data() const { return fPtr; } + T* data() const { return fPtr; } ptrdiff_t size() const { return fSize; } bool empty() const { return fSize == 0; } private: - const T* fPtr; + T* fPtr; size_t fSize; }; class SkGlyphRun { public: SkGlyphRun() = default; - SkGlyphRun(SkSpan<uint16_t> denseIndex, - SkSpan<SkPoint> positions, - SkSpan<SkGlyphID> scratchGlyphs, - SkSpan<SkGlyphID> uniqueGlyphIDs) - : fDenseIndex{denseIndex} - , fPositions{positions} - , fTemporaryShuntGlyphIDs{scratchGlyphs} - , fUniqueGlyphIDs{uniqueGlyphIDs} { - SkASSERT(denseIndex.size() == positions.size()); - SkASSERT(denseIndex.size() == scratchGlyphs.size()); - } + SkGlyphRun(SkSpan<uint16_t> denseIndex, + SkSpan<SkPoint> positions, + SkSpan<SkGlyphID> scratchGlyphs, + SkSpan<SkGlyphID> uniqueGlyphIDs, + SkSpan<const char> text, + SkSpan<uint32_t> clusters); // The temporaryShunt calls are to allow inter-operating with existing code while glyph runs // are developed. @@ -67,19 +63,43 @@ public: private: // Indices into the unique glyph IDs. On for each original glyph. - const SkSpan<uint16_t> fDenseIndex; + const SkSpan<uint16_t> fDenseIndex; // The base line position of all the glyphs in source space. - const SkSpan<SkPoint> fPositions; + const SkSpan<SkPoint> fPositions; // This is temporary while converting from the old per glyph code to the bulk code. - const SkSpan<SkGlyphID> fTemporaryShuntGlyphIDs; + const SkSpan<SkGlyphID> fTemporaryShuntGlyphIDs; // The set of unique glyphs in the run. - const SkSpan<SkGlyphID> fUniqueGlyphIDs; + const SkSpan<SkGlyphID> fUniqueGlyphIDs; + // Original text from SkTextBlob if present. Will be empty of not present. + const SkSpan<const char> fText; + // Original clusters from SkTextBlob if present. Will be empty if not present. + const SkSpan<uint32_t> fClusters; +}; + +class SkGlyphRunList { + const uint64_t fUniqueID{0}; + SkSpan<SkGlyphRun> fGlyphRuns; + +public: + SkGlyphRunList() = default; + SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, uint64_t uniqueID); + + uint64_t uniqueID() const { return fUniqueID; } + + auto begin() -> decltype(fGlyphRuns.begin()) { return fGlyphRuns.begin(); } + auto end() -> decltype(fGlyphRuns.end()) { return fGlyphRuns.end(); } + auto size() -> decltype(fGlyphRuns.size()) { return fGlyphRuns.size(); } + auto operator [] (ptrdiff_t i) -> decltype(fGlyphRuns[i]) { return fGlyphRuns[i]; } }; // A faster set implementation that does not need any initialization, and reading the set items // is order the number of items, and not the size of the universe. // This implementation is based on the paper by Briggs and Torczon, "An Efficient Representation // for Sparse Sets" +// +// This implementation assumes that the unique glyphs added are appended to a vector that may +// already have unique glyph from a previous computation. This allows the packing of multiple +// UniqueID sequences in a single vector. class SkGlyphSet { public: SkGlyphSet() = default; @@ -87,15 +107,13 @@ public: void reuse(uint32_t glyphUniverseSize, std::vector<SkGlyphID>* uniqueGlyphIDs); private: + uint32_t uniqueSize(); uint32_t fUniverseSize{0}; + size_t fStartOfUniqueIDs{0}; std::vector<uint16_t> fIndices; std::vector<SkGlyphID>* fUniqueGlyphIDs{nullptr}; }; -// Currently the old code is passing around SkGlyphRunBuilder because it facilitates working in the -// old single glyph lookup style with the cache. When the lower level code is transitioned over to -// the bulk glyph cache style, then the builder will only be used in the canvas, and only runs will -// be passed around. class SkGlyphRunBuilder { public: SkGlyphRunBuilder() = default; @@ -106,32 +124,50 @@ public: const SkScalar xpos[], SkScalar constY); void prepareDrawPosText( const SkPaint& paint, const void* bytes, size_t byteLength, const SkPoint pos[]); + void prepareTextBlob(const SkPaint& paint, const SkTextBlob& blob, SkPoint origin); - size_t runSize() const {return fDenseIndex.size();} - size_t uniqueSize() const {return fUniqueGlyphs.size();} - + SkGlyphRunList* useGlyphRunList(); SkGlyphRun* useGlyphRun(); private: - void initializeDenseAndUnique(const SkPaint& paint, const void* bytes, size_t byteLength); + size_t runSize() const; + size_t uniqueSize() const; + void initialize(); + SkGlyphID* addDenseAndUnique(const SkPaint& paint, const void* bytes, size_t byteLength); + void addGlyphRunToList( + SkGlyphID* temporaryShuntGlyphIDs, SkSpan<const char> text, SkSpan<uint32_t> clusters); + + void drawText( + const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin, + SkSpan<const char> text, SkSpan<uint32_t> clusters); + void drawPosTextH( + const SkPaint& paint, const void* bytes, size_t byteLength, + const SkScalar* xpos, SkScalar constY, + SkSpan<const char> text, SkSpan<uint32_t> clusters); + void drawPosText( + const SkPaint& paint, const void* bytes, size_t byteLength, const SkPoint* pos, + SkSpan<const char> text, SkSpan<uint32_t> clusters); + + uint64_t fUniqueID{0}; std::vector<uint16_t> fDenseIndex; std::vector<SkPoint> fPositions; std::vector<SkGlyphID> fUniqueGlyphs; + size_t fLastDenseIndex{0}; + size_t fLastUniqueIndex{0}; + // Used as a temporary for preparing using utfN text. std::vector<SkGlyphID> fScratchGlyphIDs; // Used as temporary storage for calculating positions for drawText. std::vector<SkPoint> fScratchAdvances; - // Used to temporarily use of a glyph run for bulk cache API calls (just an experiment at - // this point). - SkGlyphRun fScratchGlyphRun; + // Vector for accumulating runs. This is later deposited in fScratchGlyphRunList; + std::vector<SkGlyphRun> fGlyphRuns; - // Used as an aid to shunt from glyph runs to drawPosText. It will either be fScratchIDs or - // the bytes passed in. - const SkGlyphID* fTemporaryShuntGlyphIDs{nullptr}; + // Used as temporary glyph run for the rest of the Text stack. + SkGlyphRunList fScratchGlyphRunList; // Used for collecting the set of unique glyphs. SkGlyphSet fGlyphSet; diff --git a/tests/GlyphRunTest.cpp b/tests/GlyphRunTest.cpp index 3b25625a7a..ca97404e7c 100644 --- a/tests/GlyphRunTest.cpp +++ b/tests/GlyphRunTest.cpp @@ -7,9 +7,32 @@ #include "SkGlyphRun.h" +#include "SkTextBlob.h" + #include "Test.h" -DEF_TEST(GlyphRunInfo, reporter) { +DEF_TEST(GlyphSetBasic, reporter) { + SkGlyphSet set; + + std::vector<SkGlyphID> unique; + + set.reuse(10, &unique); + REPORTER_ASSERT(reporter, set.add(7) == 0); + REPORTER_ASSERT(reporter, set.add(3) == 1); + set.reuse(10, &unique); + REPORTER_ASSERT(reporter, set.add(5) == 0); + REPORTER_ASSERT(reporter, set.add(8) == 1); + REPORTER_ASSERT(reporter, set.add(3) == 2); + + REPORTER_ASSERT(reporter, unique.size() == 5); + REPORTER_ASSERT(reporter, unique[0] == 7); + REPORTER_ASSERT(reporter, unique[1] == 3); + REPORTER_ASSERT(reporter, unique[2] == 5); + REPORTER_ASSERT(reporter, unique[3] == 8); + REPORTER_ASSERT(reporter, unique[4] == 3); +} + +DEF_TEST(GlyphRunBasic, reporter) { SkGlyphID glyphs[] = {100, 3, 240, 3, 234, 111, 3, 4, 10, 11}; uint16_t count = SK_ARRAY_COUNT(glyphs); @@ -18,5 +41,46 @@ DEF_TEST(GlyphRunInfo, reporter) { SkGlyphRunBuilder builder; builder.prepareDrawText(paint, glyphs, count, SkPoint::Make(0, 0)); +} + +DEF_TEST(GlyphRunBlob, reporter) { + constexpr uint16_t count = 10; + + auto tf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + + SkPaint font; + font.setTypeface(tf); + font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + font.setTextAlign(SkPaint::kLeft_Align); + font.setStyle(SkPaint::kFill_Style); + font.setHinting(SkPaint::kNormal_Hinting); + font.setTextSize(1u); + + SkTextBlobBuilder blobBuilder; + for (int runNum = 0; runNum < 2; runNum++) { + const auto& runBuffer = blobBuilder.allocRunPosH(font, count, runNum); + SkASSERT(runBuffer.utf8text == nullptr); + SkASSERT(runBuffer.clusters == nullptr); + + for (int i = 0; i < count; i++) { + runBuffer.glyphs[i] = static_cast<SkGlyphID>(i + runNum * 10); + runBuffer.pos[i] = SkIntToScalar(i + runNum * 10); + } + } + + auto blob = blobBuilder.make(); + + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkGlyphRunBuilder runBuilder; + runBuilder.prepareTextBlob(font, *blob, SkPoint::Make(0, 0)); + + auto runList = runBuilder.useGlyphRunList(); + REPORTER_ASSERT(reporter, runList->size() == 2); + for (auto& run : *runList) { + REPORTER_ASSERT(reporter, run.runSize() == 10); + REPORTER_ASSERT(reporter, run.uniqueSize() == 10); + } }
\ No newline at end of file |