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>
This commit is contained in:
parent
198498b010
commit
b9177cfaa8
@ -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,12 +117,162 @@ 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->initialize();
|
||||
SkSpan<const char> originalText((const char*)bytes, byteLength);
|
||||
if (paint.getTextEncoding() != SkPaint::kUTF8_TextEncoding) {
|
||||
originalText = SkSpan<const char>();
|
||||
}
|
||||
this->drawText(paint, bytes, byteLength, origin, originalText, SkSpan<uint32_t>());
|
||||
}
|
||||
|
||||
this->initializeDenseAndUnique(paint, bytes, byteLength);
|
||||
void SkGlyphRunBuilder::prepareDrawPosTextH(const SkPaint& paint, const void* bytes,
|
||||
size_t byteLength, const SkScalar* xpos,
|
||||
SkScalar 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->initialize();
|
||||
this->drawPosText(paint, bytes, byteLength, pos, SkSpan<const char>(), SkSpan<uint32_t>());
|
||||
}
|
||||
|
||||
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() {
|
||||
auto glyphRunList = this->useGlyphRunList();
|
||||
SkASSERT(glyphRunList->size() == 1);
|
||||
return &(*glyphRunList)[0];
|
||||
}
|
||||
|
||||
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();
|
||||
fGlyphRuns.clear();
|
||||
fLastDenseIndex = 0;
|
||||
fLastUniqueIndex = 0;
|
||||
}
|
||||
|
||||
SkGlyphID* SkGlyphRunBuilder::addDenseAndUnique(
|
||||
const SkPaint& paint, const void* bytes, size_t byteLength) {
|
||||
|
||||
size_t runSize = 0;
|
||||
SkGlyphID* glyphIDs = nullptr;
|
||||
auto encoding = paint.getTextEncoding();
|
||||
auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint);
|
||||
if (encoding != SkPaint::kGlyphID_TextEncoding) {
|
||||
auto tfEncoding = convert_encoding(encoding);
|
||||
int utfSize = SkUTFN_CountUnichars(tfEncoding, bytes, byteLength);
|
||||
if (utfSize > 0) {
|
||||
runSize = SkTo<size_t>(utfSize);
|
||||
fScratchGlyphIDs.resize(runSize);
|
||||
typeface->charsToGlyphs(bytes, tfEncoding, fScratchGlyphIDs.data(), runSize);
|
||||
glyphIDs = fScratchGlyphIDs.data();
|
||||
}
|
||||
} else {
|
||||
runSize = byteLength / 2;
|
||||
glyphIDs = (SkGlyphID*)bytes;
|
||||
}
|
||||
|
||||
SkASSERT(glyphIDs != nullptr);
|
||||
|
||||
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());
|
||||
{
|
||||
@ -118,78 +292,38 @@ void SkGlyphRunBuilder::prepareDrawText(
|
||||
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
|
||||
len.scale(SK_ScalarHalf);
|
||||
}
|
||||
for (size_t i = 0; i < this->runSize(); i++) {
|
||||
for (size_t i = fLastDenseIndex; i < this->runSize(); i++) {
|
||||
fPositions[i] -= len;
|
||||
}
|
||||
}
|
||||
|
||||
this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters);
|
||||
}
|
||||
|
||||
void SkGlyphRunBuilder::prepareDrawPosTextH(const SkPaint& paint, const void* bytes,
|
||||
size_t byteLength, const SkScalar* xpos,
|
||||
SkScalar constY) {
|
||||
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) {
|
||||
|
||||
this->initializeDenseAndUnique(paint, bytes, byteLength);
|
||||
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::prepareDrawPosText(const SkPaint& paint, const void* bytes,
|
||||
size_t byteLength, const SkPoint* pos) {
|
||||
this->initializeDenseAndUnique(paint, bytes, byteLength);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void SkGlyphRunBuilder::initializeDenseAndUnique(
|
||||
const SkPaint& paint, const void* bytes, size_t byteLength) {
|
||||
|
||||
fDenseIndex.clear();
|
||||
fPositions.clear();
|
||||
fUniqueGlyphs.clear();
|
||||
fTemporaryShuntGlyphIDs = nullptr;
|
||||
|
||||
size_t runSize = 0;
|
||||
const SkGlyphID* glyphIDs = nullptr;
|
||||
auto encoding = paint.getTextEncoding();
|
||||
auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint);
|
||||
if (encoding != SkPaint::kGlyphID_TextEncoding) {
|
||||
auto tfEncoding = convert_encoding(encoding);
|
||||
int utfSize = SkUTFN_CountUnichars(tfEncoding, bytes, byteLength);
|
||||
if (utfSize > 0) {
|
||||
runSize = SkTo<size_t>(utfSize);
|
||||
fScratchGlyphIDs.resize(runSize);
|
||||
typeface->charsToGlyphs(bytes, tfEncoding, fScratchGlyphIDs.data(), runSize);
|
||||
glyphIDs = fScratchGlyphIDs.data();
|
||||
}
|
||||
} else {
|
||||
runSize = byteLength / 2;
|
||||
glyphIDs = (const SkGlyphID*)bytes;
|
||||
}
|
||||
|
||||
SkASSERT(glyphIDs != nullptr);
|
||||
|
||||
if (runSize == 0) { return; }
|
||||
fTemporaryShuntGlyphIDs = glyphIDs;
|
||||
|
||||
fGlyphSet.reuse(typeface->countGlyphs(), &fUniqueGlyphs);
|
||||
for (size_t i = 0; i < runSize; i++) {
|
||||
fDenseIndex.push_back(fGlyphSet.add(glyphIDs[i]));
|
||||
}
|
||||
this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user