diff options
-rw-r--r-- | include/core/SkTextBlob.h | 2 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 13 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 56 | ||||
-rw-r--r-- | src/core/SkDevice.h | 7 | ||||
-rw-r--r-- | src/core/SkGlyphRun.cpp | 136 | ||||
-rw-r--r-- | src/core/SkGlyphRun.h | 100 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.cpp | 23 | ||||
-rw-r--r-- | src/core/SkTypeface_remote.h | 3 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.cpp | 13 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.h | 8 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 9 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.h | 3 | ||||
-rw-r--r-- | src/gpu/text/GrTextBlobCache.h | 14 | ||||
-rw-r--r-- | src/gpu/text/GrTextContext.cpp | 156 | ||||
-rw-r--r-- | src/gpu/text/GrTextContext.h | 14 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 14 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.h | 4 | ||||
-rw-r--r-- | tests/GlyphRunTest.cpp | 4 | ||||
-rw-r--r-- | tests/SkRemoteGlyphCacheTest.cpp | 46 |
19 files changed, 338 insertions, 287 deletions
diff --git a/include/core/SkTextBlob.h b/include/core/SkTextBlob.h index 59151d3002..9f4f10f2e4 100644 --- a/include/core/SkTextBlob.h +++ b/include/core/SkTextBlob.h @@ -14,6 +14,7 @@ #include "SkString.h" #include "SkRefCnt.h" +class SkGlyphRunList; class SkReadBuffer; class SkWriteBuffer; @@ -110,6 +111,7 @@ private: fCacheID.store(cacheID); } + friend class SkGlyphRunList; friend class GrTextBlobCache; friend class SkTextBlobBuilder; friend class SkTextBlobRunIterator; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index b297f98156..c590d77cb7 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -2453,8 +2453,7 @@ void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkSca while (iter.next()) { fScratchGlyphRunBuilder->prepareDrawText( looper.paint(), text, byteLength, SkPoint::Make(x, y)); - auto glyphRun = fScratchGlyphRunBuilder->useGlyphRun(); - iter.fDevice->drawGlyphRun(looper.paint(), glyphRun); + fScratchGlyphRunBuilder->draw(iter.fDevice); } LOOPER_END @@ -2467,8 +2466,7 @@ void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint while (iter.next()) { fScratchGlyphRunBuilder->prepareDrawPosText(looper.paint(), text, byteLength, pos); - auto glyphRun = fScratchGlyphRunBuilder->useGlyphRun(); - iter.fDevice->drawGlyphRun(looper.paint(), glyphRun); + fScratchGlyphRunBuilder->draw(iter.fDevice); } LOOPER_END @@ -2482,8 +2480,7 @@ void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScala while (iter.next()) { fScratchGlyphRunBuilder->prepareDrawPosTextH( looper.paint(), text, byteLength, xpos, constY); - const auto& glyphRun = fScratchGlyphRunBuilder->useGlyphRun(); - iter.fDevice->drawGlyphRun(looper.paint(), glyphRun); + fScratchGlyphRunBuilder->draw(iter.fDevice); } LOOPER_END @@ -2536,7 +2533,9 @@ void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds) while (iter.next()) { - iter.fDevice->drawTextBlob(blob, x, y, looper.paint(), drawFilter); + fScratchGlyphRunBuilder->prepareTextBlob( + looper.paint(), *blob, SkPoint::Make(x, y), drawFilter); + fScratchGlyphRunBuilder->draw(iter.fDevice); } LOOPER_END diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 46404b2887..9fd61c9d28 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -141,53 +141,6 @@ void SkBaseDevice::drawPatch(const SkPoint cubics[12], const SkColor colors[4], } } -void SkBaseDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint &paint, SkDrawFilter* drawFilter) { - - SkPaint runPaint = paint; - - SkTextBlobRunIterator it(blob); - for (;!it.done(); it.next()) { - size_t textLen = it.glyphCount() * sizeof(uint16_t); - const SkPoint& offset = it.offset(); - // applyFontToPaint() always overwrites the exact same attributes, - // so it is safe to not re-seed the paint for this reason. - it.applyFontToPaint(&runPaint); - - if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { - // A false return from filter() means we should abort the current draw. - runPaint = paint; - continue; - } - - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: { - auto origin = SkPoint::Make(x + offset.x(), y + offset.y()); - SkGlyphRunBuilder builder; - builder.prepareDrawText(runPaint, (const char*) it.glyphs(), textLen, origin); - auto glyphRun = builder.useGlyphRun(); - glyphRun->temporaryShuntToDrawPosText(runPaint, this); - } - break; - case SkTextBlob::kHorizontal_Positioning: - this->drawPosText(it.glyphs(), textLen, it.pos(), 1, - SkPoint::Make(x, y + offset.y()), runPaint); - break; - case SkTextBlob::kFull_Positioning: - this->drawPosText(it.glyphs(), textLen, it.pos(), 2, - SkPoint::Make(x, y), runPaint); - break; - default: - SK_ABORT("unhandled positioning mode"); - } - - if (drawFilter) { - // A draw filter may change the paint arbitrarily, so we must re-seed in this case. - runPaint = paint; - } - } -} - void SkBaseDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint& paint) { SkBitmap bm; @@ -252,11 +205,10 @@ void SkBaseDevice::drawImageLattice(const SkImage* image, } } -void SkBaseDevice::drawGlyphRun(const SkPaint& paint, SkGlyphRun* glyphRun) { - SkPaint glyphPaint(paint); - glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - - glyphRun->temporaryShuntToDrawPosText(glyphPaint, this); +void SkBaseDevice::drawGlyphRunList(SkGlyphRunList* glyphRunList) { + for (auto& glyphRun : *glyphRunList) { + glyphRun.temporaryShuntToDrawPosText(this, glyphRunList->origin()); + } } void SkBaseDevice::drawBitmapLattice(const SkBitmap& bitmap, diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h index f840cf6218..9a4086e51d 100644 --- a/src/core/SkDevice.h +++ b/src/core/SkDevice.h @@ -18,6 +18,7 @@ class SkBitmap; class SkDrawFilter; struct SkDrawShadowRec; class SkGlyphRun; +class SkGlyphRunList; class SkGlyphRunBuilder; class SkImageFilterCache; struct SkIRect; @@ -224,13 +225,10 @@ protected: * Does not handle text decoration. * Decorations (underline and stike-thru) will be handled by SkCanvas. */ - virtual void drawGlyphRun(const SkPaint& paint, SkGlyphRun* glyphRun); + virtual void drawGlyphRunList(SkGlyphRunList* glyphRunList); virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0; virtual void drawShadow(const SkPath&, const SkDrawShadowRec&); - // default implementation unrolls the blob runs. - virtual void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, - const SkPaint& paint, SkDrawFilter* drawFilter); // default implementation calls drawVertices virtual void drawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], SkBlendMode, bool interpColorsLinearly, @@ -349,6 +347,7 @@ private: // Temporarily friend the SkGlyphRunBuilder until drawPosText is gone. friend class SkGlyphRun; + friend class SkGlyphRunBuilder; virtual void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) = 0; diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp index ce1c336d88..583d676940 100644 --- a/src/core/SkGlyphRun.cpp +++ b/src/core/SkGlyphRun.cpp @@ -12,7 +12,7 @@ #include <tuple> #include "SkDevice.h" -#include "SkDraw.h" +#include "SkDrawFilter.h" #include "SkGlyphCache.h" #include "SkMSAN.h" #include "SkMakeUnique.h" @@ -90,22 +90,23 @@ SkGlyphRun::SkGlyphRun(const SkIndexedRunInfo& runInfo, size_t uniqueOffset, uint16_t uniqueSize, SkSpan<SkGlyphID> scratchGlyphs, SkSpan<const char> text, - SkSpan<uint32_t> clusters) + SkSpan<const uint32_t> clusters, + SkPaint&& runPaint) : fRunInfo{runInfo} , fDenseOffset{denseOffset}, fDenseSize{denseSize} , fUniqueOffset{uniqueOffset}, fUniqueSize{uniqueSize} , fTemporaryShuntGlyphIDs{scratchGlyphs} , fText{text} - , fClusters{clusters} { } + , fClusters{clusters} + , fRunPaint{std::move(runPaint)} {} - -void SkGlyphRun::temporaryShuntToDrawPosText(const SkPaint& paint, SkBaseDevice* device) { +void SkGlyphRun::temporaryShuntToDrawPosText(SkBaseDevice* device, SkPoint origin) { auto pos = (const SkScalar*) this->positions().data(); device->drawPosText( fTemporaryShuntGlyphIDs.data(), fDenseSize * sizeof(SkGlyphID), - pos, 2, SkPoint::Make(0, 0), paint); + pos, 2, origin, fRunPaint); } void SkGlyphRun::temporaryShuntToCallback(TemporaryShuntCallback callback) { @@ -115,19 +116,40 @@ void SkGlyphRun::temporaryShuntToCallback(TemporaryShuntCallback callback) { } // -- SkGlyphRunList ------------------------------------------------------------------------------- -SkGlyphRunList::SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, uint64_t uniqueID) - : fUniqueID{uniqueID} - , fGlyphRuns{glyphRuns} { } +SkGlyphRunList::SkGlyphRunList( + SkSpan<SkGlyphRun> glyphRuns, SkPoint origin, const SkTextBlob* textBlob) + : fGlyphRuns{glyphRuns} + , fOrigin{origin} + , fTemporaryTextBlobShunt{textBlob} { } + +uint64_t SkGlyphRunList::uniqueID() const { + return fTemporaryTextBlobShunt != nullptr ? fTemporaryTextBlobShunt->uniqueID() + : SK_InvalidUniqueID; +} + +bool SkGlyphRunList::anyRunsLCD() const { + for (const auto& r : fGlyphRuns) { + if (r.paint().isLCDRenderText()) { + return true; + } + } + return false; +} + +void SkGlyphRunList::temporaryShuntBlobnotifyAddedToCache(uint32_t cacheID) const { + SkASSERT(fTemporaryTextBlobShunt != nullptr); + fTemporaryTextBlobShunt->notifyAddedToCache(cacheID); +} + +// -- SkGlyphRunListIterator ----------------------------------------------------------------------- +constexpr SkPoint SkGlyphRunListIterator::fZero; // -- 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->drawText( + paint, bytes, byteLength, origin, SkSpan<const char>(), SkSpan<const uint32_t>()); } void SkGlyphRunBuilder::prepareDrawPosTextH(const SkPaint& paint, const void* bytes, @@ -135,19 +157,22 @@ void SkGlyphRunBuilder::prepareDrawPosTextH(const SkPaint& paint, const void* by SkScalar constY) { this->initialize(); this->drawPosTextH( - paint, bytes, byteLength, xpos, constY, SkSpan<const char>(), SkSpan<uint32_t>()); + paint, bytes, byteLength, xpos, constY, + SkSpan<const char>(), SkSpan<const 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>()); + this->drawPosText(paint, bytes, byteLength, pos, + SkSpan<const char>(), SkSpan<const uint32_t>()); } void SkGlyphRunBuilder::prepareTextBlob( - const SkPaint& paint, const SkTextBlob& blob, SkPoint origin) { + const SkPaint& paint, const SkTextBlob& blob, SkPoint origin, SkDrawFilter* drawFilter) { this->initialize(); - fUniqueID = blob.uniqueID(); + fTemporaryTextBlobShunt = &blob; + fOrigin = origin; SkPaint runPaint = paint; @@ -156,33 +181,44 @@ void SkGlyphRunBuilder::prepareTextBlob( // so it is safe to not re-seed the paint for this reason. it.applyFontToPaint(&runPaint); + if (drawFilter != nullptr && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { + // A false return from filter() means we should abort the current draw. + runPaint = paint; + continue; + } + // 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()); + auto clusters = SkSpan<const 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); + this->drawText(runPaint, it.glyphs(), glyphLen, offset, text, clusters); } break; case SkTextBlob::kHorizontal_Positioning: { - auto constY = origin.y() + offset.y(); + auto constY = 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); + runPaint, it.glyphs(), glyphLen, (const SkPoint*)it.pos(), text, + clusters); break; default: SK_ABORT("unhandled positioning mode"); } + + if (drawFilter != nullptr) { + // A draw filter may change the paint arbitrarily, so we must re-seed in this case. + runPaint = paint; + } } } @@ -193,22 +229,29 @@ SkGlyphRun* SkGlyphRunBuilder::useGlyphRun() { } SkGlyphRunList* SkGlyphRunBuilder::useGlyphRunList() { - new ((void*)&fScratchGlyphRunList) SkGlyphRunList{SkSpan<SkGlyphRun>(fGlyphRuns), fUniqueID}; + new ((void*)&fScratchGlyphRunList) + SkGlyphRunList{SkSpan<SkGlyphRun>(fGlyphRuns), fOrigin, fTemporaryTextBlobShunt}; return &fScratchGlyphRunList; } +void SkGlyphRunBuilder::draw(SkBaseDevice* device) { + auto glyphRunList = this->useGlyphRunList(); + device->drawGlyphRunList(glyphRunList); +} + size_t SkGlyphRunBuilder::runSize() const { return fDenseIndex.size() - fLastDenseIndex; } size_t SkGlyphRunBuilder::uniqueSize() const { return fUniqueGlyphIDs.size() - fLastUniqueIndex; } void SkGlyphRunBuilder::initialize() { - fUniqueID = 0; + fTemporaryTextBlobShunt = nullptr; fDenseIndex.clear(); fPositions.clear(); fUniqueGlyphIDs.clear(); fGlyphRuns.clear(); fLastDenseIndex = 0; fLastUniqueIndex = 0; + fOrigin = {0, 0}; } SkGlyphID* SkGlyphRunBuilder::addDenseAndUnique( @@ -245,10 +288,19 @@ SkGlyphID* SkGlyphRunBuilder::addDenseAndUnique( } void SkGlyphRunBuilder::addGlyphRunToList( - SkGlyphID* temporaryShuntGlyphIDs, SkSpan<const char> text, SkSpan<uint32_t> clusters) { + const SkPaint& runPaint, + SkGlyphID* temporaryShuntGlyphIDs, + SkSpan<const char> text, + SkSpan<const uint32_t> clusters) { // Ignore empty runs. if (fDenseIndex.size() != fLastDenseIndex) { + SkPaint modifiedRunPaint{runPaint}; + + // TODO: remove these once the text stack has all the encoding and align code removed. + modifiedRunPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + modifiedRunPaint.setTextAlign(SkPaint::kLeft_Align); + auto runSize = this->runSize(); auto uniqueSize = this->uniqueSize(); @@ -258,7 +310,8 @@ void SkGlyphRunBuilder::addGlyphRunToList( fLastUniqueIndex, SkTo<uint16_t>(uniqueSize), SkSpan<SkGlyphID>(temporaryShuntGlyphIDs, runSize), text, - clusters); + clusters, + std::move(modifiedRunPaint)); fLastDenseIndex = fDenseIndex.size(); fLastUniqueIndex = fUniqueGlyphIDs.size(); @@ -267,7 +320,7 @@ void SkGlyphRunBuilder::addGlyphRunToList( void SkGlyphRunBuilder::drawText( const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin, - SkSpan<const char> text, SkSpan<uint32_t> clusters) { + SkSpan<const char> text, SkSpan<const uint32_t> clusters) { SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength); @@ -286,22 +339,22 @@ void SkGlyphRunBuilder::drawText( } if (paint.getTextAlign() != SkPaint::kLeft_Align) { - SkVector len = endOfLastGlyph - origin; + SkVector runWidth = endOfLastGlyph - origin; if (paint.getTextAlign() == SkPaint::kCenter_Align) { - len.scale(SK_ScalarHalf); + runWidth.scale(SK_ScalarHalf); } for (size_t i = fLastDenseIndex; i < this->runSize(); i++) { - fPositions[i] -= len; + fPositions[i] -= runWidth; } } - this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters); + this->addGlyphRunToList(paint, temporaryShuntGlyphIDs, text, clusters); } -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) { +void SkGlyphRunBuilder::drawPosTextH( + const SkPaint& paint, const void* bytes, size_t byteLength, + const SkScalar* xpos, SkScalar constY, + SkSpan<const char> text, SkSpan<const uint32_t> clusters) { SkGlyphID* temporaryShuntGlyphIDs = this->addDenseAndUnique(paint, bytes, byteLength); @@ -309,19 +362,20 @@ void SkGlyphRunBuilder::drawPosTextH(const SkPaint& paint, const void* bytes, fPositions.push_back(SkPoint::Make(xpos[i], constY)); } - this->addGlyphRunToList(temporaryShuntGlyphIDs, text, clusters); + this->addGlyphRunToList(paint, 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) { +void SkGlyphRunBuilder::drawPosText( + const SkPaint& paint, const void* bytes, + size_t byteLength, const SkPoint* pos, + SkSpan<const char> text, SkSpan<const 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); + this->addGlyphRunToList(paint, temporaryShuntGlyphIDs, text, clusters); } diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h index 739ad0bc38..6f9041c443 100644 --- a/src/core/SkGlyphRun.h +++ b/src/core/SkGlyphRun.h @@ -16,9 +16,11 @@ #include "SkMask.h" #include "SkPath.h" #include "SkPoint.h" +#include "SkTextBlob.h" #include "SkTypes.h" class SkBaseDevice; +class SkDrawFilter; template <typename T> class SkSpan { @@ -74,11 +76,12 @@ public: size_t fUniqueOffset, uint16_t fUniqueSize, SkSpan<SkGlyphID> scratchGlyphs, SkSpan<const char> text, - SkSpan<uint32_t> clusters); + SkSpan<const uint32_t> clusters, + SkPaint&& runPaint); // The temporaryShunt calls are to allow inter-operating with existing code while glyph runs // are developed. - void temporaryShuntToDrawPosText(const SkPaint& paint, SkBaseDevice* device); + void temporaryShuntToDrawPosText(SkBaseDevice* device, SkPoint origin); using TemporaryShuntCallback = std::function<void(size_t, const char*, const SkScalar*)>; void temporaryShuntToCallback(TemporaryShuntCallback callback); @@ -90,6 +93,21 @@ public: SkSpan<const SkGlyphID> uniqueGlyphIDs() const { return fRunInfo.uniqueGlyphIDs(fUniqueOffset, fUniqueSize); } + SkSpan<SkGlyphID> shuntGlyphsIDs() const { + return fTemporaryShuntGlyphIDs; + } + + const SkPaint& paint() const { + return fRunPaint; + } + + SkSpan<const uint32_t> clusters() const { + return fClusters; + } + + SkSpan<const char> text() const { + return fText; + } private: const SkIndexedRunInfo& fRunInfo; @@ -103,18 +121,39 @@ private: // 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; + const SkSpan<const uint32_t> fClusters; + // Paint for this run modified to have glyph encoding and left alignment. + const SkPaint fRunPaint; }; class SkGlyphRunList { - const uint64_t fUniqueID{0}; SkSpan<SkGlyphRun> fGlyphRuns; + SkPoint fOrigin = {0, 0}; + + // The text blob is needed to hookup the call back that the SkTextBlob destructor calls. It + // should be used for nothing else + const SkTextBlob* fTemporaryTextBlobShunt{nullptr}; public: SkGlyphRunList() = default; - SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, uint64_t uniqueID); + // Blob maybe null. + SkGlyphRunList(SkSpan<SkGlyphRun> glyphRuns, SkPoint origin, const SkTextBlob* blob); + + uint64_t uniqueID() const; + bool anyRunsLCD() const; + void temporaryShuntBlobnotifyAddedToCache(uint32_t cacheID) const; + + bool canCache() const { return fTemporaryTextBlobShunt != nullptr; } + ptrdiff_t runCount() const { return fGlyphRuns.size(); } + size_t totalGlyphCount() const { + size_t glyphCount = 0; + for(const auto& run : fGlyphRuns) { + glyphCount += run.runSize(); + } + return glyphCount; + } - uint64_t uniqueID() const { return fUniqueID; } + SkPoint origin() const { return fOrigin; } auto begin() -> decltype(fGlyphRuns.begin()) { return fGlyphRuns.begin(); } auto end() -> decltype(fGlyphRuns.end()) { return fGlyphRuns.end(); } @@ -122,6 +161,30 @@ public: auto operator [] (ptrdiff_t i) -> decltype(fGlyphRuns[i]) { return fGlyphRuns[i]; } }; +class SkGlyphRunListIterator { +public: + explicit SkGlyphRunListIterator(SkGlyphRunList* list) : fList{*list} {} + + bool done() const { return fIndex == fList.size(); } + void next() { fIndex += 1;} + uint32_t glyphCount() const { return fList[fIndex].runSize(); } + const uint16_t* glyphs() const { return fList[fIndex].shuntGlyphsIDs().data(); } + const SkScalar* pos() const { return (const SkScalar*)fList[fIndex].positions().data(); } + const SkPoint& offset() const { return fZero; } + void applyFontToPaint(SkPaint* paint) const { *paint = fList[fIndex].paint(); } + SkTextBlob::GlyphPositioning positioning() const { return SkTextBlob::kFull_Positioning; } + const uint32_t* clusters() const { return fList[fIndex].clusters().data(); } + uint32_t textSize() const { return fList[fIndex].text().size(); } + const char* text() const { return fList[fIndex].text().data(); } + + bool isLCD() const { return fList[fIndex].paint().isLCDRenderText(); } + +private: + static constexpr SkPoint fZero{0,0}; + ptrdiff_t fIndex{0}; + SkGlyphRunList& fList; +}; + // 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 @@ -154,10 +217,14 @@ 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); + void prepareTextBlob( + const SkPaint& paint, const SkTextBlob& blob, SkPoint origin, SkDrawFilter* filter); - SkGlyphRunList* useGlyphRunList(); + void draw(SkBaseDevice* device); + + // Public for testing only. SkGlyphRun* useGlyphRun(); + SkGlyphRunList* useGlyphRunList(); private: size_t runSize() const; @@ -165,20 +232,27 @@ private: 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); + const SkPaint& runPaint, + SkGlyphID* temporaryShuntGlyphIDs, + SkSpan<const char> text, + SkSpan<const uint32_t> clusters); void drawText( const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin, - SkSpan<const char> text, SkSpan<uint32_t> clusters); + SkSpan<const char> text, SkSpan<const 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); + SkSpan<const char> text, SkSpan<const 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); + SkSpan<const char> text, SkSpan<const uint32_t> clusters); + + const SkTextBlob* fTemporaryTextBlobShunt{nullptr}; - uint64_t fUniqueID{0}; + // The point passed into drawTextBlob. This allows the GPU back end to detect and adjust for + // translations. + SkPoint fOrigin; std::vector<uint16_t> fDenseIndex; std::vector<SkPoint> fPositions; diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp index 774a1eb77c..18e47f3f4c 100644 --- a/src/core/SkRemoteGlyphCache.cpp +++ b/src/core/SkRemoteGlyphCache.cpp @@ -16,9 +16,9 @@ #include "SkDevice.h" #include "SkDraw.h" #include "SkFindAndPlaceGlyph.h" +#include "SkGlyphRun.h" #include "SkPathEffect.h" #include "SkStrikeCache.h" -#include "SkTextBlobRunIterator.h" #include "SkTraceEvent.h" #include "SkTypeface_remote.h" @@ -240,27 +240,20 @@ public: } protected: - void drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint, - SkDrawFilter* drawFilter) override { - // The looper should be applied by the SkCanvas. - SkASSERT(paint.getDrawLooper() == nullptr); - // We don't support SkDrawFilter. - SkASSERT(drawFilter == nullptr); - - SkPoint position{x, y}; - SkPaint runPaint{paint}; - SkTextBlobRunIterator it(blob); + void drawGlyphRunList(SkGlyphRunList* glyphRunList) override { + SkPaint runPaint; + SkGlyphRunListIterator it(glyphRunList); for (; !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); - this->processGlyphRun(position, it, runPaint); + this->processGlyphRun(glyphRunList->origin(), it, runPaint); } } private: void processGlyphRun(const SkPoint& position, - const SkTextBlobRunIterator& it, + const SkGlyphRunListIterator& it, const SkPaint& runPaint) { TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRun"); @@ -344,7 +337,7 @@ private: } } - void processGlyphRunForPaths(const SkTextBlobRunIterator& it, + void processGlyphRunForPaths(const SkGlyphRunListIterator& it, const SkPaint& runPaint, const SkMatrix& runMatrix) { TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForPaths"); @@ -394,7 +387,7 @@ private: } #if SK_SUPPORT_GPU - bool processGlyphRunForDFT(const SkTextBlobRunIterator& it, + bool processGlyphRunForDFT(const SkGlyphRunListIterator& it, const SkPaint& runPaint, const SkMatrix& runMatrix) { TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForDFT"); diff --git a/src/core/SkTypeface_remote.h b/src/core/SkTypeface_remote.h index 47a68fd08d..f7326ccf7d 100644 --- a/src/core/SkTypeface_remote.h +++ b/src/core/SkTypeface_remote.h @@ -123,8 +123,7 @@ protected: return 0; } int onCountGlyphs() const override { - SK_ABORT("Should never be called."); - return 0; + return this->glyphCount(); } void* onGetCTFontRef() const override { diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp index 1dbde17575..9d31a0dde6 100644 --- a/src/gpu/GrRenderTargetContext.cpp +++ b/src/gpu/GrRenderTargetContext.cpp @@ -237,18 +237,17 @@ void GrRenderTargetContext::drawPosText(const GrClip& clip, const SkPaint& paint clipBounds); } -void GrRenderTargetContext::drawTextBlob(const GrClip& clip, const SkPaint& paint, - const SkMatrix& viewMatrix, const SkTextBlob* blob, - SkScalar x, SkScalar y, SkDrawFilter* filter, - const SkIRect& clipBounds) { +void GrRenderTargetContext::drawGlyphRunList( + const GrClip& clip, const SkMatrix& viewMatrix, + SkGlyphRunList* blob, const SkIRect& clipBounds) { ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) - GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextBlob", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawGlyphRunList", fContext); GrTextContext* atlasTextContext = this->drawingManager()->getTextContext(); - atlasTextContext->drawTextBlob(fContext, fTextTarget.get(), clip, paint, viewMatrix, - fSurfaceProps, blob, x, y, filter, clipBounds); + atlasTextContext->drawGlyphRunList(fContext, fTextTarget.get(), clip, viewMatrix, + fSurfaceProps, blob, clipBounds); } void GrRenderTargetContext::discard() { diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h index 783b351b06..b5293982fb 100644 --- a/src/gpu/GrRenderTargetContext.h +++ b/src/gpu/GrRenderTargetContext.h @@ -37,6 +37,7 @@ class GrTextureProxy; struct GrUserStencilSettings; class SkDrawFilter; struct SkDrawShadowRec; +class SkGlyphRunList; struct SkIPoint; struct SkIRect; class SkLatticeIter; @@ -67,10 +68,9 @@ public: const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& clipBounds); - virtual void drawTextBlob(const GrClip&, const SkPaint&, - const SkMatrix& viewMatrix, const SkTextBlob*, - SkScalar x, SkScalar y, - SkDrawFilter*, const SkIRect& clipBounds); + virtual void drawGlyphRunList(const GrClip&, + const SkMatrix& viewMatrix, SkGlyphRunList*, + const SkIRect& clipBounds); /** * Provides a perfomance hint that the render target's contents are allowed diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 819acd2ef8..d4f329dcb2 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1624,14 +1624,13 @@ void SkGpuDevice::drawPosText(const void* text, size_t byteLength, this->devClipBounds()); } -void SkGpuDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint, SkDrawFilter* drawFilter) { +void SkGpuDevice::drawGlyphRunList(SkGlyphRunList* list) { ASSERT_SINGLE_OWNER - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawTextBlob", fContext.get()); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawGlyphRunList", fContext.get()); SkDEBUGCODE(this->validate();) - fRenderTargetContext->drawTextBlob(this->clip(), paint, this->ctm(), blob, x, y, drawFilter, - this->devClipBounds()); + fRenderTargetContext->drawGlyphRunList( + this->clip(), this->ctm(), list, this->devClipBounds()); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index 7c7030e1ab..0bd1c80346 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -89,8 +89,7 @@ public: const SkPaint& paint) override; void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint&) override; - void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, - const SkPaint& paint, SkDrawFilter* drawFilter) override; + void drawGlyphRunList(SkGlyphRunList* list) override; void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; void drawShadow(const SkPath&, const SkDrawShadowRec&) override; void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[], diff --git a/src/gpu/text/GrTextBlobCache.h b/src/gpu/text/GrTextBlobCache.h index b41d401aee..7a8ea0bcfe 100644 --- a/src/gpu/text/GrTextBlobCache.h +++ b/src/gpu/text/GrTextBlobCache.h @@ -55,6 +55,20 @@ public: blob->notifyAddedToCache(fUniqueID); return cacheBlob; } + sk_sp<GrTextBlob> makeBlob(SkGlyphRunList* glyphRunList) { + return GrTextBlob::Make(glyphRunList->totalGlyphCount(), glyphRunList->size()); + } + + sk_sp<GrTextBlob> makeCachedBlob(SkGlyphRunList* glyphRunList, + const GrTextBlob::Key& key, + const SkMaskFilterBase::BlurRec& blurRec, + const SkPaint& paint) { + sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList)); + cacheBlob->setupKey(key, blurRec, paint); + this->add(cacheBlob); + glyphRunList->temporaryShuntBlobnotifyAddedToCache(fUniqueID); + return cacheBlob; + } sk_sp<GrTextBlob> find(const GrTextBlob::Key& key) const { const auto* idEntry = fBlobIDCache.find(key.fUniqueID); diff --git a/src/gpu/text/GrTextContext.cpp b/src/gpu/text/GrTextContext.cpp index a20ba4424a..3309d22d09 100644 --- a/src/gpu/text/GrTextContext.cpp +++ b/src/gpu/text/GrTextContext.cpp @@ -84,58 +84,48 @@ SkScalerContextFlags GrTextContext::ComputeScalerContextFlags( } } -// TODO if this function ever shows up in profiling, then we can compute this value when the -// textblob is being built and cache it. However, for the time being textblobs mostly only have 1 -// run so this is not a big deal to compute here. -bool GrTextContext::HasLCD(const SkTextBlob* blob) { - SkTextBlobRunIterator it(blob); - for (; !it.done(); it.next()) { - if (it.isLCD()) { - return true; - } - } - return false; -} +void GrTextContext::drawGlyphRunList( + GrContext* context, GrTextUtils::Target* target, const GrClip& clip, + const SkMatrix& viewMatrix, const SkSurfaceProps& props, SkGlyphRunList* glyphRunList, + const SkIRect& clipBounds) { + SkPoint origin = glyphRunList->origin(); + + // Get the first paint to use as the key paint. + const SkPaint& skPaint = (*glyphRunList)[0].paint(); -void GrTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target, - const GrClip& clip, const SkPaint& skPaint, - const SkMatrix& viewMatrix, const SkSurfaceProps& props, - const SkTextBlob* blob, SkScalar x, SkScalar y, - SkDrawFilter* drawFilter, const SkIRect& clipBounds) { // If we have been abandoned, then don't draw if (context->abandoned()) { return; } - sk_sp<GrTextBlob> cacheBlob; SkMaskFilterBase::BlurRec blurRec; - GrTextBlob::Key key; // It might be worth caching these things, but its not clear at this time // TODO for animated mask filters, this will fill up our cache. We need a safeguard here const SkMaskFilter* mf = skPaint.getMaskFilter(); - bool canCache = !(skPaint.getPathEffect() || - (mf && !as_MFB(mf)->asABlur(&blurRec)) || - drawFilter); + bool canCache = glyphRunList->canCache() && !(skPaint.getPathEffect() || + (mf && !as_MFB(mf)->asABlur(&blurRec))); SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo()); auto glyphCache = context->contextPriv().getGlyphCache(); GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache(); + sk_sp<GrTextBlob> cacheBlob; + GrTextBlob::Key key; if (canCache) { - bool hasLCD = HasLCD(blob); + bool hasLCD = glyphRunList->anyRunsLCD(); // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() : - kUnknown_SkPixelGeometry; + kUnknown_SkPixelGeometry; // TODO we want to figure out a way to be able to use the canonical color on LCD text, // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to // ensure we always match the same key GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT : - ComputeCanonicalColor(skPaint, hasLCD); + ComputeCanonicalColor(skPaint, hasLCD); key.fPixelGeometry = pixelGeometry; - key.fUniqueID = blob->uniqueID(); + key.fUniqueID = glyphRunList->uniqueID(); key.fStyle = skPaint.getStyle(); key.fHasBlur = SkToBool(mf); key.fCanonicalColor = canonicalColor; @@ -145,135 +135,77 @@ void GrTextContext::drawTextBlob(GrContext* context, GrTextUtils::Target* target GrTextUtils::Paint paint(&skPaint, &target->colorSpaceInfo()); if (cacheBlob) { - if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) { + if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, origin.x(), origin.y())) { // We have to remake the blob because changes may invalidate our masks. // TODO we could probably get away reuse most of the time if the pointer is unique, // but we'd have to clear the subrun information textBlobCache->remove(cacheBlob.get()); - cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint); - this->regenerateTextBlob(cacheBlob.get(), glyphCache, + cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint); + this->regenerateGlyphRunList(cacheBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(), paint, - scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter); + scalerContextFlags, viewMatrix, props, glyphRunList); } else { textBlobCache->makeMRU(cacheBlob.get()); if (CACHE_SANITY_CHECK) { - int glyphCount = 0; - int runCount = 0; - GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob); + int glyphCount = glyphRunList->totalGlyphCount(); + int runCount = glyphRunList->runCount(); sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphCount, runCount)); sanityBlob->setupKey(key, blurRec, skPaint); - this->regenerateTextBlob( + this->regenerateGlyphRunList( sanityBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(), - paint, scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter); + paint, scalerContextFlags, viewMatrix, props, glyphRunList); GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob); } } } else { if (canCache) { - cacheBlob = textBlobCache->makeCachedBlob(blob, key, blurRec, skPaint); + cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, skPaint); } else { - cacheBlob = textBlobCache->makeBlob(blob); + cacheBlob = textBlobCache->makeBlob(glyphRunList); } - this->regenerateTextBlob(cacheBlob.get(), glyphCache, + this->regenerateGlyphRunList(cacheBlob.get(), glyphCache, *context->contextPriv().caps()->shaderCaps(), paint, - scalerContextFlags, viewMatrix, props, blob, x, y, drawFilter); + scalerContextFlags, viewMatrix, props, glyphRunList); } cacheBlob->flush(target, props, fDistanceAdjustTable.get(), paint, - clip, viewMatrix, clipBounds, x, y); + clip, viewMatrix, clipBounds, origin.x(), origin.y()); } -void GrTextContext::regenerateTextBlob(GrTextBlob* cacheBlob, +void GrTextContext::regenerateGlyphRunList(GrTextBlob* cacheBlob, GrGlyphCache* glyphCache, const GrShaderCaps& shaderCaps, const GrTextUtils::Paint& paint, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, - const SkSurfaceProps& props, const SkTextBlob* blob, - SkScalar x, SkScalar y, - SkDrawFilter* drawFilter) const { - cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y); + const SkSurfaceProps& props, + SkGlyphRunList* glyphRunList) const { + SkPoint origin = glyphRunList->origin(); + cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, origin.x(), origin.y()); // Regenerate textblob - SkTextBlobRunIterator it(blob); - GrTextUtils::RunPaint runPaint(&paint, drawFilter); + SkGlyphRunListIterator it(glyphRunList); + GrTextUtils::RunPaint runPaint(&paint, nullptr); for (int run = 0; !it.done(); it.next(), run++) { int glyphCount = it.glyphCount(); size_t textLen = glyphCount * sizeof(uint16_t); - const SkPoint& offset = it.offset(); cacheBlob->push_back_run(run); if (!runPaint.modifyForRun([it](SkPaint* p) { it.applyFontToPaint(p); })) { continue; } cacheBlob->setRunPaintFlags(run, runPaint.skPaint().getFlags()); + SkASSERT(it.positioning() == SkTextBlob::kFull_Positioning); if (CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps.supportsDistanceFieldText(), fOptions)) { - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: { - auto origin = SkPoint::Make(x + offset.x(), y + offset.y()); - SkGlyphRunBuilder builder; - builder.prepareDrawText(runPaint.skPaint(), - (const char*)it.glyphs(), textLen, origin); - - auto glyphRun = builder.useGlyphRun(); - - glyphRun->temporaryShuntToCallback( - [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) { - this->drawDFPosText( - cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, - viewMatrix, glyphIDs, 2 * runSize, pos, 2, - SkPoint::Make(0,0)); - }); - break; - } - - case SkTextBlob::kHorizontal_Positioning: { - SkPoint dfOffset = SkPoint::Make(x, y + offset.y()); - this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint, - scalerContextFlags, viewMatrix, (const char*)it.glyphs(), - textLen, it.pos(), 1, dfOffset); - break; - } - case SkTextBlob::kFull_Positioning: { - SkPoint dfOffset = SkPoint::Make(x, y); - this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint, - scalerContextFlags, viewMatrix, (const char*)it.glyphs(), - textLen, it.pos(), 2, dfOffset); - break; - } - } + this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint, + scalerContextFlags, viewMatrix, (const char*)it.glyphs(), + textLen, it.pos(), 2, origin); } else { - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: { - auto origin = SkPoint::Make(x + offset.x(), y + offset.y()); - SkGlyphRunBuilder builder; - builder.prepareDrawText(runPaint.skPaint(), - (const char*)it.glyphs(), textLen, origin); - - auto glyphRun = builder.useGlyphRun(); - - glyphRun->temporaryShuntToCallback( - [&](size_t runSize, const char* glyphIDs, const SkScalar* pos) { - this->DrawBmpPosText( - cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, - viewMatrix, glyphIDs, 2 * runSize, - pos, 2, SkPoint::Make(0, 0)); - }); - break; - } - case SkTextBlob::kHorizontal_Positioning: - DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, - viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, - SkPoint::Make(x, y + offset.y())); - break; - case SkTextBlob::kFull_Positioning: - DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, - viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, - SkPoint::Make(x, y)); - break; - } + DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, + origin); } } } @@ -333,7 +265,6 @@ void GrTextContext::drawPosText(GrContext* context, GrTextUtils::Target* target, } } - void GrTextContext::DrawBmpPosText(GrTextBlob* blob, int runIndex, GrGlyphCache* glyphCache, const SkSurfaceProps& props, const GrTextUtils::Paint& paint, @@ -786,6 +717,7 @@ std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context builder.prepareDrawText(skPaint, text, textLen, origin); sk_sp<GrTextBlob> blob; + // TODO - remove shunt call when removing drawPosText from device. auto glyphRun = builder.useGlyphRun(); // Use the text and textLen below, because we don't want to mess with the paint. glyphRun->temporaryShuntToCallback( diff --git a/src/gpu/text/GrTextContext.h b/src/gpu/text/GrTextContext.h index dd82b3ce7d..25aeea0c7a 100644 --- a/src/gpu/text/GrTextContext.h +++ b/src/gpu/text/GrTextContext.h @@ -48,9 +48,9 @@ public: const SkMatrix& viewMatrix, const SkSurfaceProps&, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& regionClipBounds); - void drawTextBlob(GrContext*, GrTextUtils::Target*, const GrClip&, const SkPaint&, - const SkMatrix& viewMatrix, const SkSurfaceProps&, const SkTextBlob*, - SkScalar x, SkScalar y, SkDrawFilter*, const SkIRect& clipBounds); + void drawGlyphRunList(GrContext*, GrTextUtils::Target*, const GrClip&, + const SkMatrix& viewMatrix, const SkSurfaceProps&, SkGlyphRunList*, + const SkIRect& clipBounds); std::unique_ptr<GrDrawOp> createOp_TestingOnly(GrContext*, GrTextContext*, @@ -115,17 +115,15 @@ private: static SkColor ComputeCanonicalColor(const SkPaint&, bool lcd); // Determines if we need to use fake gamma (and contrast boost): static SkScalerContextFlags ComputeScalerContextFlags(const GrColorSpaceInfo&); - void regenerateTextBlob(GrTextBlob* bmp, + + void regenerateGlyphRunList(GrTextBlob* bmp, GrGlyphCache*, const GrShaderCaps&, const GrTextUtils::Paint&, SkScalerContextFlags scalerContextFlags, const SkMatrix& viewMatrix, const SkSurfaceProps&, - const SkTextBlob* blob, SkScalar x, SkScalar y, - SkDrawFilter* drawFilter) const; - - static bool HasLCD(const SkTextBlob*); + SkGlyphRunList* glyphRunList) const; sk_sp<GrTextBlob> makeDrawPosTextBlob(GrTextBlobCache*, GrGlyphCache*, const GrShaderCaps&, diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 45a9b95915..72d543c67e 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -19,6 +19,7 @@ #include "SkDraw.h" #include "SkDrawFilter.h" #include "SkGlyphCache.h" +#include "SkGlyphRun.h" #include "SkImageFilterCache.h" #include "SkJpegEncoder.h" #include "SkMakeUnique.h" @@ -1458,17 +1459,12 @@ void SkPDFDevice::drawPosText(const void* text, size_t len, offset, paint, nullptr, 0, nullptr); } -void SkPDFDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint &paint, SkDrawFilter* drawFilter) { - for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) { - SkPaint runPaint(paint); +void SkPDFDevice::drawGlyphRunList(SkGlyphRunList* glyphRunList) { + for (SkGlyphRunListIterator it(glyphRunList); !it.done(); it.next()) { + SkPaint runPaint; it.applyFontToPaint(&runPaint); - if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { - continue; - } - SkPoint offset = it.offset() + SkPoint{x, y}; this->internalDrawText(it.glyphs(), sizeof(SkGlyphID) * it.glyphCount(), - it.pos(), it.positioning(), offset, runPaint, + it.pos(), it.positioning(), glyphRunList->origin(), runPaint, it.clusters(), it.textSize(), it.text()); } } diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h index 33d0e70e67..90f4b8e480 100644 --- a/src/pdf/SkPDFDevice.h +++ b/src/pdf/SkPDFDevice.h @@ -22,6 +22,7 @@ #include "SkTextBlob.h" #include "SkKeyedImage.h" +class SkGlyphRunList; class SkKeyedImage; class SkPath; class SkPDFArray; @@ -98,8 +99,7 @@ public: void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint&) override; - void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, - const SkPaint &, SkDrawFilter*) override; + void drawGlyphRunList(SkGlyphRunList* glyphRunList) override; void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override; diff --git a/tests/GlyphRunTest.cpp b/tests/GlyphRunTest.cpp index cd2a221719..8107f2075c 100644 --- a/tests/GlyphRunTest.cpp +++ b/tests/GlyphRunTest.cpp @@ -75,7 +75,7 @@ DEF_TEST(GlyphRunBlob, reporter) { paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkGlyphRunBuilder runBuilder; - runBuilder.prepareTextBlob(font, *blob, SkPoint::Make(0, 0)); + runBuilder.prepareTextBlob(font, *blob, SkPoint::Make(0, 0), nullptr); auto runList = runBuilder.useGlyphRunList(); @@ -96,4 +96,4 @@ DEF_TEST(GlyphRunBlob, reporter) { runIndex += 1; } -}
\ No newline at end of file +} diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp index 4a7bd86dc0..6986a7bb03 100644 --- a/tests/SkRemoteGlyphCacheTest.cpp +++ b/tests/SkRemoteGlyphCacheTest.cpp @@ -69,6 +69,8 @@ sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) { font.setStyle(SkPaint::kFill_Style); font.setHinting(SkPaint::kNormal_Hinting); font.setTextSize(1u); + font.setAntiAlias(true); + font.setSubpixelText(true); SkTextBlobBuilder builder; SkRect bounds = SkRect::MakeWH(10, 10); @@ -99,12 +101,13 @@ SkTextBlobCacheDiffCanvas::Settings MakeSettings(GrContext* context) { } SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint, - GrContext* context, const SkMatrix* matrix = nullptr) { + GrContext* context, const SkMatrix* matrix = nullptr, + SkScalar x = 0) { const SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); auto surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info); if (matrix) surface->getCanvas()->concat(*matrix); - surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint); + surface->getCanvas()->drawTextBlob(blob.get(), x, 0, paint); SkBitmap bitmap; bitmap.allocN32Pixels(width, height); surface->readPixels(bitmap, 0, 0); @@ -332,6 +335,45 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, discardableManager->unlockAndDeleteAll(); } +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager, false); + SkPaint paint; + paint.setAntiAlias(true); + paint.setSubpixelText(true); + paint.setLCDRenderText(true); + + // Server. + auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + auto serverTfData = server.serializeTypeface(serverTf.get()); + + int glyphCount = 10; + auto serverBlob = buildTextBlob(serverTf, glyphCount); + const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server, + MakeSettings(ctxInfo.grContext())); + cache_diff_canvas.drawTextBlob(serverBlob.get(), 0.5, 0, paint); + + std::vector<uint8_t> serverStrikeData; + server.writeStrikeData(&serverStrikeData); + + // Client. + auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()); + REPORTER_ASSERT(reporter, + client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); + auto clientBlob = buildTextBlob(clientTf, glyphCount); + + SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5); + SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5); + COMPARE_BLOBS(expected, actual, reporter); + REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); + SkStrikeCache::ValidateGlyphCacheDataSize(); + + // Must unlock everything on termination, otherwise valgrind complains about memory leaks. + discardableManager->unlockAndDeleteAll(); +} + DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) { sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); SkStrikeServer server(discardableManager.get()); |