Add SkMaskCache

BUG=skia:

Review URL: https://codereview.chromium.org/670063004
diff --git a/src/core/SkMaskCache.cpp b/src/core/SkMaskCache.cpp
new file mode 100644
index 0000000..4520e97
--- /dev/null
+++ b/src/core/SkMaskCache.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMaskCache.h"
+
+#define CHECK_LOCAL(localCache, localName, globalName, ...) \
+    ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
+
+struct MaskValue {
+    SkMask          fMask;
+    SkCachedData*   fData;
+};
+
+namespace {
+static unsigned gRRectBlurKeyNamespaceLabel;
+
+struct RRectBlurKey : public SkResourceCache::Key {
+public:
+    RRectBlurKey(SkScalar sigma, const SkRRect& rrect, SkBlurStyle style, SkBlurQuality quality)
+        : fSigma(sigma)
+        , fRRect(rrect)
+        , fStyle(style)
+        , fQuality(quality) {
+        this->init(&gRRectBlurKeyNamespaceLabel,
+                   sizeof(fSigma) + sizeof(fRRect) + sizeof(fStyle) + sizeof(fQuality));
+    }
+
+    SkScalar   fSigma;
+    SkRRect    fRRect;
+    int32_t    fStyle;
+    int32_t    fQuality;
+};
+
+struct RRectBlurRec : public SkResourceCache::Rec {
+    RRectBlurRec(RRectBlurKey key, const SkMask& mask, SkCachedData* data)
+        : fKey(key)
+    {
+        fValue.fMask = mask;
+        fValue.fData = data;
+        fValue.fData->attachToCacheAndRef();
+    }
+    ~RRectBlurRec() {
+        fValue.fData->detachFromCacheAndUnref();
+    }
+
+    RRectBlurKey   fKey;
+    MaskValue      fValue;
+
+    virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
+    virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(*this) + fValue.fData->size(); }
+
+    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) {
+        const RRectBlurRec& rec = static_cast<const RRectBlurRec&>(baseRec);
+        MaskValue* result = (MaskValue*)contextData;
+
+        SkCachedData* tmpData = rec.fValue.fData;
+        tmpData->ref();
+        if (NULL == tmpData->data()) {
+            tmpData->unref();
+            return false;
+        }
+        *result = rec.fValue;
+        return true;
+    }
+};
+} // namespace
+
+SkCachedData* SkMaskCache::FindAndRef(SkScalar sigma, const SkRRect& rrect, SkBlurStyle style,
+                                      SkBlurQuality quality, SkMask* mask,
+                                      SkResourceCache* localCache) {
+    MaskValue result;
+    RRectBlurKey key(sigma, rrect, style, quality);
+    if (!CHECK_LOCAL(localCache, find, Find, key, RRectBlurRec::Visitor, &result)) {
+        return NULL;
+    }
+
+    *mask = result.fMask;
+    mask->fImage = (uint8_t*)(result.fData->data());
+    return result.fData;
+}
+
+void SkMaskCache::Add(SkScalar sigma, const SkRRect& rrect, SkBlurStyle style,
+                      SkBlurQuality quality, const SkMask& mask, SkCachedData* data,
+                      SkResourceCache* localCache) {
+    RRectBlurKey key(sigma, rrect, style, quality);
+    return CHECK_LOCAL(localCache, add, Add, SkNEW_ARGS(RRectBlurRec, (key, mask, data)));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+static unsigned gRectsBlurKeyNamespaceLabel;
+
+struct RectsBlurKey : public SkResourceCache::Key {
+public:
+    RectsBlurKey(SkScalar sigma, int count, const SkRect rects[], SkBlurStyle style)
+        : fSigma(sigma)
+        , fRecCount(count)
+        , fStyle(style){
+        SkASSERT(1 == count || 2 == count);
+        fRects[0] = SkRect::MakeEmpty();
+        fRects[1] = SkRect::MakeEmpty();
+        for (int i = 0; i < count; i++) {
+            fRects[i] = rects[i];
+        }
+        this->init(&gRectsBlurKeyNamespaceLabel,
+                   sizeof(fSigma) + sizeof(fRecCount) + sizeof(fRects) + sizeof(fStyle));
+    }
+
+    SkScalar    fSigma;
+    int         fRecCount;
+    SkRect      fRects[2];
+    int32_t     fStyle;
+};
+
+struct RectsBlurRec : public SkResourceCache::Rec {
+    RectsBlurRec(RectsBlurKey key, const SkMask& mask, SkCachedData* data)
+        : fKey(key)
+    {
+        fValue.fMask = mask;
+        fValue.fData = data;
+        fValue.fData->attachToCacheAndRef();
+    }
+    ~RectsBlurRec() {
+        fValue.fData->detachFromCacheAndUnref();
+    }
+
+    RectsBlurKey   fKey;
+    MaskValue      fValue;
+
+    virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
+    virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(*this) + fValue.fData->size(); }
+
+    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) {
+        const RectsBlurRec& rec = static_cast<const RectsBlurRec&>(baseRec);
+        MaskValue* result = (MaskValue*)contextData;
+
+        SkCachedData* tmpData = rec.fValue.fData;
+        tmpData->ref();
+        if (NULL == tmpData->data()) {
+            tmpData->unref();
+            return false;
+        }
+        *result = rec.fValue;
+        return true;
+    }
+};
+} // namespace
+
+SkCachedData* SkMaskCache::FindAndRef(SkScalar sigma, const SkRect rects[], int count,
+                                      SkBlurStyle style, SkMask* mask,
+                                      SkResourceCache* localCache) {
+    MaskValue result;
+    RectsBlurKey key(sigma, count, rects, style);
+    if (!CHECK_LOCAL(localCache, find, Find, key, RectsBlurRec::Visitor, &result)) {
+        return NULL;
+    }
+
+    *mask = result.fMask;
+    mask->fImage = (uint8_t*)(result.fData->data());
+    return result.fData;
+}
+
+void SkMaskCache::Add(SkScalar sigma, const SkRect rects[], int count, SkBlurStyle style,
+                      const SkMask& mask, SkCachedData* data,
+                      SkResourceCache* localCache) {
+    RectsBlurKey key(sigma, count, rects, style);
+    return CHECK_LOCAL(localCache, add, Add, SkNEW_ARGS(RectsBlurRec, (key, mask, data)));
+}
diff --git a/src/core/SkMaskCache.h b/src/core/SkMaskCache.h
new file mode 100644
index 0000000..f44ed38
--- /dev/null
+++ b/src/core/SkMaskCache.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMaskCache_DEFINED
+#define SkMaskCache_DEFINED
+
+#include "SkBlurTypes.h"
+#include "SkCachedData.h"
+#include "SkMask.h"
+#include "SkRect.h"
+#include "SkResourceCache.h"
+#include "SkRRect.h"
+
+class SkMaskCache {
+public:
+    /**
+     * On success, return a ref to the SkCachedData that holds the pixels, and have mask
+     * already point to that memory.
+     *
+     * On failure, return NULL.
+     */
+    static SkCachedData* FindAndRef(SkScalar sigma, const SkRRect& rrect, SkBlurStyle style,
+                                    SkBlurQuality quality, SkMask* mask,
+                                    SkResourceCache* localCache = NULL);
+    static SkCachedData* FindAndRef(SkScalar sigma, const SkRect rects[], int count,
+                                    SkBlurStyle style,SkMask* mask,
+                                    SkResourceCache* localCache = NULL);
+
+    /**
+     * Add a mask and its pixel-data to the cache.
+     */
+    static void Add(SkScalar sigma, const SkRRect& rrect, SkBlurStyle style, SkBlurQuality quality,
+                    const SkMask& mask, SkCachedData* data, SkResourceCache* localCache = NULL);
+    static void Add(SkScalar sigma, const SkRect rects[], int count, SkBlurStyle style,
+                    const SkMask& mask, SkCachedData* data, SkResourceCache* localCache = NULL);
+};
+
+#endif
diff --git a/src/core/SkResourceCache.cpp b/src/core/SkResourceCache.cpp
index 1eb53cd..ae8412d 100644
--- a/src/core/SkResourceCache.cpp
+++ b/src/core/SkResourceCache.cpp
@@ -303,6 +303,15 @@
     return prevLimit;
 }
 
