diff options
-rw-r--r-- | include/core/SkPaint.h | 1 | ||||
-rw-r--r-- | src/core/SkGlyph.cpp | 5 | ||||
-rw-r--r-- | src/core/SkGlyph.h | 1 | ||||
-rw-r--r-- | src/core/SkGlyphCache.cpp | 25 | ||||
-rw-r--r-- | src/core/SkGlyphCache.h | 10 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.cpp | 324 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.h | 20 | ||||
-rw-r--r-- | tests/SkRemoteGlyphCacheTest.cpp | 57 |
8 files changed, 297 insertions, 146 deletions
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 3cc63b8fe8..85c2e3b2f7 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -1690,6 +1690,7 @@ private: friend class SkPDFDevice; friend class SkScalerContext; // for computeLuminanceColor() friend class SkTextBaseIter; + friend class SkTextBlobCacheDiffCanvas; }; #endif diff --git a/src/core/SkGlyph.cpp b/src/core/SkGlyph.cpp index 836a47d608..9e040e39a4 100644 --- a/src/core/SkGlyph.cpp +++ b/src/core/SkGlyph.cpp @@ -60,6 +60,11 @@ static size_t format_rowbytes(int width, SkMask::Format format) { : width * format_alignment(format); } +size_t SkGlyph::formatAlignment() const { + auto format = static_cast<SkMask::Format>(fMaskFormat); + return format_alignment(format); +} + size_t SkGlyph::allocImage(SkArenaAlloc* alloc) { auto size = this->computeImageSize(); auto format = static_cast<SkMask::Format>(fMaskFormat); diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h index dfb51cb35a..307042f724 100644 --- a/src/core/SkGlyph.h +++ b/src/core/SkGlyph.h @@ -155,6 +155,7 @@ public: void initWithGlyphID(SkPackedGlyphID glyph_id); + size_t formatAlignment() const; size_t allocImage(SkArenaAlloc* alloc); size_t rowBytes() const; diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp index d9d8d41eb1..bf5f77c817 100644 --- a/src/core/SkGlyphCache.cpp +++ b/src/core/SkGlyphCache.cpp @@ -194,7 +194,9 @@ const void* SkGlyphCache::findImage(const SkGlyph& glyph) { return glyph.fImage; } -void SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGlyph* glyph) { +bool SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGlyph* glyph) { + if (glyph->fImage) return false; + if (glyph->fWidth > 0 && glyph->fWidth < kMaxGlyphWidth) { size_t allocSize = glyph->allocImage(&fAlloc); // check that alloc() actually succeeded @@ -204,6 +206,8 @@ void SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGly fMemoryUsed += size; } } + + return true; } const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { @@ -225,6 +229,25 @@ const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { return glyph.fPathData ? glyph.fPathData->fPath : nullptr; } +bool SkGlyphCache::initializePath(SkGlyph* glyph, const volatile void* data, size_t size) { + if (glyph->fPathData) return false; + + if (glyph->fWidth) { + SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>(); + glyph->fPathData = pathData; + pathData->fIntercept = nullptr; + SkPath* path = new SkPath; + if (!path->readFromMemory(const_cast<const void*>(data), size)) { + delete path; + return false; + } + pathData->fPath = path; + fMemoryUsed += compute_path_size(*path); + } + + return true; +} + #include "../pathops/SkPathOpsCubic.h" #include "../pathops/SkPathOpsQuad.h" diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h index e069efd905..2e7355779a 100644 --- a/src/core/SkGlyphCache.h +++ b/src/core/SkGlyphCache.h @@ -88,9 +88,10 @@ public: */ const void* findImage(const SkGlyph&); - /** Initializes the image associated with the glyph with |data|. + /** Initializes the image associated with the glyph with |data|. Returns false if an image + * already exists. */ - void initializeImage(const volatile void* data, size_t size, SkGlyph*); + bool initializeImage(const volatile void* data, size_t size, SkGlyph*); /** If the advance axis intersects the glyph's path, append the positions scaled and offset to the array (if non-null), and set the count to the updated array length. @@ -103,6 +104,11 @@ public: */ const SkPath* findPath(const SkGlyph&); + /** Initializes the path associated with the glyph with |data|. Returns false if a path + * already exits or data is invalid. + */ + bool initializePath(SkGlyph*, const volatile void* data, size_t size); + /** Return the vertical metrics for this strike. */ const SkPaint::FontMetrics& getFontMetrics() const { diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp index 985c0655e6..c7f1f4497b 100644 --- a/src/core/SkRemoteGlyphCache.cpp +++ b/src/core/SkRemoteGlyphCache.cpp @@ -13,12 +13,18 @@ #include <tuple> #include "SkDevice.h" +#include "SkDraw.h" #include "SkFindAndPlaceGlyph.h" +#include "SkPathEffect.h" #include "SkStrikeCache.h" #include "SkTextBlobRunIterator.h" #include "SkTraceEvent.h" #include "SkTypeface_remote.h" +#if SK_SUPPORT_GPU +#include "GrDrawOpAtlas.h" +#endif + static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc, SkFontID font_id, SkAutoDescriptor* ad) { @@ -54,33 +60,6 @@ static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc, return desc; } -template <typename T> -class ArraySlice final : public std::tuple<const T*, size_t> { -public: - // Additional constructors as needed. - ArraySlice(const T* data, size_t size) : fData{data}, fSize{size} { } - ArraySlice() : ArraySlice<T>(nullptr, 0) { } - - const T* begin() { - return this->data(); - } - const T* end() { - return &this->data()[this->size()]; - } - - const T* data() const { - return fData; - } - - size_t size() const { - return fSize; - } - -private: - const T* fData; - size_t fSize; -}; - // -- Serializer ---------------------------------------------------------------------------------- size_t pad(size_t size, size_t alignment) { return (size + (alignment - 1)) & ~(alignment - 1); } @@ -113,19 +92,13 @@ public: memcpy(result, &desc, desc.getLength()); } - template <typename T> - T* allocateArray(int count) { - auto result = allocate(sizeof(T) * count, alignof(T)); - return new (result) T[count]; - } - -private: void* allocate(size_t size, size_t alignment) { size_t aligned = pad(fBuffer->size(), alignment); fBuffer->resize(aligned + size); return &(*fBuffer)[aligned]; } +private: std::vector<uint8_t>* fBuffer; }; @@ -157,14 +130,8 @@ public: return true; } - template <typename T> - ArraySlice<T> readArray(int count) { - size_t size = count * sizeof(T); - const T* base = (const T*)this->ensureAtLeast(size, alignof(T)); - if (!base) return ArraySlice<T>(); - - ArraySlice<T> result = ArraySlice<T>{base, (uint32_t)count}; - return result; + const volatile void* read(size_t size, size_t alignment) { + return this->ensureAtLeast(size, alignment); } private: @@ -185,6 +152,21 @@ private: size_t fBytesRead = 0u; }; +// Paths use a SkWriter32 which requires 4 byte alignment. +static const size_t kPathAlignment = 4u; + +bool read_path(Deserializer* deserializer, SkGlyph* glyph, SkGlyphCache* cache) { + size_t pathSize = 0u; + if (!deserializer->read<size_t>(&pathSize)) return false; + + if (pathSize == 0u) return true; + + auto* path = deserializer->read(pathSize, kPathAlignment); + if (!path) return false; + + return cache->initializePath(glyph, path, pathSize); +} + size_t SkDescriptorMapOperators::operator()(const SkDescriptor* key) const { return key->getChecksum(); } @@ -281,15 +263,27 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( FAIL_AND_RETURN } + if (it.positioning() == SkTextBlob::kDefault_Positioning) { + // Default positioning needs advances. Can't do that. + TRACE_EVENT0("skia", "kDefault_Positioning"); + FAIL_AND_RETURN + } + + SkMatrix runMatrix{fDeviceMatrix}; + runMatrix.preConcat(this->getTotalMatrix()); + runMatrix.preTranslate(position.x(), position.y()); + runMatrix.preTranslate(it.offset().x(), it.offset().y()); + + // If the matrix has perspective, we fall back to using distance field text or paths. + // TODO: Add distance field text support, and FallbackTextHelper logic from GrAtlasTextContext. + if (SkDraw::ShouldDrawTextAsPaths(runPaint, runMatrix)) { + this->processGlyphRunForPaths(it, runPaint); + return; + } + using PosFn = SkPoint(*)(int index, const SkScalar* pos); PosFn posFn; switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: { - // Default positioning needs advances. Can't do that. - TRACE_EVENT0("skia", "kDefault_Positioning"); - FAIL_AND_RETURN - } - case SkTextBlob::kHorizontal_Positioning: posFn = [](int index, const SkScalar* pos) { return SkPoint{pos[index], 0}; @@ -308,17 +302,6 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( SK_ABORT("unhandled positioning mode"); } - SkMatrix blobMatrix{fDeviceMatrix}; - blobMatrix.preConcat(this->getTotalMatrix()); - if (blobMatrix.hasPerspective()) { - TRACE_EVENT0("skia", "hasPerspective"); - FAIL_AND_RETURN - } - blobMatrix.preTranslate(position.x(), position.y()); - - SkMatrix runMatrix{blobMatrix}; - runMatrix.preTranslate(it.offset().x(), it.offset().y()); - using MapFn = SkPoint(*)(const SkMatrix& m, SkPoint pt); MapFn mapFn; switch ((int)runMatrix.getType()) { @@ -350,35 +333,13 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( } SkScalerContextRec deviceSpecificRec; - SkScalerContextRec keyRec; SkScalerContextEffects effects; - - SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix, - SkScalerContextFlags::kFakeGammaAndBoostContrast, - &deviceSpecificRec, - &effects, true); - - SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix, - SkScalerContextFlags::kFakeGammaAndBoostContrast, - &keyRec, - &effects, false); - - - TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(keyRec.dump().c_str())); - - // TODO: possible perf improvement - move descriptor calculation into getOrCreateCache. - auto deviceDescriptor = - SkScalerContext::DescriptorGivenRecAndEffects(deviceSpecificRec, effects); - auto keyDescriptor = - SkScalerContext::DescriptorGivenRecAndEffects(keyRec, effects); - auto* glyphCacheState = - static_cast<SkStrikeServer*>(fStrikeServer) - ->getOrCreateCache( - runPaint.getTypeface(), - std::move(deviceDescriptor), - std::move(keyDescriptor)); + auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer) + ->getOrCreateCache(runPaint, &fSurfaceProps, &runMatrix, + &deviceSpecificRec, &effects); SkASSERT(glyphCacheState); + const bool asPath = false; bool isSubpixel = SkToBool(deviceSpecificRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); SkAxisAlignment axisAlignment = deviceSpecificRec.computeAxisAlignmentForHText(); @@ -393,18 +354,43 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( glyphCacheState->addGlyph(runPaint.getTypeface(), effects, - SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y())); + SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()), + asPath); + } +} + +void SkTextBlobCacheDiffCanvas::processGlyphRunForPaths(const SkTextBlobRunIterator& it, + const SkPaint& runPaint) { + TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForPaths"); + + // The code below borrowed from GrAtlasTextContext::DrawBmpPosTextAsPaths. + SkPaint pathPaint(runPaint); + pathPaint.setupForAsPaths(); + pathPaint.setStyle(SkPaint::kFill_Style); + pathPaint.setPathEffect(nullptr); + + SkScalerContextRec deviceSpecificRec; + SkScalerContextEffects effects; + auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer) + ->getOrCreateCache(pathPaint, &fSurfaceProps, nullptr, + &deviceSpecificRec, &effects); + + const bool asPath = true; + const SkIPoint subPixelPos{0, 0}; + const uint16_t* glyphs = it.glyphs(); + for (uint32_t index = 0; index < it.glyphCount(); index++) { + glyphCacheState->addGlyph(runPaint.getTypeface(), + effects, + SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()), + asPath); } } struct StrikeSpec { StrikeSpec() {} - StrikeSpec(SkFontID typefaceID_, size_t glyphCount_, SkDiscardableHandleId discardableHandleId_) - : typefaceID{typefaceID_} - , glyphCount{glyphCount_} - , discardableHandleId(discardableHandleId_) {} + StrikeSpec(SkFontID typefaceID_, SkDiscardableHandleId discardableHandleId_) + : typefaceID{typefaceID_}, discardableHandleId(discardableHandleId_) {} SkFontID typefaceID = 0u; - size_t glyphCount = 0u; SkDiscardableHandleId discardableHandleId = 0u; /* desc */ /* n X (glyphs ids) */ @@ -462,11 +448,23 @@ void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) { } SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( - SkTypeface* tf, - std::unique_ptr<SkDescriptor> deviceDesc, - std::unique_ptr<SkDescriptor> keyDesc) { - SkASSERT(deviceDesc); - SkASSERT(keyDesc); + const SkPaint& paint, + const SkSurfaceProps* props, + const SkMatrix* matrix, + SkScalerContextRec* deviceRec, + SkScalerContextEffects* effects) { + SkScalerContextRec keyRec; + SkScalerContext::MakeRecAndEffects(paint, props, matrix, + SkScalerContextFlags::kFakeGammaAndBoostContrast, deviceRec, + effects, true); + SkScalerContext::MakeRecAndEffects(paint, props, matrix, + SkScalerContextFlags::kFakeGammaAndBoostContrast, &keyRec, + effects, false); + TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(keyRec.dump().c_str())); + + // TODO: possible perf improvement - don't recompute the device desc on cache hit. + auto deviceDesc = SkScalerContext::DescriptorGivenRecAndEffects(*deviceRec, *effects); + auto keyDesc = SkScalerContext::DescriptorGivenRecAndEffects(keyRec, *effects); // Already locked. if (fLockedDescs.find(keyDesc.get()) != fLockedDescs.end()) { @@ -490,6 +488,7 @@ SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( fRemoteGlyphStateMap.erase(it); } + auto* tf = paint.getTypeface(); const SkFontID typefaceId = tf->uniqueID(); if (!fCachedTypefaces.contains(typefaceId)) { fCachedTypefaces.add(typefaceId); @@ -499,10 +498,8 @@ SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( auto* keyDescPtr = keyDesc.get(); auto newHandle = fDiscardableHandleManager->createHandle(); - auto cacheState = skstd::make_unique<SkGlyphCacheState>( - std::move(deviceDesc), - std::move(keyDesc), - newHandle); + auto cacheState = skstd::make_unique<SkGlyphCacheState>(std::move(deviceDesc), + std::move(keyDesc), newHandle); auto* cacheStatePtr = cacheState.get(); fLockedDescs.insert(keyDescPtr); @@ -510,10 +507,10 @@ SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( return cacheStatePtr; } -SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState( - std::unique_ptr<SkDescriptor> deviceDescriptor, - std::unique_ptr<SkDescriptor> keyDescriptor, - uint32_t discardable_handle_id) +SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(std::unique_ptr<SkDescriptor> deviceDescriptor, + std::unique_ptr<SkDescriptor> + keyDescriptor, + uint32_t discardable_handle_id) : fDeviceDescriptor(std::move(deviceDescriptor)) , fKeyDescriptor(std::move(keyDescriptor)) , fDiscardableHandleId(discardable_handle_id) { @@ -525,51 +522,95 @@ SkStrikeServer::SkGlyphCacheState::~SkGlyphCacheState() = default; void SkStrikeServer::SkGlyphCacheState::addGlyph(SkTypeface* typeface, const SkScalerContextEffects& effects, - SkPackedGlyphID glyph) { + SkPackedGlyphID glyph, + bool asPath) { + auto* cache = asPath ? &fCachedGlyphPaths : &fCachedGlyphImages; + auto* pending = asPath ? &fPendingGlyphPaths : &fPendingGlyphImages; + // Already cached. - if (fCachedGlyphs.contains(glyph)) return; + if (cache->contains(glyph)) return; // Serialize and cache. Also create the scalar context to use when serializing // this glyph. - fCachedGlyphs.add(glyph); - fPendingGlyphs.push_back(glyph); - if (!fContext) fContext = typeface->createScalerContext(effects, fDeviceDescriptor.get(), false); + cache->add(glyph); + pending->push_back(glyph); + if (!fContext) { + fContext = typeface->createScalerContext(effects, fDeviceDescriptor.get(), false); + } } void SkStrikeServer::SkGlyphCacheState::writePendingGlyphs(Serializer* serializer) { // Write the desc. - serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fPendingGlyphs.size(), - fDiscardableHandleId); + serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fDiscardableHandleId); serializer->writeDescriptor(*fKeyDescriptor.get()); // Write FontMetrics. + // TODO(khushalsagar): Do we need to re-send each time? SkPaint::FontMetrics fontMetrics; fContext->getFontMetrics(&fontMetrics); serializer->write<SkPaint::FontMetrics>(fontMetrics); - // Write Glyphs. - for (const auto& glyphID : fPendingGlyphs) { + // Write glyphs images. + serializer->emplace<size_t>(fPendingGlyphImages.size()); + for (const auto& glyphID : fPendingGlyphImages) { auto glyph = serializer->emplace<SkGlyph>(); glyph->initWithGlyphID(glyphID); fContext->getMetrics(glyph); - auto imageSize = glyph->computeImageSize(); glyph->fPathData = nullptr; glyph->fImage = nullptr; - if (imageSize > 0) { - // Since the allocateArray can move glyph, make one that stays in one place. - SkGlyph stationaryGlyph = *glyph; - stationaryGlyph.fImage = serializer->allocateArray<uint8_t>(imageSize); - fContext->getImage(stationaryGlyph); + bool tooLargeForAtlas = false; +#if SK_SUPPORT_GPU + tooLargeForAtlas = GrDrawOpAtlas::GlyphTooLargeForAtlas(glyph->fWidth, glyph->fHeight); +#endif + if (tooLargeForAtlas) { + // Add this to the path cache, since we will always fall back to using paths + // for this glyph. + fCachedGlyphPaths.add(glyphID); + writeGlyphPath(glyphID, serializer); + continue; } + + auto imageSize = glyph->computeImageSize(); + if (imageSize == 0u) continue; + + // Since the allocate can move glyph, make one that stays in one place. + SkGlyph stationaryGlyph = *glyph; + stationaryGlyph.fImage = serializer->allocate(imageSize, stationaryGlyph.formatAlignment()); + fContext->getImage(stationaryGlyph); } + fPendingGlyphImages.clear(); + + // Write glyphs paths. + serializer->emplace<size_t>(fPendingGlyphPaths.size()); + for (const auto& glyphID : fPendingGlyphPaths) { + auto glyph = serializer->emplace<SkGlyph>(); + glyph->initWithGlyphID(glyphID); + fContext->getMetrics(glyph); + glyph->fPathData = nullptr; + glyph->fImage = nullptr; + writeGlyphPath(glyphID, serializer); + } + fPendingGlyphPaths.clear(); // Note that we reset the context after serializing pending glyphs since we // don't want to extend the lifetime of the typeface. - fPendingGlyphs.clear(); fContext.reset(); } +void SkStrikeServer::SkGlyphCacheState::writeGlyphPath(const SkPackedGlyphID& glyphID, + Serializer* serializer) const { + SkPath path; + if (!fContext->getPath(glyphID, &path)) { + serializer->write<size_t>(0u); + return; + } + + size_t pathSize = path.writeToMemory(nullptr); + serializer->write<size_t>(pathSize); + path.writeToMemory(serializer->allocate(pathSize, kPathAlignment)); +} + // SkStrikeClient ----------------------------------------- class SkStrikeClient::DiscardableStrikePinner : public SkStrikePinner { @@ -656,20 +697,49 @@ bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySi fDiscardableHandleManager)); } - for (size_t j = 0; j < spec.glyphCount; j++) { + size_t glyphImagesCount = 0u; + if (!deserializer.read<size_t>(&glyphImagesCount)) READ_FAILURE + for (size_t j = 0; j < glyphImagesCount; j++) { SkGlyph glyph; if (!deserializer.read<SkGlyph>(&glyph)) READ_FAILURE SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph.getPackedID()); + // Don't override the path, if the glyph has one. + auto* glyphPath = allocatedGlyph->fPathData; *allocatedGlyph = glyph; + allocatedGlyph->fPathData = glyphPath; + + bool tooLargeForAtlas = false; +#if SK_SUPPORT_GPU + tooLargeForAtlas = GrDrawOpAtlas::GlyphTooLargeForAtlas(glyph.fWidth, glyph.fHeight); +#endif + if (tooLargeForAtlas) { + if (!read_path(&deserializer, allocatedGlyph, strike.get())) READ_FAILURE + continue; + } - ArraySlice<uint8_t> image; auto imageSize = glyph.computeImageSize(); - if (imageSize != 0) { - image = deserializer.readArray<uint8_t>(imageSize); - if (!image.data()) READ_FAILURE - strike->initializeImage(image.data(), image.size(), allocatedGlyph); - } + if (imageSize == 0u) continue; + + auto* image = deserializer.read(imageSize, allocatedGlyph->formatAlignment()); + if (!image || + !strike->initializeImage(image, imageSize, allocatedGlyph)) + READ_FAILURE + } + + size_t glyphPathsCount = 0u; + if (!deserializer.read<size_t>(&glyphPathsCount)) READ_FAILURE + for (size_t j = 0; j < glyphPathsCount; j++) { + SkGlyph glyph; + if (!deserializer.read<SkGlyph>(&glyph)) READ_FAILURE + + SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph.getPackedID()); + // Don't override the image, if the glyph has one. + auto* glyphImage = allocatedGlyph->fImage; + *allocatedGlyph = glyph; + allocatedGlyph->fImage = glyphImage; + + if (!read_path(&deserializer, allocatedGlyph, strike.get())) READ_FAILURE } } diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h index 36fe59306b..60d880791f 100644 --- a/src/core/SkRemoteGlyphCache.h +++ b/src/core/SkRemoteGlyphCache.h @@ -69,6 +69,7 @@ private: void processGlyphRun(const SkPoint& position, const SkTextBlobRunIterator& it, const SkPaint& runPaint); + void processGlyphRunForPaths(const SkTextBlobRunIterator& it, const SkPaint& runPaint); const SkMatrix fDeviceMatrix; const SkSurfaceProps fSurfaceProps; @@ -122,9 +123,11 @@ public: SkDiscardableHandleId discardableHandleId); ~SkGlyphCacheState(); - void addGlyph(SkTypeface*, const SkScalerContextEffects&, SkPackedGlyphID); + void addGlyph(SkTypeface*, const SkScalerContextEffects&, SkPackedGlyphID, bool pathOnly); void writePendingGlyphs(Serializer* serializer); - bool has_pending_glyphs() const { return !fPendingGlyphs.empty(); } + bool has_pending_glyphs() const { + return !fPendingGlyphImages.empty() || !fPendingGlyphPaths.empty(); + } SkDiscardableHandleId discardable_handle_id() const { return fDiscardableHandleId; } const SkDescriptor& getDeviceDescriptor() { return *fDeviceDescriptor; @@ -135,12 +138,16 @@ public: } private: + void writeGlyphPath(const SkPackedGlyphID& glyphID, Serializer* serializer) const; + // The set of glyphs cached on the remote client. - SkTHashSet<SkPackedGlyphID> fCachedGlyphs; + SkTHashSet<SkPackedGlyphID> fCachedGlyphImages; + SkTHashSet<SkPackedGlyphID> fCachedGlyphPaths; // The set of glyphs which has not yet been serialized and sent to the // remote client. - std::vector<SkPackedGlyphID> fPendingGlyphs; + std::vector<SkPackedGlyphID> fPendingGlyphImages; + std::vector<SkPackedGlyphID> fPendingGlyphPaths; // The device descriptor is used to create the scaler context. The glyphs to have the // correct device rendering. The key descriptor is used for communication. The GPU side will @@ -153,8 +160,9 @@ public: std::unique_ptr<SkScalerContext> fContext; }; - SkGlyphCacheState* getOrCreateCache( - SkTypeface*, std::unique_ptr<SkDescriptor>, std::unique_ptr<SkDescriptor>); + SkGlyphCacheState* getOrCreateCache(const SkPaint&, const SkSurfaceProps*, const SkMatrix*, + SkScalerContextRec* deviceRec, + SkScalerContextEffects* effects); private: SkDescriptorMap<std::unique_ptr<SkGlyphCacheState>> fRemoteGlyphStateMap; diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp index dbe36d1e6a..811bf1be50 100644 --- a/tests/SkRemoteGlyphCacheTest.cpp +++ b/tests/SkRemoteGlyphCacheTest.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkDraw.h" #include "SkGraphics.h" #include "SkMutex.h" #include "SkRemoteGlyphCache.h" @@ -71,9 +72,15 @@ sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) { return builder.make(); } -SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height) { +#define COMPARE_BLOBS(expected, actual, reporter) \ + for (int i = 0; i < expected.width(); ++i) { \ + for (int j = 0; j < expected.height(); ++j) { \ + REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j)); \ + } \ + } + +SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint) { auto surface = SkSurface::MakeRasterN32Premul(width, height); - SkPaint paint; surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint); SkBitmap bitmap; bitmap.allocN32Pixels(width, height); @@ -99,6 +106,7 @@ DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) { sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); SkStrikeServer server(discardableManager.get()); SkStrikeClient client(discardableManager); + const SkPaint paint; // Server. auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); @@ -108,7 +116,6 @@ DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) { auto serverBlob = buildTextBlob(serverTf, glyphCount); const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server); - SkPaint paint; cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); std::vector<uint8_t> serverStrikeData; @@ -120,13 +127,9 @@ DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) { client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); auto clientBlob = buildTextBlob(clientTf, glyphCount); - SkBitmap expected = RasterBlob(serverBlob, 10, 10); - SkBitmap actual = RasterBlob(clientBlob, 10, 10); - for (int i = 0; i < expected.width(); ++i) { - for (int j = 0; j < expected.height(); ++j) { - REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j)); - } - } + SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint); + SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint); + COMPARE_BLOBS(expected, actual, reporter); } DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) { @@ -246,3 +249,37 @@ DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) { client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); SkStrikeCache::Validate(); } + +DEF_TEST(SkRemoteGlyphCache_DrawTextAsPath, reporter) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(0); + REPORTER_ASSERT(reporter, SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())); + + // 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); + cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 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); + SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint); + COMPARE_BLOBS(expected, actual, reporter); + SkStrikeCache::Validate(); +} |