From 51371a43397424ee4d3057fd5a82ec7c01eff705 Mon Sep 17 00:00:00 2001 From: Khushal Date: Thu, 17 May 2018 10:41:40 -0700 Subject: fonts: Handle fallback to using paths for text rendering for remoting. SkRemoteGlyphCache only sends images for glyphs, even for cases where the gpu falls back to drawing text as paths. This includes cases in SkDraw::ShouldDrawTextAsPaths and when the glyph exceeds the max bounds that can fit on the atlas. Fix this by identifying these cases in the renderer and sending paths instead. Note: We still don't handle distance field text correctly. R=herb@google.com, bsalomon@google.com Bug: skia:7913 Change-Id: I17d4eccbeaa2e995ae67b61c76cebd27f8280329 Reviewed-on: https://skia-review.googlesource.com/128203 Reviewed-by: Herb Derby Commit-Queue: Khusal Sagar --- include/core/SkPaint.h | 1 + src/core/SkGlyph.cpp | 5 + src/core/SkGlyph.h | 1 + src/core/SkGlyphCache.cpp | 25 ++- src/core/SkGlyphCache.h | 10 +- src/core/SkRemoteGlyphCache.cpp | 324 ++++++++++++++++++++++++--------------- src/core/SkRemoteGlyphCache.h | 20 ++- 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(fMaskFormat); + return format_alignment(format); +} + size_t SkGlyph::allocImage(SkArenaAlloc* alloc) { auto size = this->computeImageSize(); auto format = static_cast(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(); + glyph->fPathData = pathData; + pathData->fIntercept = nullptr; + SkPath* path = new SkPath; + if (!path->readFromMemory(const_cast(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 #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 -class ArraySlice final : public std::tuple { -public: - // Additional constructors as needed. - ArraySlice(const T* data, size_t size) : fData{data}, fSize{size} { } - ArraySlice() : ArraySlice(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 - 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* fBuffer; }; @@ -157,14 +130,8 @@ public: return true; } - template - ArraySlice readArray(int count) { - size_t size = count * sizeof(T); - const T* base = (const T*)this->ensureAtLeast(size, alignof(T)); - if (!base) return ArraySlice(); - - ArraySlice result = ArraySlice{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(&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(fStrikeServer) - ->getOrCreateCache( - runPaint.getTypeface(), - std::move(deviceDescriptor), - std::move(keyDescriptor)); + auto* glyphCacheState = static_cast(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(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* memory) { } SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( - SkTypeface* tf, - std::unique_ptr deviceDesc, - std::unique_ptr 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( - std::move(deviceDesc), - std::move(keyDesc), - newHandle); + auto cacheState = skstd::make_unique(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 deviceDescriptor, - std::unique_ptr keyDescriptor, - uint32_t discardable_handle_id) +SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(std::unique_ptr deviceDescriptor, + std::unique_ptr + 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(fContext->getTypeface()->uniqueID(), fPendingGlyphs.size(), - fDiscardableHandleId); + serializer->emplace(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(fontMetrics); - // Write Glyphs. - for (const auto& glyphID : fPendingGlyphs) { + // Write glyphs images. + serializer->emplace(fPendingGlyphImages.size()); + for (const auto& glyphID : fPendingGlyphImages) { auto glyph = serializer->emplace(); 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(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(fPendingGlyphPaths.size()); + for (const auto& glyphID : fPendingGlyphPaths) { + auto glyph = serializer->emplace(); + 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(0u); + return; + } + + size_t pathSize = path.writeToMemory(nullptr); + serializer->write(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(&glyphImagesCount)) READ_FAILURE + for (size_t j = 0; j < glyphImagesCount; j++) { SkGlyph glyph; if (!deserializer.read(&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 image; auto imageSize = glyph.computeImageSize(); - if (imageSize != 0) { - image = deserializer.readArray(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(&glyphPathsCount)) READ_FAILURE + for (size_t j = 0; j < glyphPathsCount; j++) { + SkGlyph glyph; + if (!deserializer.read(&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 fCachedGlyphs; + SkTHashSet fCachedGlyphImages; + SkTHashSet fCachedGlyphPaths; // The set of glyphs which has not yet been serialized and sent to the // remote client. - std::vector fPendingGlyphs; + std::vector fPendingGlyphImages; + std::vector 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 fContext; }; - SkGlyphCacheState* getOrCreateCache( - SkTypeface*, std::unique_ptr, std::unique_ptr); + SkGlyphCacheState* getOrCreateCache(const SkPaint&, const SkSurfaceProps*, const SkMatrix*, + SkScalerContextRec* deviceRec, + SkScalerContextEffects* effects); private: SkDescriptorMap> 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 buildTextBlob(sk_sp tf, int glyphCount) { return builder.make(); } -SkBitmap RasterBlob(sk_sp 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 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 = sk_make_sp(); 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 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 = sk_make_sp(); + 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 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(); +} -- cgit v1.2.3