+SkCachedData* SkResourceCache::newCachedData(size_t bytes) {
+    if (fDiscardableFactory) {
+        SkDiscardableMemory* dm = fDiscardableFactory(bytes);
+        return dm ? SkNEW_ARGS(SkCachedData, (bytes, dm)) : NULL;
+    } else {
+        return SkNEW_ARGS(SkCachedData, (sk_malloc_throw(bytes), bytes));
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkResourceCache::detach(Rec* rec) {
@@ -482,6 +491,11 @@
     return get_cache()->allocator();
 }
 
+SkCachedData* SkResourceCache::NewCachedData(size_t bytes) {
+    SkAutoMutexAcquire am(gMutex);
+    return get_cache()->newCachedData(bytes);
+}
+
 void SkResourceCache::Dump() {
     SkAutoMutexAcquire am(gMutex);
     get_cache()->dump();
diff --git a/src/core/SkResourceCache.h b/src/core/SkResourceCache.h
index c16913f..883ed18 100644
--- a/src/core/SkResourceCache.h
+++ b/src/core/SkResourceCache.h
@@ -137,6 +137,8 @@
      */
     static SkBitmap::Allocator* GetAllocator();
 
+    static SkCachedData* NewCachedData(size_t bytes);
+
     /**
      *  Call SkDebugf() with diagnostic information about the state of the cache
      */