aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Herb Derby <herb@google.com>2018-06-18 19:13:37 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-06-22 15:26:17 +0000
commitb9177cfaa8f56cc2f561669dc4e68cf6d43f529d (patch)
treee616eff6c9a54b7f21218e1d647e51914ec529e4
parent198498b010b92fae139df506a39fd4da8e846238 (diff)
Add SkGlyphRunList - v2
Extend the glyph run system with a glyph run list. This allows the processing of text blobs. Add original text an cluster to runs for PDF. PS - the original had read off the end of a buffer problem. Change-Id: I9430f0c27aaa3d9458bfe3caba5f433b72fdf84c Reviewed-on: https://skia-review.googlesource.com/136792 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Herb Derby <herb@google.com>
-rw-r--r--src/core/SkGlyphRun.cpp256
-rw-r--r--src/core/SkGlyphRun.h104
-rw-r--r--tests/GlyphRunTest.cpp66
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