blob: 291bae76add1126bac5b4eb34806a63e635de4a5 [file] [log] [blame]
/*
* 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 <vector>
#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<Value, Key>::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<SkSpecialImage> 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<const uint32_t*>(&key), sizeof(Key));
}
SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
};
sk_sp<SkSpecialImage> 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<Value, Key> fLookup;
mutable SkTInternalLList<Value> fLRU;
// Value* always points to an item in fLookup.
SkTHashMap<const SkImageFilter*, std::vector<Value*>> 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;
}