diff options
author | Khushal <khushalsagar@chromium.org> | 2018-06-12 11:26:17 -0700 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-06-12 20:42:34 +0000 |
commit | 8523b6bd0d22083266d990191764a0460885fd6e (patch) | |
tree | 15e9d39366e769bd79c9d243d01f8c2d2db983c1 | |
parent | 047fb122b94d5a569a5b64f4e65ecc8bf68cf09c (diff) |
fonts: Fix memory allocation for fallback glyphs.
When allocating the mask for a fallback glyph, we allocate it on the
arena on the SkScalerContext while the image belongs to a glyph on a
different cache. This can lead to use-after-free bugs if accessing the
image after the context owning that memory is destroyed. Fix this by
allocating on the arena from the owning cache.
R=herb@google.com, mtklein@google.com
Bug: 829622
Change-Id: Ife53e24f5bc868f36c43f2adcd7a2629ab5577fe
Reviewed-on: https://skia-review.googlesource.com/134182
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
-rw-r--r-- | src/core/SkGlyph.h | 9 | ||||
-rw-r--r-- | src/core/SkGlyphCache.cpp | 33 | ||||
-rw-r--r-- | src/core/SkGlyphCache.h | 10 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.cpp | 1 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.h | 18 | ||||
-rw-r--r-- | src/core/SkStrikeCache.cpp | 51 | ||||
-rw-r--r-- | src/core/SkStrikeCache.h | 9 | ||||
-rw-r--r-- | src/core/SkTypeface_remote.cpp | 47 | ||||
-rw-r--r-- | src/core/SkTypeface_remote.h | 10 | ||||
-rw-r--r-- | tests/SkRemoteGlyphCacheTest.cpp | 97 |
10 files changed, 167 insertions, 118 deletions
diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h index 748b87fab3..2b3adafd74 100644 --- a/src/core/SkGlyph.h +++ b/src/core/SkGlyph.h @@ -196,7 +196,9 @@ public: void toMask(SkMask* mask) const; - void copyImageData(const SkGlyph& from, SkArenaAlloc* alloc) { + /** Returns the size allocated on the arena. + */ + size_t copyImageData(const SkGlyph& from, SkArenaAlloc* alloc) { fMaskFormat = from.fMaskFormat; fWidth = from.fWidth; fHeight = from.fHeight; @@ -206,8 +208,13 @@ public: if (from.fImage != nullptr) { auto imageSize = this->allocImage(alloc); + SkASSERT(imageSize == from.computeImageSize()); + memcpy(fImage, from.fImage, imageSize); + return imageSize; } + + return 0u; } class HashTraits { diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp index 79e714758c..6b8bd7098a 100644 --- a/src/core/SkGlyphCache.cpp +++ b/src/core/SkGlyphCache.cpp @@ -154,13 +154,7 @@ SkGlyph* SkGlyphCache::lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, Metr glyph = this->allocateNewGlyph(packedGlyphID, type); } else { if (type == kFull_MetricsType && glyph->isJustAdvance()) { - fScalerContext->getMetrics(glyph); - - // Just in case someone allocated an image in the getMetrics call, be sure to account - // for the memory used. - if (glyph->fImage != nullptr) { - fMemoryUsed += glyph->computeImageSize(); - } + fScalerContext->getMetrics(glyph); } } return glyph; @@ -215,7 +209,7 @@ bool SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGly // check that alloc() actually succeeded if (glyph->fImage) { SkAssertResult(size == allocSize); - memcpy(glyph->fImage, const_cast<const void*>(data), size); + memcpy(glyph->fImage, const_cast<const void*>(data), allocSize); fMemoryUsed += size; } } @@ -261,6 +255,29 @@ bool SkGlyphCache::initializePath(SkGlyph* glyph, const volatile void* data, siz return true; } +bool SkGlyphCache::belongsToCache(const SkGlyph* glyph) const { + return glyph && fGlyphMap.find(glyph->getPackedID()) == glyph; +} + +const SkGlyph* SkGlyphCache::getCachedGlyphAnySubPix(SkGlyphID glyphID, + SkPackedGlyphID vetoID) const { + for (SkFixed subY = 0; subY < SK_Fixed1; subY += SK_FixedQuarter) { + for (SkFixed subX = 0; subX < SK_Fixed1; subX += SK_FixedQuarter) { + SkPackedGlyphID packedGlyphID{glyphID, subX, subY}; + if (packedGlyphID == vetoID) continue; + if (const auto* glyph = fGlyphMap.find(packedGlyphID)) { + return glyph; + } + } + } + + return nullptr; +} + +void SkGlyphCache::initializeGlyphFromFallback(SkGlyph* glyph, const SkGlyph& fallback) { + fMemoryUsed += glyph->copyImageData(fallback, &fAlloc); +} + #include "../pathops/SkPathOpsCubic.h" #include "../pathops/SkPathOpsQuad.h" diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h index e904c6e5c7..47a1d3d4d2 100644 --- a/src/core/SkGlyphCache.h +++ b/src/core/SkGlyphCache.h @@ -112,6 +112,16 @@ public: */ bool initializePath(SkGlyph*, const volatile void* data, size_t size); + /** Fallback glyphs used during font remoting if the original glyph can't be found. + */ + bool belongsToCache(const SkGlyph* glyph) const; + /** Find any glyph in this cache with the given ID, regardless of subpixel positioning. + * If set and present, skip over the glyph with vetoID. + */ + const SkGlyph* getCachedGlyphAnySubPix(SkGlyphID, + SkPackedGlyphID vetoID = SkPackedGlyphID()) const; + void initializeGlyphFromFallback(SkGlyph* glyph, const SkGlyph&); + /** 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 49688886c1..0ccbf2f15e 100644 --- a/src/core/SkRemoteGlyphCache.cpp +++ b/src/core/SkRemoteGlyphCache.cpp @@ -801,6 +801,7 @@ bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySi *client_desc, std::move(scaler), &fontMetrics, skstd::make_unique<DiscardableStrikePinner>(spec.discardableHandleId, fDiscardableHandleManager)); + static_cast<SkScalerContextProxy*>(strike->getScalerContext())->initCache(strike.get()); } size_t glyphImagesCount = 0u; diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h index ce318489e4..ac13920772 100644 --- a/src/core/SkRemoteGlyphCache.h +++ b/src/core/SkRemoteGlyphCache.h @@ -184,12 +184,20 @@ private: class SK_API SkStrikeClient { public: + // This enum is used in histogram reporting in chromium. Please don't re-order the list of + // entries, and consider it to be append-only. enum CacheMissType : uint32_t { - kFontMetrics, - kGlyphMetrics, - kGlyphImage, - kGlyphPath, - kLast = kGlyphPath + // Hard failures where no fallback could be found. + kFontMetrics = 0, + kGlyphMetrics = 1, + kGlyphImage = 2, + kGlyphPath = 3, + + // The original glyph could not be found and a fallback was used. + kGlyphMetricsFallback = 4, + kGlyphPathFallback = 5, + + kLast = kGlyphPathFallback }; // An interface to delete handles that may be pinned by the remote server. diff --git a/src/core/SkStrikeCache.cpp b/src/core/SkStrikeCache.cpp index 20ed6e772c..fef46fd36d 100644 --- a/src/core/SkStrikeCache.cpp +++ b/src/core/SkStrikeCache.cpp @@ -101,9 +101,9 @@ SkExclusiveStrikePtr SkStrikeCache::FindStrikeExclusive(const SkDescriptor& desc return get_globals().findStrikeExclusive(desc); } -bool SkStrikeCache::DesperationSearchForImage( - const SkDescriptor& desc, SkGlyph* glyph, SkArenaAlloc* arena) { - return get_globals().desperationSearchForImage(desc, glyph, arena); +bool SkStrikeCache::DesperationSearchForImage(const SkDescriptor& desc, SkGlyph* glyph, + SkGlyphCache* targetCache) { + return get_globals().desperationSearchForImage(desc, glyph, targetCache); } bool SkStrikeCache::DesperationSearchForPath( @@ -265,7 +265,7 @@ SkExclusiveStrikePtr SkStrikeCache::findStrikeExclusive(const SkDescriptor& desc return SkExclusiveStrikePtr(nullptr); } -static bool loose_compare (const SkDescriptor& lhs, const SkDescriptor& rhs) { +static bool loose_compare(const SkDescriptor& lhs, const SkDescriptor& rhs) { uint32_t size; auto ptr = lhs.findEntry(kRec_SkDescriptorTag, &size); SkScalerContextRec lhsRec; @@ -276,7 +276,10 @@ static bool loose_compare (const SkDescriptor& lhs, const SkDescriptor& rhs) { std::memcpy(&rhsRec, ptr, size); // If these don't match, there's no way we can use these strikes interchangeably. - // TODO: make sure we don't search other renderer's caches. + // Note that a typeface from each renderer maps to a unique proxy typeface on the GPU, + // keyed in the glyph cache using fontID in the SkDescriptor. By limiting this search + // to descriptors with the same fontID, we ensure that a renderer never uses glyphs + // generated by a different renderer. return lhsRec.fFontID == rhsRec.fFontID && lhsRec.fTextSize == rhsRec.fTextSize && @@ -288,46 +291,34 @@ static bool loose_compare (const SkDescriptor& lhs, const SkDescriptor& rhs) { lhsRec.fPost2x2[1][1] == rhsRec.fPost2x2[1][1]; } -bool SkStrikeCache::desperationSearchForImage( - const SkDescriptor& desc, SkGlyph* glyph, SkArenaAlloc* alloc) { +bool SkStrikeCache::desperationSearchForImage(const SkDescriptor& desc, SkGlyph* glyph, + SkGlyphCache* targetCache) { SkAutoExclusive ac(fLock); SkGlyphID glyphID = glyph->getGlyphID(); SkFixed targetSubX = glyph->getSubXFixed(), targetSubY = glyph->getSubYFixed(); - // We don't have this glyph with the exact subpixel positioning, - // but we might have this glyph with another subpixel position... search them all. for (Node* node = internalGetHead(); node != nullptr; node = node->fNext) { if (loose_compare(node->fCache.getDescriptor(), desc)) { auto targetGlyphID = SkPackedGlyphID(glyphID, targetSubX, targetSubY); if (node->fCache.isGlyphCached(glyphID, targetSubX, targetSubY)) { - SkGlyph* from = node->fCache.getRawGlyphByID(targetGlyphID); - if (from->fImage != nullptr) { - // This desperate-match node may disappear as soon as we drop fLock, so we - // need to copy the glyph from node into this strike, including a - // deep copy of the mask. - glyph->copyImageData(*from, alloc); - return true; - } + SkGlyph* fallback = node->fCache.getRawGlyphByID(targetGlyphID); + // This desperate-match node may disappear as soon as we drop fLock, so we + // need to copy the glyph from node into this strike, including a + // deep copy of the mask. + targetCache->initializeGlyphFromFallback(glyph, *fallback); + return true; } - // We don't have this glyph with the exact subpixel positioning, - // but we might have this glyph with another subpixel position... search them all. - for (SkFixed subY = 0; subY < SK_Fixed1; subY += SK_FixedQuarter) { - for (SkFixed subX = 0; subX < SK_Fixed1; subX += SK_FixedQuarter) { - if (node->fCache.isGlyphCached(glyphID, subX, subY)) { - SkGlyph* from = - node->fCache.getRawGlyphByID(SkPackedGlyphID(glyphID, subX, subY)); - if (from->fImage != nullptr) { - glyph->copyImageData(*from, alloc); - return true; - } - } - } + // Look for any sub-pixel pos for this glyph, in case there is a pos mismatch. + if (const auto* fallback = node->fCache.getCachedGlyphAnySubPix(glyphID)) { + targetCache->initializeGlyphFromFallback(glyph, *fallback); + return true; } } } + return false; } diff --git a/src/core/SkStrikeCache.h b/src/core/SkStrikeCache.h index da984cce13..05e3aa4025 100644 --- a/src/core/SkStrikeCache.h +++ b/src/core/SkStrikeCache.h @@ -70,8 +70,9 @@ public: static ExclusiveStrikePtr FindStrikeExclusive(const SkDescriptor&); - static bool DesperationSearchForImage( - const SkDescriptor& desc, SkGlyph* glyph, SkArenaAlloc* arena); + static bool DesperationSearchForImage(const SkDescriptor& desc, + SkGlyph* glyph, + SkGlyphCache* targetCache); static bool DesperationSearchForPath( const SkDescriptor& desc, SkGlyphID glyphID, SkPath* path); @@ -112,7 +113,9 @@ public: // Routines to find suitable data when working in a remote cache situation. These are // suitable as substitutes for similar calls in SkScalerContext. - bool desperationSearchForImage(const SkDescriptor& desc, SkGlyph* glyph, SkArenaAlloc* alloc); + bool desperationSearchForImage(const SkDescriptor& desc, + SkGlyph* glyph, + SkGlyphCache* targetCache); bool desperationSearchForPath(const SkDescriptor& desc, SkGlyphID glyphID, SkPath* path); void purgeAll(); // does not change budget diff --git a/src/core/SkTypeface_remote.cpp b/src/core/SkTypeface_remote.cpp index 9388b16ef4..4b10f1e96f 100644 --- a/src/core/SkTypeface_remote.cpp +++ b/src/core/SkTypeface_remote.cpp @@ -6,6 +6,7 @@ */ #include "SkTypeface_remote.h" +#include "SkGlyphCache.h" #include "SkPaint.h" #include "SkRemoteGlyphCache.h" #include "SkStrikeCache.h" @@ -18,6 +19,13 @@ SkScalerContextProxy::SkScalerContextProxy(sk_sp<SkTypeface> tf, : SkScalerContext{std::move(tf), effects, desc} , fDiscardableManager{std::move(manager)} {} +void SkScalerContextProxy::initCache(SkGlyphCache* cache) { + SkASSERT(fCache == nullptr); + SkASSERT(cache != nullptr); + + fCache = cache; +} + unsigned SkScalerContextProxy::generateGlyphCount() { SK_ABORT("Should never be called."); return 0; @@ -28,9 +36,7 @@ uint16_t SkScalerContextProxy::generateCharToGlyph(SkUnichar) { return 0; } -void SkScalerContextProxy::generateAdvance(SkGlyph* glyph) { - this->generateMetrics(glyph); -} +void SkScalerContextProxy::generateAdvance(SkGlyph* glyph) { this->generateMetrics(glyph); } void SkScalerContextProxy::generateMetrics(SkGlyph* glyph) { TRACE_EVENT1("skia", "generateMetrics", "rec", TRACE_STR_COPY(this->getRec().dump().c_str())); @@ -38,14 +44,27 @@ void SkScalerContextProxy::generateMetrics(SkGlyph* glyph) { SkDebugf("GlyphCacheMiss generateMetrics: %s\n", this->getRec().dump().c_str()); } - // Since the scaler context is being called, we don't have the needed data. Go look through - // looking for a suitable substitute before failing. - auto desc = SkScalerContext::DescriptorGivenRecAndEffects(this->getRec(), this->getEffects()); - SkStrikeCache::DesperationSearchForImage(*desc, glyph, &fAlloc); + // Since the scaler context is being called, we don't have the needed data. Try to find a + // fallback before failing. + if (fCache && fCache->belongsToCache(glyph)) { + // First check the original cache, in case there is a sub-pixel pos mismatch. + if (const auto* fallback = + fCache->getCachedGlyphAnySubPix(glyph->getGlyphID(), glyph->getPackedID())) { + fCache->initializeGlyphFromFallback(glyph, *fallback); + fDiscardableManager->notifyCacheMiss( + SkStrikeClient::CacheMissType::kGlyphMetricsFallback); + return; + } - if (glyph->fMaskFormat == MASK_FORMAT_UNKNOWN) { - glyph->zeroMetrics(); + // Now check other caches for a desc mismatch. + if (SkStrikeCache::DesperationSearchForImage(fCache->getDescriptor(), glyph, fCache)) { + fDiscardableManager->notifyCacheMiss( + SkStrikeClient::CacheMissType::kGlyphMetricsFallback); + return; + } } + + glyph->zeroMetrics(); fDiscardableManager->notifyCacheMiss(SkStrikeClient::CacheMissType::kGlyphMetrics); } @@ -65,12 +84,14 @@ bool SkScalerContextProxy::generatePath(SkGlyphID glyphID, SkPath* path) { if (this->getProxyTypeface()->isLogging()) { SkDebugf("GlyphCacheMiss generatePath: %s\n", this->getRec().dump().c_str()); } - // Since the scaler context is being called, we don't have the needed data. Go look through - // looking for a suitable substitute before failing. + + // Since the scaler context is being called, we don't have the needed data. Try to find a + // fallback before failing. auto desc = SkScalerContext::DescriptorGivenRecAndEffects(this->getRec(), this->getEffects()); bool foundPath = SkStrikeCache::DesperationSearchForPath(*desc, glyphID, path); - - fDiscardableManager->notifyCacheMiss(SkStrikeClient::CacheMissType::kGlyphPath); + fDiscardableManager->notifyCacheMiss(foundPath + ? SkStrikeClient::CacheMissType::kGlyphPathFallback + : SkStrikeClient::CacheMissType::kGlyphPath); return foundPath; } diff --git a/src/core/SkTypeface_remote.h b/src/core/SkTypeface_remote.h index 1e079eff49..88628d13b4 100644 --- a/src/core/SkTypeface_remote.h +++ b/src/core/SkTypeface_remote.h @@ -26,6 +26,8 @@ public: const SkDescriptor* desc, sk_sp<SkStrikeClient::DiscardableHandleManager> manager); + void initCache(SkGlyphCache*); + protected: unsigned generateGlyphCount() override; uint16_t generateCharToGlyph(SkUnichar) override; @@ -37,14 +39,8 @@ protected: SkTypefaceProxy* getProxyTypeface() const; private: - // Copied from SkGlyphCache - // so we don't grow our arrays a lot - static constexpr size_t kMinGlyphCount = 8; - static constexpr size_t kMinGlyphImageSize = 16 /* height */ * 8 /* width */; - static constexpr size_t kMinAllocAmount = kMinGlyphImageSize * kMinGlyphCount; - - SkArenaAlloc fAlloc{kMinAllocAmount}; sk_sp<SkStrikeClient::DiscardableHandleManager> fDiscardableManager; + SkGlyphCache* fCache = nullptr; typedef SkScalerContext INHERITED; }; diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp index 359c506950..62036d2285 100644 --- a/tests/SkRemoteGlyphCacheTest.cpp +++ b/tests/SkRemoteGlyphCacheTest.cpp @@ -46,7 +46,7 @@ public: } const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; } SkDiscardableHandleId handleCount() { return fNextHandleId; } - int cacheMissCount(SkStrikeClient::CacheMissType type) { return fCacheMissCount[type]; } + int cacheMissCount(uint32_t type) { return fCacheMissCount[type]; } bool hasCacheMiss() const { for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) { if (fCacheMissCount[i] > 0) return true; @@ -410,14 +410,22 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, report } DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation, reporter) { + // Build proxy typeface on the client for initializing the cache. + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager, false); + + auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + auto tfData = server.serializeTypeface(serverTf.get()); + auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size()); + REPORTER_ASSERT(reporter, clientTf); + SkPaint paint; paint.setAntiAlias(true); - auto typeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); - paint.setTypeface(typeface); + paint.setTypeface(clientTf); paint.setColor(SK_ColorRED); auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf); - const uint8_t glyphImage[] = {0xFF, 0xFF}; // Build a fallback cache. @@ -429,7 +437,7 @@ DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation, reporter) { SkScalerContext::MakeRecAndEffects(paint, nullptr, nullptr, flags, &rec, &effects, false); auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); - auto fallbackCache = SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *typeface); + auto fallbackCache = SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *clientTf); auto glyph = fallbackCache->getRawGlyphByID(lostGlyphID); glyph->fMaskFormat = SkMask::kA8_Format; glyph->fHeight = 1; @@ -450,67 +458,54 @@ DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation, reporter) { REPORTER_ASSERT(reporter, !(testCache == nullptr)); } - // Make sure we can't find the target cache. + // Create the target cache. + SkExclusiveStrikePtr testCache; + SkAutoDescriptor ad; + SkScalerContextRec rec; + SkScalerContextEffects effects; + SkScalerContextFlags flags = SkScalerContextFlags::kNone; + SkScalerContext::MakeRecAndEffects(paint, nullptr, nullptr, flags, &rec, &effects, false); + auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); + testCache = SkStrikeCache::FindStrikeExclusive(*desc); + REPORTER_ASSERT(reporter, testCache == nullptr); + testCache = SkStrikeCache::CreateStrikeExclusive(*desc, + clientTf->createScalerContext(effects, desc)); + static_cast<SkScalerContextProxy*>(testCache->getScalerContext())->initCache(testCache.get()); + + // Look for the lost glyph. { - SkAutoDescriptor ad; - SkScalerContextRec rec; - SkScalerContextEffects effects; - SkScalerContextFlags flags = SkScalerContextFlags::kNone; - SkScalerContext::MakeRecAndEffects(paint, nullptr, nullptr, flags, &rec, &effects, false); - auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); - auto testCache = SkStrikeCache::FindStrikeExclusive(*desc); - REPORTER_ASSERT(reporter, testCache == nullptr); - } + const auto& lostGlyph = testCache->getGlyphIDMetrics( + lostGlyphID.code(), lostGlyphID.getSubXFixed(), lostGlyphID.getSubYFixed()); + testCache->findImage(lostGlyph); - { - SkGlyph lostGlyph; - lostGlyph.initWithGlyphID(lostGlyphID); - - SkAutoDescriptor ad; - SkScalerContextRec rec; - SkScalerContextEffects effects; - SkScalerContextFlags flags = SkScalerContextFlags::kNone; - SkScalerContext::MakeRecAndEffects(paint, nullptr, nullptr, flags, &rec, &effects, false); - auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); - SkArenaAlloc alloc{100}; - auto found = SkStrikeCache::DesperationSearchForImage(*desc, &lostGlyph, &alloc); - REPORTER_ASSERT(reporter, found); REPORTER_ASSERT(reporter, lostGlyph.fHeight == 1); REPORTER_ASSERT(reporter, lostGlyph.fWidth == 2); REPORTER_ASSERT(reporter, lostGlyph.fMaskFormat == SkMask::kA8_Format); + REPORTER_ASSERT(reporter, memcmp(lostGlyph.fImage, glyphImage, sizeof(glyphImage)) == 0); } + // Look for the lost glyph with a different sub-pix position. { - SkGlyph lostGlyph; - auto reallyLostGlyphID = SkPackedGlyphID(1, SK_FixedQuarter, SK_FixedQuarter); - lostGlyph.initWithGlyphID(reallyLostGlyphID); + const auto& lostGlyph = + testCache->getGlyphIDMetrics(lostGlyphID.code(), SK_FixedQuarter, SK_FixedQuarter); + testCache->findImage(lostGlyph); - SkAutoDescriptor ad; - SkScalerContextRec rec; - SkScalerContextEffects effects; - SkScalerContextFlags flags = SkScalerContextFlags::kNone; - SkScalerContext::MakeRecAndEffects(paint, nullptr, nullptr, flags, &rec, &effects, false); - auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); - SkArenaAlloc alloc{100}; - auto found = SkStrikeCache::DesperationSearchForImage(*desc, &lostGlyph, &alloc); - REPORTER_ASSERT(reporter, found); REPORTER_ASSERT(reporter, lostGlyph.fHeight == 1); REPORTER_ASSERT(reporter, lostGlyph.fWidth == 2); REPORTER_ASSERT(reporter, lostGlyph.fMaskFormat == SkMask::kA8_Format); + REPORTER_ASSERT(reporter, memcmp(lostGlyph.fImage, glyphImage, sizeof(glyphImage)) == 0); } - // Make sure we can't find the target cache again. - { - SkAutoDescriptor ad; - SkScalerContextRec rec; - SkScalerContextEffects effects; - SkScalerContextFlags flags = SkScalerContextFlags::kNone; - SkScalerContext::MakeRecAndEffects(paint, nullptr, nullptr, flags, &rec, &effects, false); - auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); - auto testCache = SkStrikeCache::FindStrikeExclusive(*desc); - REPORTER_ASSERT(reporter, testCache == nullptr); + for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) { + if (i == SkStrikeClient::CacheMissType::kGlyphMetricsFallback || + i == SkStrikeClient::CacheMissType::kFontMetrics) { + REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 2); + } else { + REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 0); + } } - SkStrikeCache::Validate(); + // Must unlock everything on termination, otherwise valgrind complains about memory leaks. + discardableManager->unlockAndDeleteAll(); } |