1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkImageFilterCache.h"
9
10#include "SkImageFilter.h"
11#include "SkMutex.h"
12#include "SkOnce.h"
13#include "SkOpts.h"
14#include "SkRefCnt.h"
15#include "SkSpecialImage.h"
16#include "SkTDynamicHash.h"
17#include "SkTInternalLList.h"
18
19#ifdef SK_BUILD_FOR_IOS
20  enum { kDefaultCacheSize = 2 * 1024 * 1024 };
21#else
22  enum { kDefaultCacheSize = 128 * 1024 * 1024 };
23#endif
24
25namespace {
26
27class CacheImpl : public SkImageFilterCache {
28public:
29    typedef SkImageFilterCacheKey Key;
30    CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) { }
31    ~CacheImpl() override {
32        SkTDynamicHash<Value, Key>::Iter iter(&fLookup);
33
34        while (!iter.done()) {
35            Value* v = &*iter;
36            ++iter;
37            delete v;
38        }
39    }
40    struct Value {
41        Value(const Key& key, SkSpecialImage* image, const SkIPoint& offset, const SkImageFilter* filter)
42            : fKey(key), fImage(SkRef(image)), fOffset(offset), fFilter(filter) {}
43
44        Key fKey;
45        sk_sp<SkSpecialImage> fImage;
46        SkIPoint fOffset;
47        const SkImageFilter* fFilter;
48        static const Key& GetKey(const Value& v) {
49            return v.fKey;
50        }
51        static uint32_t Hash(const Key& key) {
52            return SkOpts::hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
53        }
54        SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
55    };
56
57    sk_sp<SkSpecialImage> get(const Key& key, SkIPoint* offset) const override {
58        SkAutoMutexAcquire mutex(fMutex);
59        if (Value* v = fLookup.find(key)) {
60            *offset = v->fOffset;
61            if (v != fLRU.head()) {
62                fLRU.remove(v);
63                fLRU.addToHead(v);
64            }
65            return v->fImage;
66        }
67        return nullptr;
68    }
69
70    void set(const Key& key, SkSpecialImage* image, const SkIPoint& offset, const SkImageFilter* filter) override {
71        SkAutoMutexAcquire mutex(fMutex);
72        if (Value* v = fLookup.find(key)) {
73            this->removeInternal(v);
74        }
75        Value* v = new Value(key, image, offset, filter);
76        fLookup.add(v);
77        fLRU.addToHead(v);
78        fCurrentBytes += image->getSize();
79        while (fCurrentBytes > fMaxBytes) {
80            Value* tail = fLRU.tail();
81            SkASSERT(tail);
82            if (tail == v) {
83                break;
84            }
85            this->removeInternal(tail);
86        }
87    }
88
89    void purge() override {
90        SkAutoMutexAcquire mutex(fMutex);
91        while (fCurrentBytes > 0) {
92            Value* tail = fLRU.tail();
93            SkASSERT(tail);
94            this->removeInternal(tail);
95        }
96    }
97
98    void purgeByKeys(const Key keys[], int count) override {
99        SkAutoMutexAcquire mutex(fMutex);
100        // This function is only called in the destructor of SkImageFilter.
101        // Because the destructor will destroy the fCacheKeys anyway, we set the
102        // filter to be null so that removeInternal() won't call the
103        // SkImageFilter::removeKey() function.
104        for (int i = 0; i < count; i++) {
105            if (Value* v = fLookup.find(keys[i])) {
106                v->fFilter = nullptr;
107                this->removeInternal(v);
108            }
109        }
110    }
111
112    SkDEBUGCODE(int count() const override { return fLookup.count(); })
113private:
114    void removeInternal(Value* v) {
115        SkASSERT(v->fImage);
116        if (v->fFilter) {
117            v->fFilter->removeKey(v->fKey);
118        }
119        fCurrentBytes -= v->fImage->getSize();
120        fLRU.remove(v);
121        fLookup.remove(v->fKey);
122        delete v;
123    }
124private:
125    SkTDynamicHash<Value, Key>            fLookup;
126    mutable SkTInternalLList<Value>       fLRU;
127    size_t                                fMaxBytes;
128    size_t                                fCurrentBytes;
129    mutable SkMutex                       fMutex;
130};
131
132} // namespace
133
134SkImageFilterCache* SkImageFilterCache::Create(size_t maxBytes) {
135    return new CacheImpl(maxBytes);
136}
137
138SkImageFilterCache* SkImageFilterCache::Get() {
139    static SkOnce once;
140    static SkImageFilterCache* cache;
141
142    once([]{ cache = SkImageFilterCache::Create(kDefaultCacheSize); });
143    return cache;
144}
145