/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkImageFilterCache.h" #include #include "SkImageFilter.h" #include "SkMutex.h" #include "SkOnce.h" #include "SkOpts.h" #include "SkRefCnt.h" #include "SkSpecialImage.h" #include "SkTDynamicHash.h" #include "SkTHash.h" #include "SkTInternalLList.h" #ifdef SK_BUILD_FOR_IOS enum { kDefaultCacheSize = 2 * 1024 * 1024 }; #else enum { kDefaultCacheSize = 128 * 1024 * 1024 }; #endif namespace { class CacheImpl : public SkImageFilterCache { public: typedef SkImageFilterCacheKey Key; CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) { } ~CacheImpl() override { SkTDynamicHash::Iter iter(&fLookup); while (!iter.done()) { Value* v = &*iter; ++iter; delete v; } } struct Value { Value(const Key& key, SkSpecialImage* image, const SkIPoint& offset, const SkImageFilter* filter) : fKey(key), fImage(SkRef(image)), fOffset(offset), fFilter(filter) {} Key fKey; sk_sp fImage; SkIPoint fOffset; const SkImageFilter* fFilter; static const Key& GetKey(const Value& v) { return v.fKey; } static uint32_t Hash(const Key& key) { return SkOpts::hash(reinterpret_cast(&key), sizeof(Key)); } SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value); }; sk_sp get(const Key& key, SkIPoint* offset) const override { SkAutoMutexAcquire mutex(fMutex); if (Value* v = fLookup.find(key)) { *offset = v->fOffset; if (v != fLRU.head()) { fLRU.remove(v); fLRU.addToHead(v); } return v->fImage; } return nullptr; } void set(const Key& key, SkSpecialImage* image, const SkIPoint& offset, const SkImageFilter* filter) override { SkAutoMutexAcquire mutex(fMutex); if (Value* v = fLookup.find(key)) { this->removeInternal(v); } Value* v = new Value(key, image, offset, filter); fLookup.add(v); fLRU.addToHead(v); fCurrentBytes += image->getSize(); if (auto* values = fImageFilterValues.find(filter)) { values->push_back(v); } else { fImageFilterValues.set(filter, {v}); } while (fCurrentBytes > fMaxBytes) { Value* tail = fLRU.tail(); SkASSERT(tail); if (tail == v) { break; } this->removeInternal(tail); } } void purge() override { SkAutoMutexAcquire mutex(fMutex); while (fCurrentBytes > 0) { Value* tail = fLRU.tail(); SkASSERT(tail); this->removeInternal(tail); } } void purgeByImageFilter(const SkImageFilter* filter) override { SkAutoMutexAcquire mutex(fMutex); auto* values = fImageFilterValues.find(filter); if (!values) { return; } for (Value* v : *values) { // We set the filter to be null so that removeInternal() won't delete from values while // we're iterating over it. v->fFilter = nullptr; this->removeInternal(v); } fImageFilterValues.remove(filter); } SkDEBUGCODE(int count() const override { return fLookup.count(); }) private: void removeInternal(Value* v) { SkASSERT(v->fImage); if (v->fFilter) { if (auto* values = fImageFilterValues.find(v->fFilter)) { if (values->size() == 1 && (*values)[0] == v) { fImageFilterValues.remove(v->fFilter); } else { for (auto it = values->begin(); it != values->end(); ++it) { if (*it == v) { values->erase(it); break; } } } } } fCurrentBytes -= v->fImage->getSize(); fLRU.remove(v); fLookup.remove(v->fKey); delete v; } private: SkTDynamicHash fLookup; mutable SkTInternalLList fLRU; // Value* always points to an item in fLookup. SkTHashMap> fImageFilterValues; size_t fMaxBytes; size_t fCurrentBytes; mutable SkMutex fMutex; }; } // namespace SkImageFilterCache* SkImageFilterCache::Create(size_t maxBytes) { return new CacheImpl(maxBytes); } SkImageFilterCache* SkImageFilterCache::Get() { static SkOnce once; static SkImageFilterCache* cache; once([]{ cache = SkImageFilterCache::Create(kDefaultCacheSize); }); return cache; }