Add SkMaskCache
BUG=skia:
Review URL: https://codereview.chromium.org/670063004
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 4f9379f..553e36d 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -104,6 +104,7 @@
'<(skia_src_path)/core/SkLineClipper.cpp',
'<(skia_src_path)/core/SkMallocPixelRef.cpp',
'<(skia_src_path)/core/SkMask.cpp',
+ '<(skia_src_path)/core/SkMaskCache.cpp',
'<(skia_src_path)/core/SkMaskFilter.cpp',
'<(skia_src_path)/core/SkMaskGamma.cpp',
'<(skia_src_path)/core/SkMaskGamma.h',
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index 827d6e3..b6e0606 100644
--- a/gyp/tests.gypi
+++ b/gyp/tests.gypi
@@ -133,6 +133,7 @@
'../tests/LazyPtrTest.cpp',
'../tests/MD5Test.cpp',
'../tests/MallocPixelRefTest.cpp',
+ '../tests/MaskCacheTest.cpp',
'../tests/MathTest.cpp',
'../tests/Matrix44Test.cpp',
'../tests/MatrixClipCollapseTest.cpp',
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
*/
diff --git a/tests/MaskCacheTest.cpp b/tests/MaskCacheTest.cpp
new file mode 100644
index 0000000..0b83c62
--- /dev/null
+++ b/tests/MaskCacheTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "SkCachedData.h"
+#include "SkMaskCache.h"
+#include "SkResourceCache.h"
+#include "Test.h"
+
+enum LockedState {
+ kUnlocked,
+ kLocked,
+};
+
+enum CachedState {
+ kNotInCache,
+ kInCache,
+};
+
+static void check_data(skiatest::Reporter* reporter, SkCachedData* data,
+ int refcnt, CachedState cacheState, LockedState lockedState) {
+ REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
+ REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
+ bool isLocked = (data->data() != NULL);
+ REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
+}
+
+DEF_TEST(RRectMaskCache, reporter) {
+ SkResourceCache cache(1024);
+
+ SkScalar sigma = 0.8f;
+ SkRect rect = SkRect::MakeWH(100, 100);
+ SkRRect rrect;
+ rrect.setRectXY(rect, 30, 30);
+ SkBlurStyle style = kNormal_SkBlurStyle;
+ SkBlurQuality quality = kLow_SkBlurQuality;
+ SkMask mask;
+
+ SkCachedData* data = SkMaskCache::FindAndRef(sigma, rrect, style, quality, &mask, &cache);
+ REPORTER_ASSERT(reporter, NULL == data);
+
+ size_t size = 256;
+ data = cache.newCachedData(size);
+ memset(data->writable_data(), 0xff, size);
+ mask.fBounds.setXYWH(0, 0, 100, 100);
+ mask.fRowBytes = 100;
+ mask.fFormat = SkMask::kBW_Format;
+ SkMaskCache::Add(sigma, rrect, style, quality, mask, data, &cache);
+ check_data(reporter, data, 2, kInCache, kLocked);
+
+ data->unref();
+ check_data(reporter, data, 1, kInCache, kUnlocked);
+
+ sk_bzero(&mask, sizeof(mask));
+ data = SkMaskCache::FindAndRef(sigma, rrect, style, quality, &mask, &cache);
+ REPORTER_ASSERT(reporter, data);
+ REPORTER_ASSERT(reporter, data->size() == size);
+ REPORTER_ASSERT(reporter, mask.fBounds.top() == 0 && mask.fBounds.bottom() == 100);
+ REPORTER_ASSERT(reporter, data->data() == (const void*)mask.fImage);
+ check_data(reporter, data, 2, kInCache, kLocked);
+
+ cache.purgeAll();
+ check_data(reporter, data, 1, kNotInCache, kLocked);
+ data->unref();
+}
+
+DEF_TEST(RectsMaskCache, reporter) {
+ SkResourceCache cache(1024);
+
+ SkScalar sigma = 0.8f;
+ SkRect rect = SkRect::MakeWH(100, 100);
+ SkRect rects[2] = {rect};
+ SkBlurStyle style = kNormal_SkBlurStyle;
+ SkMask mask;
+
+ SkCachedData* data = SkMaskCache::FindAndRef(sigma, rects, 1, style, &mask, &cache);
+ REPORTER_ASSERT(reporter, NULL == data);
+
+ size_t size = 256;
+ data = cache.newCachedData(size);
+ memset(data->writable_data(), 0xff, size);
+ mask.fBounds.setXYWH(0, 0, 100, 100);
+ mask.fRowBytes = 100;
+ mask.fFormat = SkMask::kBW_Format;
+ SkMaskCache::Add(sigma, rects, 1, style, mask, data, &cache);
+ check_data(reporter, data, 2, kInCache, kLocked);
+
+ data->unref();
+ check_data(reporter, data, 1, kInCache, kUnlocked);
+
+ sk_bzero(&mask, sizeof(mask));
+ data = SkMaskCache::FindAndRef(sigma, rects, 1, style, &mask, &cache);
+ REPORTER_ASSERT(reporter, data);
+ REPORTER_ASSERT(reporter, data->size() == size);
+ REPORTER_ASSERT(reporter, mask.fBounds.top() == 0 && mask.fBounds.bottom() == 100);
+ REPORTER_ASSERT(reporter, data->data() == (const void*)mask.fImage);
+ check_data(reporter, data, 2, kInCache, kLocked);
+
+ cache.purgeAll();
+ check_data(reporter, data, 1, kNotInCache, kLocked);
+ data->unref();
+}