Split out SkRunFont and SkPaint

Coping paints take significant time. The ApplyFontToPaint
idiom requires a copy and a dtor. This CL keeps the paint and
font in parallel through the code until a paint is actually
needed, then a special ctor is used to create it.

Also, inline a bunch of text blob calls that were showing up
in perf.

Change-Id: I7da746a287e4d3942e45e9536ef9acdc64f084d4
Reviewed-on: https://skia-review.googlesource.com/c/159222
Reviewed-by: Mike Klein <mtklein@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herb Derby 2018-10-02 16:48:17 -04:00
parent 2c06e14697
commit 434377abfb
7 changed files with 384 additions and 324 deletions

View File

@ -63,7 +63,8 @@ class TextBlobCachedBench : public SkTextBlobBench {
SkPaint paint;
auto blob = this->makeBlob();
for (int i = 0; i < loops; i++) {
auto bigLoops = loops * 1000;
for (int i = 0; i < bigLoops; i++) {
// To ensure maximum caching, we just redraw the blob at the same place everytime
canvas->drawTextBlob(blob, 0, 0, paint);
}
@ -78,7 +79,8 @@ class TextBlobFirstTimeBench : public SkTextBlobBench {
void onDraw(int loops, SkCanvas* canvas) override {
SkPaint paint;
for (int i = 0; i < loops; i++) {
auto bigLoops = loops * 1000;
for (int i = 0; i < bigLoops; i++) {
canvas->drawTextBlob(this->makeBlob(), 0, 0, paint);
}
}

View File

@ -33,6 +33,8 @@ class SkData;
class SkDescriptor;
class SkDrawLooper;
class SkGlyph;
class SkGlyphRunBuilder;
class SkGlyphRun;
class SkGlyphRunListPainter;
struct SkRect;
class SkGlyphCache;
@ -41,9 +43,11 @@ class SkMaskFilter;
class SkPath;
class SkPathEffect;
struct SkPoint;
class SkRunFont;
class SkShader;
class SkSurfaceProps;
class SkTextBlob;
class SkTextBlobRunIterator;
class SkTypeface;
/** \class SkPaint
@ -1474,6 +1478,9 @@ public:
Style style) const;
private:
friend class SkGlyphRun;
friend class SkGlyphRunBuilder;
SkPaint(const SkPaint&, const SkRunFont&);
typedef const SkGlyph& (*GlyphCacheProc)(SkGlyphCache*, const char**, const char*);
sk_sp<SkTypeface> fTypeface;

View File

@ -28,7 +28,8 @@ static SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) {
} // namespace
// -- SkGlyphRun -----------------------------------------------------------------------------------
SkGlyphRun::SkGlyphRun(const SkPaint& runPaint,
SkGlyphRun::SkGlyphRun(const SkPaint& basePaint,
const SkRunFont& runFont,
SkSpan<const uint16_t> denseIndices,
SkSpan<const SkPoint> positions,
SkSpan<const SkGlyphID> glyphIDs,
@ -41,14 +42,14 @@ SkGlyphRun::SkGlyphRun(const SkPaint& runPaint,
, fUniqueGlyphIDs{uniqueGlyphIDs}
, fText{text}
, fClusters{clusters}
, fRunPaint{runPaint} {}
, fRunPaint{basePaint, runFont} {}
void SkGlyphRun::eachGlyphToGlyphRun(SkGlyphRun::PerGlyph perGlyph) {
SkPaint paint{fRunPaint};
SkPoint point;
SkGlyphID glyphID;
SkGlyphRun run{
std::move(paint),
fRunPaint,
SkRunFont{fRunPaint},
SkSpan<const uint16_t>{}, // No dense indices for now.
SkSpan<const SkPoint>{&point, 1},
SkSpan<const SkGlyphID>{&glyphID, 1},
@ -185,6 +186,7 @@ void SkGlyphRunBuilder::drawTextAtOrigin(
this->makeGlyphRun(
paint,
SkRunFont{paint},
glyphIDs,
positions,
SkSpan<const uint16_t>{}, // no dense indices for now.,
@ -199,8 +201,8 @@ void SkGlyphRunBuilder::drawText(
auto glyphIDs = textToGlyphIDs(paint, bytes, byteLength);
if (!glyphIDs.empty()) {
this->initialize(glyphIDs.size());
this->simplifyDrawText(
paint, glyphIDs, origin, fUniqueGlyphIDIndices, fUniqueGlyphIDs, fPositions);
this->simplifyDrawText(paint, SkRunFont{paint}, glyphIDs, origin,
fUniqueGlyphIDIndices, fUniqueGlyphIDs, fPositions);
}
this->makeGlyphRunList(paint, nullptr, SkPoint::Make(0, 0));
@ -213,7 +215,8 @@ void SkGlyphRunBuilder::drawPosTextH(const SkPaint& paint, const void* bytes,
if (!glyphIDs.empty()) {
this->initialize(glyphIDs.size());
this->simplifyDrawPosTextH(
paint, glyphIDs, xpos, constY, fUniqueGlyphIDIndices, fUniqueGlyphIDs, fPositions);
paint, SkRunFont{paint}, glyphIDs, xpos, constY,
fUniqueGlyphIDIndices, fUniqueGlyphIDs, fPositions);
}
this->makeGlyphRunList(paint, nullptr, SkPoint::Make(0, 0));
@ -224,7 +227,7 @@ void SkGlyphRunBuilder::drawPosText(const SkPaint& paint, const void* bytes,
auto glyphIDs = textToGlyphIDs(paint, bytes, byteLength);
if (!glyphIDs.empty()) {
this->initialize(glyphIDs.size());
this->simplifyDrawPosText(paint, glyphIDs, pos,
this->simplifyDrawPosText(paint, SkRunFont{paint}, glyphIDs, pos,
fUniqueGlyphIDIndices, fUniqueGlyphIDs);
}
@ -232,8 +235,6 @@ void SkGlyphRunBuilder::drawPosText(const SkPaint& paint, const void* bytes,
}
void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blob, SkPoint origin) {
SkPaint runPaint = paint;
// Figure out all the storage needed to pre-size everything below.
size_t totalGlyphs = 0;
for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
@ -250,12 +251,8 @@ void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blo
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);
size_t runSize = it.glyphCount();
// These better be glyphs
SkASSERT(runPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
auto text = SkSpan<const char>(it.text(), it.textSize());
auto clusters = SkSpan<const uint32_t>(it.clusters(), runSize);
const SkPoint& offset = it.offset();
@ -265,7 +262,7 @@ void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blo
switch (it.positioning()) {
case SkTextBlobRunIterator::kDefault_Positioning: {
uniqueGlyphIDsSize = this->simplifyDrawText(
runPaint, glyphIDs, offset,
paint, it.runFont(), glyphIDs, offset,
currentDenseIndices, currentUniqueGlyphIDs, currentPositions,
text, clusters);
}
@ -273,14 +270,14 @@ void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blo
case SkTextBlobRunIterator::kHorizontal_Positioning: {
auto constY = offset.y();
uniqueGlyphIDsSize = this->simplifyDrawPosTextH(
runPaint, glyphIDs, it.pos(), constY,
paint, it.runFont(), glyphIDs, it.pos(), constY,
currentDenseIndices, currentUniqueGlyphIDs, currentPositions,
text, clusters);
}
break;
case SkTextBlobRunIterator::kFull_Positioning:
uniqueGlyphIDsSize = this->simplifyDrawPosText(
runPaint, glyphIDs, (const SkPoint*)it.pos(),
paint, it.runFont(), glyphIDs, (const SkPoint*)it.pos(),
currentDenseIndices, currentUniqueGlyphIDs,
text, clusters);
break;
@ -298,8 +295,8 @@ void SkGlyphRunBuilder::drawGlyphPos(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos) {
if (!glyphIDs.empty()) {
this->initialize(glyphIDs.size());
this->simplifyDrawPosText(paint, glyphIDs, pos,
fUniqueGlyphIDIndices, fUniqueGlyphIDs);
this->simplifyDrawPosText(paint, SkRunFont{paint}, glyphIDs, pos,
fUniqueGlyphIDIndices, fUniqueGlyphIDs);
this->makeGlyphRunList(paint, nullptr, SkPoint::Make(0, 0));
}
}
@ -361,7 +358,8 @@ SkSpan<const SkGlyphID> SkGlyphRunBuilder::addDenseAndUnique(
}
void SkGlyphRunBuilder::makeGlyphRun(
const SkPaint& runPaint,
const SkPaint& basePaint,
const SkRunFont& runFont,
SkSpan<const SkGlyphID> glyphIDs,
SkSpan<const SkPoint> positions,
SkSpan<const uint16_t> uniqueGlyphIDIndices,
@ -372,7 +370,8 @@ void SkGlyphRunBuilder::makeGlyphRun(
// Ignore empty runs.
if (!glyphIDs.empty()) {
fGlyphRunListStorage.emplace_back(
runPaint,
basePaint,
runFont,
uniqueGlyphIDIndices,
positions,
glyphIDs,
@ -391,20 +390,22 @@ void SkGlyphRunBuilder::makeGlyphRunList(
}
size_t SkGlyphRunBuilder::simplifyDrawText(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin,
uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, SkPoint* positions,
SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
const SkPaint& paint, const SkRunFont& runFont, SkSpan<const SkGlyphID> glyphIDs,
SkPoint origin, uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer,
SkPoint* positions, SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
SkASSERT(!glyphIDs.empty());
auto runSize = glyphIDs.size();
SkPaint runPaint{paint, runFont};
auto unqiueGlyphIDs = this->addDenseAndUnique(
paint, glyphIDs, uniqueGlyphIDIndicesBuffer, uniqueGlyphIDsBuffer);
runPaint, glyphIDs, uniqueGlyphIDIndicesBuffer, uniqueGlyphIDsBuffer);
if (!unqiueGlyphIDs.empty()) {
fScratchAdvances.resize(runSize);
{
auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint);
auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(runPaint);
cache->getAdvances(unqiueGlyphIDs, fScratchAdvances.data());
}
@ -428,6 +429,7 @@ size_t SkGlyphRunBuilder::simplifyDrawText(
this->makeGlyphRun(
paint,
runFont,
glyphIDs,
SkSpan<const SkPoint>{positions, runSize},
SkSpan<const uint16_t>{uniqueGlyphIDIndicesBuffer, runSize},
@ -440,7 +442,7 @@ size_t SkGlyphRunBuilder::simplifyDrawText(
}
size_t SkGlyphRunBuilder::simplifyDrawPosTextH(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs,
const SkPaint& paint, const SkRunFont& runFont, SkSpan<const SkGlyphID> glyphIDs,
const SkScalar* xpos, SkScalar constY,
uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, SkPoint* positions,
SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
@ -450,14 +452,14 @@ size_t SkGlyphRunBuilder::simplifyDrawPosTextH(
*posCursor++ = SkPoint::Make(x, constY);
}
return simplifyDrawPosText(paint, glyphIDs, positions,
return simplifyDrawPosText(paint, runFont, glyphIDs, positions,
uniqueGlyphIDIndicesBuffer, uniqueGlyphIDsBuffer,
text, clusters);
}
size_t SkGlyphRunBuilder::simplifyDrawPosText(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos,
uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer,
const SkPaint& paint, const SkRunFont& runFont, SkSpan<const SkGlyphID> glyphIDs,
const SkPoint* pos, uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer,
SkSpan<const char> text, SkSpan<const uint32_t> clusters) {
auto runSize = glyphIDs.size();
@ -472,6 +474,7 @@ size_t SkGlyphRunBuilder::simplifyDrawPosText(
// drawText above.
this->makeGlyphRun(
paint,
runFont,
glyphIDs,
SkSpan<const SkPoint>{pos, runSize},
SkSpan<const SkGlyphID>{uniqueGlyphIDIndicesBuffer, runSize},

View File

@ -85,7 +85,8 @@ public:
class SkGlyphRun {
public:
SkGlyphRun() = default;
SkGlyphRun(const SkPaint& runPaint,
SkGlyphRun(const SkPaint& basePaint,
const SkRunFont& runFont,
SkSpan<const uint16_t> denseIndices,
SkSpan<const SkPoint> positions,
SkSpan<const SkGlyphID> glyphIDs,
@ -214,7 +215,8 @@ private:
SkGlyphID* uniqueGlyphIDs);
void makeGlyphRun(
const SkPaint& runPaint,
const SkPaint& basePaint,
const SkRunFont& runFont,
SkSpan<const SkGlyphID> glyphIDs,
SkSpan<const SkPoint> positions,
SkSpan<const uint16_t> uniqueGlyphIDIndices,
@ -225,19 +227,20 @@ private:
void makeGlyphRunList(const SkPaint& paint, const SkTextBlob* blob, SkPoint origin);
size_t simplifyDrawText(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin,
uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, SkPoint* positions,
const SkPaint& paint, const SkRunFont& runFont, SkSpan<const SkGlyphID> glyphIDs,
SkPoint origin,uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs,
SkPoint* positions,
SkSpan<const char> text = SkSpan<const char>{},
SkSpan<const uint32_t> clusters = SkSpan<const uint32_t>{});
size_t simplifyDrawPosTextH(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs,
const SkPaint& paint, const SkRunFont& runFont, SkSpan<const SkGlyphID> glyphIDs,
const SkScalar* xpos, SkScalar constY,
uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, SkPoint* positions,
SkSpan<const char> text = SkSpan<const char>{},
SkSpan<const uint32_t> clusters = SkSpan<const uint32_t>{});
size_t simplifyDrawPosText(
const SkPaint& paint, SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos,
uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs,
const SkPaint& paint, const SkRunFont& runFont, SkSpan<const SkGlyphID> glyphIDs,
const SkPoint* pos, uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs,
SkSpan<const char> text = SkSpan<const char>{},
SkSpan<const uint32_t> clusters = SkSpan<const uint32_t>{});

View File

@ -22,12 +22,7 @@
#include "text/GrTextBlobCache.h"
#endif
namespace {
// TODO(fmalita): replace with SkFont.
class RunFont : SkNoncopyable {
public:
RunFont(const SkPaint& paint)
SkRunFont::SkRunFont(const SkPaint& paint)
: fSize(paint.getTextSize())
, fScaleX(paint.getTextScaleX())
, fTypeface(SkPaintPriv::RefTypefaceOrDefault(paint))
@ -36,98 +31,73 @@ public:
, fHinting(paint.getHinting())
, fFlags(paint.getFlags() & kFlagsMask) { }
void applyToPaint(SkPaint* paint) const {
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint->setTypeface(fTypeface);
paint->setTextSize(fSize);
paint->setTextScaleX(fScaleX);
paint->setTextSkewX(fSkewX);
paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
void SkRunFont::applyToPaint(SkPaint* paint) const {
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint->setTypeface(fTypeface);
paint->setTextSize(fSize);
paint->setTextScaleX(fScaleX);
paint->setTextSkewX(fSkewX);
paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
}
paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
}
bool operator==(const RunFont& other) const {
return fTypeface == other.fTypeface
&& fSize == other.fSize
&& fScaleX == other.fScaleX
&& fSkewX == other.fSkewX
&& fAlign == other.fAlign
&& fHinting == other.fHinting
&& fFlags == other.fFlags;
}
bool operator!=(const RunFont& other) const {
return !(*this == other);
}
uint32_t flags() const { return fFlags; }
private:
const static uint32_t kFlagsMask =
SkPaint::kAntiAlias_Flag |
SkPaint::kFakeBoldText_Flag |
SkPaint::kLinearText_Flag |
SkPaint::kSubpixelText_Flag |
SkPaint::kLCDRenderText_Flag |
SkPaint::kEmbeddedBitmapText_Flag |
SkPaint::kAutoHinting_Flag |
SkPaint::kVerticalText_Flag ;
SkScalar fSize;
SkScalar fScaleX;
// Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
// empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
sk_sp<SkTypeface> fTypeface;
SkScalar fSkewX;
static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
uint32_t fAlign : 2;
static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
uint32_t fHinting : 2;
static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
uint32_t fFlags : 16;
typedef SkNoncopyable INHERITED;
};
bool SkRunFont::operator==(const SkRunFont& other) const {
return fTypeface == other.fTypeface
&& fSize == other.fSize
&& fScaleX == other.fScaleX
&& fSkewX == other.fSkewX
&& fAlign == other.fAlign
&& fHinting == other.fHinting
&& fFlags == other.fFlags;
}
namespace {
struct RunFontStorageEquivalent {
SkScalar fSize, fScaleX;
void* fTypeface;
SkScalar fSkewX;
uint32_t fFlags;
};
static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
static_assert(sizeof(SkRunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
}
} // anonymous namespace
size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
SkTextBlob::GlyphPositioning positioning,
SkSafeMath* safe) {
static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
//
// Textblob data is laid out into externally-managed storage as follows:
//
// -----------------------------------------------------------------------------
// | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
// -----------------------------------------------------------------------------
//
// 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.
auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
// RunRecord object + (aligned) glyph buffer + position buffer
auto size = sizeof(SkTextBlob::RunRecord);
size = safe->add(size, safe->alignUp(glyphSize, 4));
size = safe->add(size, posSize);
if (textSize) { // Extended run.
size = safe->add(size, sizeof(uint32_t));
size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
size = safe->add(size, textSize);
}
return safe->alignUp(size, sizeof(void*));
}
const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) {
// The first record (if present) is stored following the blob object.
// (aligned up to make the RunRecord aligned too)
return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
}
const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
}
namespace {
struct RunRecordStorageEquivalent {
RunFont fFont;
SkRunFont fFont;
SkPoint fOffset;
uint32_t fCount;
uint32_t fFlags;
@ -135,169 +105,59 @@ struct RunRecordStorageEquivalent {
};
}
class SkTextBlob::RunRecord {
public:
RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
: fFont(font)
, fCount(count)
, fOffset(offset)
, fFlags(pos) {
SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
SkASSERT(kRunRecordMagic == fMagic);
SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
SkDEBUGCODE(fMagic = kRunRecordMagic);
if (textSize > 0) {
fFlags |= kExtended_Flag;
*this->textSizePtr() = textSize;
}
SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
<= (SkScalar*)NextUnchecked(this));
if (isExtended()) {
SkASSERT(textSize() > 0);
SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
}
static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
"runrecord_should_stay_packed");
}
uint32_t glyphCount() const {
return fCount;
}
const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) {
SkSafeMath safe;
auto res = reinterpret_cast<const RunRecord*>(
reinterpret_cast<const uint8_t*>(run)
+ StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
SkASSERT(safe);
return res;
}
const SkPoint& offset() const {
return fOffset;
}
size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
SkTextBlob::GlyphPositioning positioning,
SkSafeMath* safe) {
return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
}
const RunFont& font() const {
return fFont;
}
uint32_t* SkTextBlob::RunRecord::textSizePtr() const {
// textSize follows the position buffer.
SkASSERT(isExtended());
SkSafeMath safe;
auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
SkASSERT(safe);
return res;
}
GlyphPositioning positioning() const {
return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
}
void SkTextBlob::RunRecord::grow(uint32_t count) {
SkScalar* initialPosBuffer = posBuffer();
uint32_t initialCount = fCount;
fCount += count;
uint16_t* glyphBuffer() const {
static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
// Glyphs are stored immediately following the record.
return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
}
// Move the initial pos scalars to their new location.
size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
SkScalar* posBuffer() const {
// Position scalars follow the (aligned) glyph buffer.
return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
SkAlign4(fCount * sizeof(uint16_t)));
}
uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
uint32_t* clusterBuffer() const {
// clusters follow the textSize.
return isExtended() ? 1 + this->textSizePtr() : nullptr;
}
char* textBuffer() const {
return isExtended()
? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
: nullptr;
}
static size_t StorageSize(uint32_t glyphCount, uint32_t textSize,
SkTextBlob::GlyphPositioning positioning,
SkSafeMath* safe) {
static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
// RunRecord object + (aligned) glyph buffer + position buffer
auto size = sizeof(SkTextBlob::RunRecord);
size = safe->add(size, safe->alignUp(glyphSize, 4));
size = safe->add(size, posSize);
if (textSize) { // Extended run.
size = safe->add(size, sizeof(uint32_t));
size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
size = safe->add(size, textSize);
}
return safe->alignUp(size, sizeof(void*));
}
static const RunRecord* First(const SkTextBlob* blob) {
// The first record (if present) is stored following the blob object.
// (aligned up to make the RunRecord aligned too)
return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
}
static const RunRecord* Next(const RunRecord* run) {
return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
}
void validate(const uint8_t* storageTop) const {
SkASSERT(kRunRecordMagic == fMagic);
SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
<= (SkScalar*)NextUnchecked(this));
if (isExtended()) {
SkASSERT(textSize() > 0);
SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
}
static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
"runrecord_should_stay_packed");
}
private:
friend class SkTextBlobBuilder;
enum Flags {
kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning
kLast_Flag = 0x04, // set for the last blob run
kExtended_Flag = 0x08, // set for runs with text/cluster info
};
static const RunRecord* NextUnchecked(const RunRecord* run) {
SkSafeMath safe;
auto res = reinterpret_cast<const RunRecord*>(
reinterpret_cast<const uint8_t*>(run)
+ StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
SkASSERT(safe);
return res;
}
static size_t PosCount(uint32_t glyphCount,
SkTextBlob::GlyphPositioning positioning,
SkSafeMath* safe) {
return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
}
uint32_t* textSizePtr() const {
// textSize follows the position buffer.
SkASSERT(isExtended());
SkSafeMath safe;
auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
SkASSERT(safe);
return res;
}
void grow(uint32_t count) {
SkScalar* initialPosBuffer = posBuffer();
uint32_t initialCount = fCount;
fCount += count;
// Move the initial pos scalars to their new location.
size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
// memmove, as the buffers may overlap
memmove(posBuffer(), initialPosBuffer, copySize);
}
bool isExtended() const {
return fFlags & kExtended_Flag;
}
RunFont fFont;
uint32_t fCount;
SkPoint fOffset;
uint32_t fFlags;
SkDEBUGCODE(unsigned fMagic;)
};
// memmove, as the buffers may overlap
memmove(posBuffer(), initialPosBuffer, copySize);
}
static int32_t gNextID = 1;
static int32_t next_id() {
@ -374,10 +234,6 @@ SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
}
bool SkTextBlobRunIterator::done() const {
return !fCurrentRun;
}
void SkTextBlobRunIterator::next() {
SkASSERT(!this->done());
@ -387,26 +243,6 @@ void SkTextBlobRunIterator::next() {
}
}
uint32_t SkTextBlobRunIterator::glyphCount() const {
SkASSERT(!this->done());
return fCurrentRun->glyphCount();
}
const uint16_t* SkTextBlobRunIterator::glyphs() const {
SkASSERT(!this->done());
return fCurrentRun->glyphBuffer();
}
const SkScalar* SkTextBlobRunIterator::pos() const {
SkASSERT(!this->done());
return fCurrentRun->posBuffer();
}
const SkPoint& SkTextBlobRunIterator::offset() const {
SkASSERT(!this->done());
return fCurrentRun->offset();
}
SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const {
SkASSERT(!this->done());
static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) ==
@ -425,20 +261,6 @@ 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);
}

View File

@ -8,7 +8,16 @@
#ifndef SkTextBlobPriv_DEFINED
#define SkTextBlobPriv_DEFINED
#include "SkColorFilter.h"
#include "SkDrawLooper.h"
#include "SkImageFilter.h"
#include "SkMaskFilter.h"
#include "SkPaintPriv.h"
#include "SkPathEffect.h"
#include "SkSafeMath.h"
#include "SkShader.h"
#include "SkTextBlob.h"
#include "SkTypeface.h"
class SkReadBuffer;
class SkWriteBuffer;
@ -49,6 +58,193 @@ public:
}
};
// TODO(fmalita): replace with SkFont.
class SkRunFont : SkNoncopyable {
public:
SkRunFont(const SkPaint& paint);
void applyToPaint(SkPaint* paint) const;
bool operator==(const SkRunFont& other) const;
bool operator!=(const SkRunFont& other) const {
return !(*this == other);
}
uint32_t flags() const { return fFlags; }
private:
friend SkPaint;
const static uint32_t kFlagsMask =
SkPaint::kAntiAlias_Flag |
SkPaint::kFakeBoldText_Flag |
SkPaint::kLinearText_Flag |
SkPaint::kSubpixelText_Flag |
SkPaint::kLCDRenderText_Flag |
SkPaint::kEmbeddedBitmapText_Flag |
SkPaint::kAutoHinting_Flag |
SkPaint::kVerticalText_Flag ;
SkScalar fSize;
SkScalar fScaleX;
// Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
// empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
sk_sp<SkTypeface> fTypeface;
SkScalar fSkewX;
static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
uint32_t fAlign : 2;
static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
uint32_t fHinting : 2;
static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
uint32_t fFlags : 16;
typedef SkNoncopyable INHERITED;
};
//
// Textblob data is laid out into externally-managed storage as follows:
//
// -----------------------------------------------------------------------------
// | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
// -----------------------------------------------------------------------------
//
// 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;)
class SkTextBlob::RunRecord {
public:
RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
: fFont(font)
, fCount(count)
, fOffset(offset)
, fFlags(pos) {
SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
SkDEBUGCODE(fMagic = kRunRecordMagic);
if (textSize > 0) {
fFlags |= kExtended_Flag;
*this->textSizePtr() = textSize;
}
}
uint32_t glyphCount() const {
return fCount;
}
const SkPoint& offset() const {
return fOffset;
}
const SkRunFont& font() const {
return fFont;
}
GlyphPositioning positioning() const {
return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
}
uint16_t* glyphBuffer() const {
static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
// Glyphs are stored immediately following the record.
return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
}
SkScalar* posBuffer() const {
// Position scalars follow the (aligned) glyph buffer.
return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
SkAlign4(fCount * sizeof(uint16_t)));
}
uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
uint32_t* clusterBuffer() const {
// clusters follow the textSize.
return isExtended() ? 1 + this->textSizePtr() : nullptr;
}
char* textBuffer() const {
return isExtended()
? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
: nullptr;
}
static size_t StorageSize(uint32_t glyphCount, uint32_t textSize,
SkTextBlob::GlyphPositioning positioning,
SkSafeMath* safe);
static const RunRecord* First(const SkTextBlob* blob);
static const RunRecord* Next(const RunRecord* run);
void validate(const uint8_t* storageTop) const;
private:
friend class SkTextBlobBuilder;
enum Flags {
kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning
kLast_Flag = 0x04, // set for the last blob run
kExtended_Flag = 0x08, // set for runs with text/cluster info
};
static const RunRecord* NextUnchecked(const RunRecord* run);
static size_t PosCount(uint32_t glyphCount,
SkTextBlob::GlyphPositioning positioning,
SkSafeMath* safe);
uint32_t* textSizePtr() const;
void grow(uint32_t count);
bool isExtended() const {
return fFlags & kExtended_Flag;
}
SkRunFont fFont;
uint32_t fCount;
SkPoint fOffset;
uint32_t fFlags;
SkDEBUGCODE(unsigned fMagic;)
};
// (paint->getFlags() & ~kFlagsMask) | fFlags
inline SkPaint::SkPaint(const SkPaint& basePaint, const SkRunFont& runFont)
: fTypeface{runFont.fTypeface}
, fPathEffect{basePaint.fPathEffect}
, fShader{basePaint.fShader}
, fMaskFilter{basePaint.fMaskFilter}
, fColorFilter{basePaint.fColorFilter}
, fDrawLooper{basePaint.fDrawLooper}
, fImageFilter{basePaint.fImageFilter}
, fTextSize{runFont.fSize}
, fTextScaleX{runFont.fScaleX}
, fTextSkewX{runFont.fSkewX}
, fColor4f{basePaint.fColor4f}
, fWidth{basePaint.fWidth}
, fMiterLimit{basePaint.fMiterLimit}
, fBlendMode{basePaint.fBlendMode}
, fBitfieldsUInt{(basePaint.fBitfieldsUInt & ~SkRunFont::kFlagsMask) | runFont.fFlags} {
fBitfields.fTextEncoding = kGlyphID_TextEncoding;
fBitfields.fHinting = runFont.fHinting;
fBitfields.fTextAlign = runFont.fAlign;
}
/**
* Iterate through all of the text runs of the text blob. For example:
* for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
@ -65,18 +261,45 @@ public:
kFull_Positioning = 2 // Point positioning -- two scalars per glyph.
};
bool done() const;
bool done() const {
return !fCurrentRun;
}
void next();
uint32_t glyphCount() const;
const uint16_t* glyphs() const;
const SkScalar* pos() const;
const SkPoint& offset() const;
uint32_t glyphCount() const {
SkASSERT(!this->done());
return fCurrentRun->glyphCount();
}
const uint16_t* glyphs() const {
SkASSERT(!this->done());
return fCurrentRun->glyphBuffer();
}
const SkScalar* pos() const {
SkASSERT(!this->done());
return fCurrentRun->posBuffer();
}
const SkPoint& offset() const {
SkASSERT(!this->done());
return fCurrentRun->offset();
}
const SkRunFont& runFont() const {
SkASSERT(!this->done());
return fCurrentRun->font();
}
void applyFontToPaint(SkPaint*) const;
GlyphPositioning positioning() const;
uint32_t* clusters() const;
uint32_t textSize() const;
char* text() const;
uint32_t* clusters() const {
SkASSERT(!this->done());
return fCurrentRun->clusterBuffer();
}
uint32_t textSize() const {
SkASSERT(!this->done());
return fCurrentRun->textSize();
}
char* text() const {
SkASSERT(!this->done());
return fCurrentRun->textBuffer();
}
bool isLCD() const;

View File

@ -492,7 +492,7 @@ DEF_TEST(SkPDF_Primitives_Color, reporter) {
static SkGlyphRun make_run(size_t len, const SkGlyphID* glyphs, SkPoint* pos,
SkPaint paint, const uint32_t* clusters,
size_t utf8TextByteLength, const char* utf8Text) {
return SkGlyphRun(std::move(paint),
return SkGlyphRun(paint, SkRunFont{paint},
SkSpan<const uint16_t>{}, // No dense indices for now.
SkSpan<const SkPoint>{pos, len},
SkSpan<const SkGlyphID>{glyphs, len},