diff --git a/bench/TextBlobBench.cpp b/bench/TextBlobBench.cpp index f839714256..483f33cf6c 100644 --- a/bench/TextBlobBench.cpp +++ b/bench/TextBlobBench.cpp @@ -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); } } diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 5590ebbcf4..c486a0e5ec 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -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 fTypeface; diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp index a6ac71b3fa..31897436d2 100644 --- a/src/core/SkGlyphRun.cpp +++ b/src/core/SkGlyphRun.cpp @@ -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 denseIndices, SkSpan positions, SkSpan 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{}, // No dense indices for now. SkSpan{&point, 1}, SkSpan{&glyphID, 1}, @@ -185,6 +186,7 @@ void SkGlyphRunBuilder::drawTextAtOrigin( this->makeGlyphRun( paint, + SkRunFont{paint}, glyphIDs, positions, SkSpan{}, // 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(it.text(), it.textSize()); auto clusters = SkSpan(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 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 SkGlyphRunBuilder::addDenseAndUnique( } void SkGlyphRunBuilder::makeGlyphRun( - const SkPaint& runPaint, + const SkPaint& basePaint, + const SkRunFont& runFont, SkSpan glyphIDs, SkSpan positions, SkSpan 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 glyphIDs, SkPoint origin, - uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, SkPoint* positions, - SkSpan text, SkSpan clusters) { + const SkPaint& paint, const SkRunFont& runFont, SkSpan glyphIDs, + SkPoint origin, uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, + SkPoint* positions, SkSpan text, SkSpan 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{positions, runSize}, SkSpan{uniqueGlyphIDIndicesBuffer, runSize}, @@ -440,7 +442,7 @@ size_t SkGlyphRunBuilder::simplifyDrawText( } size_t SkGlyphRunBuilder::simplifyDrawPosTextH( - const SkPaint& paint, SkSpan glyphIDs, + const SkPaint& paint, const SkRunFont& runFont, SkSpan glyphIDs, const SkScalar* xpos, SkScalar constY, uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, SkPoint* positions, SkSpan text, SkSpan 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 glyphIDs, const SkPoint* pos, - uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, + const SkPaint& paint, const SkRunFont& runFont, SkSpan glyphIDs, + const SkPoint* pos, uint16_t* uniqueGlyphIDIndicesBuffer, SkGlyphID* uniqueGlyphIDsBuffer, SkSpan text, SkSpan clusters) { auto runSize = glyphIDs.size(); @@ -472,6 +474,7 @@ size_t SkGlyphRunBuilder::simplifyDrawPosText( // drawText above. this->makeGlyphRun( paint, + runFont, glyphIDs, SkSpan{pos, runSize}, SkSpan{uniqueGlyphIDIndicesBuffer, runSize}, diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h index b905cbff33..3f7c26eb0d 100644 --- a/src/core/SkGlyphRun.h +++ b/src/core/SkGlyphRun.h @@ -85,7 +85,8 @@ public: class SkGlyphRun { public: SkGlyphRun() = default; - SkGlyphRun(const SkPaint& runPaint, + SkGlyphRun(const SkPaint& basePaint, + const SkRunFont& runFont, SkSpan denseIndices, SkSpan positions, SkSpan glyphIDs, @@ -214,7 +215,8 @@ private: SkGlyphID* uniqueGlyphIDs); void makeGlyphRun( - const SkPaint& runPaint, + const SkPaint& basePaint, + const SkRunFont& runFont, SkSpan glyphIDs, SkSpan positions, SkSpan uniqueGlyphIDIndices, @@ -225,19 +227,20 @@ private: void makeGlyphRunList(const SkPaint& paint, const SkTextBlob* blob, SkPoint origin); size_t simplifyDrawText( - const SkPaint& paint, SkSpan glyphIDs, SkPoint origin, - uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, SkPoint* positions, + const SkPaint& paint, const SkRunFont& runFont, SkSpan glyphIDs, + SkPoint origin,uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, + SkPoint* positions, SkSpan text = SkSpan{}, SkSpan clusters = SkSpan{}); size_t simplifyDrawPosTextH( - const SkPaint& paint, SkSpan glyphIDs, + const SkPaint& paint, const SkRunFont& runFont, SkSpan glyphIDs, const SkScalar* xpos, SkScalar constY, uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, SkPoint* positions, SkSpan text = SkSpan{}, SkSpan clusters = SkSpan{}); size_t simplifyDrawPosText( - const SkPaint& paint, SkSpan glyphIDs, const SkPoint* pos, - uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, + const SkPaint& paint, const SkRunFont& runFont, SkSpan glyphIDs, + const SkPoint* pos, uint16_t* uniqueGlyphIDIndices, SkGlyphID* uniqueGlyphIDs, SkSpan text = SkSpan{}, SkSpan clusters = SkSpan{}); diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp index 47eb282d9e..b758c15150 100644 --- a/src/core/SkTextBlob.cpp +++ b/src/core/SkTextBlob.cpp @@ -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(fAlign)); - paint->setHinting(static_cast(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(fAlign)); + paint->setHinting(static_cast(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 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(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(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( + reinterpret_cast(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(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(const_cast(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(reinterpret_cast(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(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(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( - reinterpret_cast(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(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); } diff --git a/src/core/SkTextBlobPriv.h b/src/core/SkTextBlobPriv.h index 63405ffc4f..82aa969099 100644 --- a/src/core/SkTextBlobPriv.h +++ b/src/core/SkTextBlobPriv.h @@ -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 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(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(fFlags & kPositioning_Mask); + } + + uint16_t* glyphBuffer() const { + static_assert(SkIsAlignPtr(sizeof(RunRecord)), ""); + // Glyphs are stored immediately following the record. + return reinterpret_cast(const_cast(this) + 1); + } + + SkScalar* posBuffer() const { + // Position scalars follow the (aligned) glyph buffer. + return reinterpret_cast(reinterpret_cast(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(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; diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index a8f7ee06a1..fec03dcbdd 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -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{}, // No dense indices for now. SkSpan{pos, len}, SkSpan{glyphs, len},