aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBo Liu <boliu@google.com>2014-08-13 09:53:42 -0700
committerBo Liu <boliu@google.com>2014-08-13 09:53:42 -0700
commite90b557be8fb14d632e6243bf7c4efc9286740e3 (patch)
tree6fac41aff192982f0a486c991fdb9694930ceb14
parent9465c499720c87a1e61ec91f1c0b11babf928f5d (diff)
parentc7560eb5d81a46be62c0531054bc3b1447d8dc0e (diff)
downloadskia-e90b557be8fb14d632e6243bf7c4efc9286740e3.tar.gz
Merge from Chromium at DEPS revision 37.0.2062.76
This commit was generated by merge_to_master.py. Change-Id: I4eb8934973e4c6323bdcc15be5e18313c9c5c764
-rw-r--r--bench/ImageCacheBench.cpp2
-rw-r--r--gyp/tests.gypi1
-rw-r--r--include/core/SkGraphics.h44
-rw-r--r--src/core/SkBitmapProcState.cpp19
-rw-r--r--src/core/SkScaledImageCache.cpp82
-rw-r--r--src/core/SkScaledImageCache.h35
-rw-r--r--tests/ImageCacheTest.cpp2
-rw-r--r--tests/ScaledImageCache.cpp60
8 files changed, 200 insertions, 45 deletions
diff --git a/bench/ImageCacheBench.cpp b/bench/ImageCacheBench.cpp
index 5f1715fc3..e65d1fc3e 100644
--- a/bench/ImageCacheBench.cpp
+++ b/bench/ImageCacheBench.cpp
@@ -37,7 +37,7 @@ protected:
}
virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
- if (fCache.getBytesUsed() == 0) {
+ if (fCache.getTotalBytesUsed() == 0) {
this->populateCache();
}
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index 2a7dddd4f..9e418d687 100644
--- a/gyp/tests.gypi
+++ b/gyp/tests.gypi
@@ -155,6 +155,7 @@
'../tests/RuntimeConfigTest.cpp',
'../tests/SHA1Test.cpp',
'../tests/ScalarTest.cpp',
+ '../tests/ScaledImageCache.cpp',
'../tests/SerializationTest.cpp',
'../tests/ShaderImageFilterTest.cpp',
'../tests/ShaderOpacityTest.cpp',
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
index 2667a388d..e7865ca5a 100644
--- a/include/core/SkGraphics.h
+++ b/include/core/SkGraphics.h
@@ -79,9 +79,47 @@ public:
*/
static void PurgeFontCache();
- static size_t GetImageCacheBytesUsed();
- static size_t GetImageCacheByteLimit();
- static size_t SetImageCacheByteLimit(size_t newLimit);
+ /**
+ * Scaling bitmaps with the SkPaint::kHigh_FilterLevel setting is
+ * expensive, so the result is saved in the global Scaled Image
+ * Cache.
+ *
+ * This function returns the memory usage of the Scaled Image Cache.
+ */
+ static size_t GetImageCacheTotalBytesUsed();
+ /**
+ * These functions get/set the memory usage limit for the Scaled
+ * Image Cache. Bitmaps are purged from the cache when the
+ * memory useage exceeds this limit.
+ */
+ static size_t GetImageCacheTotalByteLimit();
+ static size_t SetImageCacheTotalByteLimit(size_t newLimit);
+
+ // DEPRECATED
+ static size_t GetImageCacheBytesUsed() {
+ return GetImageCacheTotalBytesUsed();
+ }
+ // DEPRECATED
+ static size_t GetImageCacheByteLimit() {
+ return GetImageCacheTotalByteLimit();
+ }
+ // DEPRECATED
+ static size_t SetImageCacheByteLimit(size_t newLimit) {
+ return SetImageCacheTotalByteLimit(newLimit);
+ }
+
+ /**
+ * Scaling bitmaps with the SkPaint::kHigh_FilterLevel setting is
+ * expensive, so the result is saved in the global Scaled Image
+ * Cache. When the resulting bitmap is too large, this can
+ * overload the cache. If the ImageCacheSingleAllocationByteLimit
+ * is set to a non-zero number, and the resulting bitmap would be
+ * larger than that value, the bitmap scaling algorithm falls
+ * back onto a cheaper algorithm and does not cache the result.
+ * Zero is the default value.
+ */
+ static size_t GetImageCacheSingleAllocationByteLimit();
+ static size_t SetImageCacheSingleAllocationByteLimit(size_t newLimit);
/**
* Applications with command line options may pass optional state, such
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index f6f9f3fa0..137bcda91 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -127,6 +127,21 @@ private:
};
#define AutoScaledCacheUnlocker(...) SK_REQUIRE_LOCAL_VAR(AutoScaledCacheUnlocker)
+// Check to see that the size of the bitmap that would be produced by
+// scaling by the given inverted matrix is less than the maximum allowed.
+static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
+ size_t maximumAllocation
+ = SkScaledImageCache::GetSingleAllocationByteLimit();
+ if (0 == maximumAllocation) {
+ return true;
+ }
+ // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
+ // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
+ // Skip the division step:
+ return bm.info().getSafeSize(bm.info().minRowBytes())
+ < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
+}
+
// TODO -- we may want to pass the clip into this function so we only scale
// the portion of the image that we're going to need. This will complicate
// the interface to the cache, but might be well worth it.
@@ -140,14 +155,14 @@ bool SkBitmapProcState::possiblyScaleImage() {
if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
return false;
}
-
// Check to see if the transformation matrix is simple, and if we're
// doing high quality scaling. If so, do the bitmap scale here and
// remove the scaling component from the matrix.
if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
- kN32_SkColorType == fOrigBitmap.colorType()) {
+ kN32_SkColorType == fOrigBitmap.colorType() &&
+ cache_size_okay(fOrigBitmap, fInvMatrix)) {
SkScalar invScaleX = fInvMatrix.getScaleX();
SkScalar invScaleY = fInvMatrix.getScaleY();
diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp
index a03024819..43ff7ef89 100644
--- a/src/core/SkScaledImageCache.cpp
+++ b/src/core/SkScaledImageCache.cpp
@@ -165,12 +165,13 @@ void SkScaledImageCache::init() {
#else
fHash = NULL;
#endif
- fBytesUsed = 0;
+ fTotalBytesUsed = 0;
fCount = 0;
+ fSingleAllocationByteLimit = 0;
fAllocator = NULL;
// One of these should be explicit set by the caller after we return.
- fByteLimit = 0;
+ fTotalByteLimit = 0;
fDiscardableFactory = NULL;
}
@@ -297,7 +298,7 @@ SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
this->init();
- fByteLimit = byteLimit;
+ fTotalByteLimit = byteLimit;
}
SkScaledImageCache::~SkScaledImageCache() {
@@ -502,10 +503,10 @@ void SkScaledImageCache::purgeAsNeeded() {
byteLimit = SK_MaxU32; // no limit based on bytes
} else {
countLimit = SK_MaxS32; // no limit based on count
- byteLimit = fByteLimit;
+ byteLimit = fTotalByteLimit;
}
- size_t bytesUsed = fBytesUsed;
+ size_t bytesUsed = fTotalBytesUsed;
int countUsed = fCount;
Rec* rec = fTail;
@@ -531,13 +532,13 @@ void SkScaledImageCache::purgeAsNeeded() {
rec = prev;
}
- fBytesUsed = bytesUsed;
+ fTotalBytesUsed = bytesUsed;
fCount = countUsed;
}
-size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
- size_t prevLimit = fByteLimit;
- fByteLimit = newLimit;
+size_t SkScaledImageCache::setTotalByteLimit(size_t newLimit) {
+ size_t prevLimit = fTotalByteLimit;
+ fTotalByteLimit = newLimit;
if (newLimit < prevLimit) {
this->purgeAsNeeded();
}
@@ -597,7 +598,7 @@ void SkScaledImageCache::addToHead(Rec* rec) {
if (!fTail) {
fTail = rec;
}
- fBytesUsed += rec->bytesUsed();
+ fTotalBytesUsed += rec->bytesUsed();
fCount += 1;
this->validate();
@@ -609,14 +610,14 @@ void SkScaledImageCache::addToHead(Rec* rec) {
void SkScaledImageCache::validate() const {
if (NULL == fHead) {
SkASSERT(NULL == fTail);
- SkASSERT(0 == fBytesUsed);
+ SkASSERT(0 == fTotalBytesUsed);
return;
}
if (fHead == fTail) {
SkASSERT(NULL == fHead->fPrev);
SkASSERT(NULL == fHead->fNext);
- SkASSERT(fHead->bytesUsed() == fBytesUsed);
+ SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
return;
}
@@ -631,7 +632,7 @@ void SkScaledImageCache::validate() const {
while (rec) {
count += 1;
used += rec->bytesUsed();
- SkASSERT(used <= fBytesUsed);
+ SkASSERT(used <= fTotalBytesUsed);
rec = rec->fNext;
}
SkASSERT(fCount == count);
@@ -661,10 +662,20 @@ void SkScaledImageCache::dump() const {
}
SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
- fCount, fBytesUsed, locked,
+ fCount, fTotalBytesUsed, locked,
fDiscardableFactory ? "discardable" : "malloc");
}
+size_t SkScaledImageCache::setSingleAllocationByteLimit(size_t newLimit) {
+ size_t oldLimit = fSingleAllocationByteLimit;
+ fSingleAllocationByteLimit = newLimit;
+ return oldLimit;
+}
+
+size_t SkScaledImageCache::getSingleAllocationByteLimit() const {
+ return fSingleAllocationByteLimit;
+}
+
///////////////////////////////////////////////////////////////////////////////
#include "SkThread.h"
@@ -751,19 +762,19 @@ void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
// get_cache()->dump();
}
-size_t SkScaledImageCache::GetBytesUsed() {
+size_t SkScaledImageCache::GetTotalBytesUsed() {
SkAutoMutexAcquire am(gMutex);
- return get_cache()->getBytesUsed();
+ return get_cache()->getTotalBytesUsed();
}
-size_t SkScaledImageCache::GetByteLimit() {
+size_t SkScaledImageCache::GetTotalByteLimit() {
SkAutoMutexAcquire am(gMutex);
- return get_cache()->getByteLimit();
+ return get_cache()->getTotalByteLimit();
}
-size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
+size_t SkScaledImageCache::SetTotalByteLimit(size_t newLimit) {
SkAutoMutexAcquire am(gMutex);
- return get_cache()->setByteLimit(newLimit);
+ return get_cache()->setTotalByteLimit(newLimit);
}
SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
@@ -776,18 +787,37 @@ void SkScaledImageCache::Dump() {
get_cache()->dump();
}
+size_t SkScaledImageCache::SetSingleAllocationByteLimit(size_t size) {
+ SkAutoMutexAcquire am(gMutex);
+ return get_cache()->setSingleAllocationByteLimit(size);
+}
+
+size_t SkScaledImageCache::GetSingleAllocationByteLimit() {
+ SkAutoMutexAcquire am(gMutex);
+ return get_cache()->getSingleAllocationByteLimit();
+}
+
///////////////////////////////////////////////////////////////////////////////
#include "SkGraphics.h"
-size_t SkGraphics::GetImageCacheBytesUsed() {
- return SkScaledImageCache::GetBytesUsed();
+size_t SkGraphics::GetImageCacheTotalBytesUsed() {
+ return SkScaledImageCache::GetTotalBytesUsed();
+}
+
+size_t SkGraphics::GetImageCacheTotalByteLimit() {
+ return SkScaledImageCache::GetTotalByteLimit();
}
-size_t SkGraphics::GetImageCacheByteLimit() {
- return SkScaledImageCache::GetByteLimit();
+size_t SkGraphics::SetImageCacheTotalByteLimit(size_t newLimit) {
+ return SkScaledImageCache::SetTotalByteLimit(newLimit);
}
-size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
- return SkScaledImageCache::SetByteLimit(newLimit);
+size_t SkGraphics::GetImageCacheSingleAllocationByteLimit() {
+ return SkScaledImageCache::GetSingleAllocationByteLimit();
}
+
+size_t SkGraphics::SetImageCacheSingleAllocationByteLimit(size_t newLimit) {
+ return SkScaledImageCache::SetSingleAllocationByteLimit(newLimit);
+}
+
diff --git a/src/core/SkScaledImageCache.h b/src/core/SkScaledImageCache.h
index fe072306d..817147e3b 100644
--- a/src/core/SkScaledImageCache.h
+++ b/src/core/SkScaledImageCache.h
@@ -60,9 +60,12 @@ public:
static void Unlock(ID*);
- static size_t GetBytesUsed();
- static size_t GetByteLimit();
- static size_t SetByteLimit(size_t newLimit);
+ static size_t GetTotalBytesUsed();
+ static size_t GetTotalByteLimit();
+ static size_t SetTotalByteLimit(size_t newLimit);
+
+ static size_t SetSingleAllocationByteLimit(size_t);
+ static size_t GetSingleAllocationByteLimit();
static SkBitmap::Allocator* GetAllocator();
@@ -76,9 +79,9 @@ public:
/**
* Construct the cache to call DiscardableFactory when it
* allocates memory for the pixels. In this mode, the cache has
- * not explicit budget, and so methods like getBytesUsed() and
- * getByteLimit() will return 0, and setByteLimit will ignore its argument
- * and return 0.
+ * not explicit budget, and so methods like getTotalBytesUsed()
+ * and getTotalByteLimit() will return 0, and setTotalByteLimit
+ * will ignore its argument and return 0.
*/
SkScaledImageCache(DiscardableFactory);
@@ -86,7 +89,7 @@ public:
* Construct the cache, allocating memory with malloc, and respect the
* byteLimit, purging automatically when a new image is added to the cache
* that pushes the total bytesUsed over the limit. Note: The limit can be
- * changed at runtime with setByteLimit.
+ * changed at runtime with setTotalByteLimit.
*/
SkScaledImageCache(size_t byteLimit);
@@ -144,15 +147,22 @@ public:
*/
void unlock(ID*);
- size_t getBytesUsed() const { return fBytesUsed; }
- size_t getByteLimit() const { return fByteLimit; }
+ size_t getTotalBytesUsed() const { return fTotalBytesUsed; }
+ size_t getTotalByteLimit() const { return fTotalByteLimit; }
/**
+ * This is respected by SkBitmapProcState::possiblyScaleImage.
+ * 0 is no maximum at all; this is the default.
+ * setSingleAllocationByteLimit() returns the previous value.
+ */
+ size_t setSingleAllocationByteLimit(size_t maximumAllocationSize);
+ size_t getSingleAllocationByteLimit() const;
+ /**
* Set the maximum number of bytes available to this cache. If the current
* cache exceeds this new value, it will be purged to try to fit within
* this new limit.
*/
- size_t setByteLimit(size_t newLimit);
+ size_t setTotalByteLimit(size_t newLimit);
SkBitmap::Allocator* allocator() const { return fAllocator; };
@@ -175,8 +185,9 @@ private:
// the allocator is NULL or one that matches discardables
SkBitmap::Allocator* fAllocator;
- size_t fBytesUsed;
- size_t fByteLimit;
+ size_t fTotalBytesUsed;
+ size_t fTotalByteLimit;
+ size_t fSingleAllocationByteLimit;
int fCount;
Rec* findAndLock(uint32_t generationID, SkScalar sx, SkScalar sy,
diff --git a/tests/ImageCacheTest.cpp b/tests/ImageCacheTest.cpp
index 92d0b519d..00f6c77aa 100644
--- a/tests/ImageCacheTest.cpp
+++ b/tests/ImageCacheTest.cpp
@@ -74,7 +74,7 @@ static void test_cache(skiatest::Reporter* reporter, SkScaledImageCache& cache,
}
}
- cache.setByteLimit(0);
+ cache.setTotalByteLimit(0);
}
#include "SkDiscardableMemoryPool.h"
diff --git a/tests/ScaledImageCache.cpp b/tests/ScaledImageCache.cpp
new file mode 100644
index 000000000..2040afea2
--- /dev/null
+++ b/tests/ScaledImageCache.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "Test.h"
+#include "SkGraphics.h"
+#include "SkCanvas.h"
+
+static const int kCanvasSize = 1;
+static const int kBitmapSize = 16;
+static const int kScale = 8;
+
+static size_t test_scaled_image_cache_useage() {
+ SkAutoTUnref<SkCanvas> canvas(
+ SkCanvas::NewRasterN32(kCanvasSize, kCanvasSize));
+ SkBitmap bitmap;
+ SkAssertResult(bitmap.allocN32Pixels(kBitmapSize, kBitmapSize));
+ SkScalar scaledSize = SkIntToScalar(kScale * kBitmapSize);
+ canvas->clipRect(SkRect::MakeLTRB(0, 0, scaledSize, scaledSize));
+ SkPaint paint;
+ paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+ size_t bytesUsed = SkGraphics::GetImageCacheBytesUsed();
+ canvas->drawBitmapRect(bitmap,
+ SkRect::MakeLTRB(0, 0, scaledSize, scaledSize),
+ &paint);
+ return SkGraphics::GetImageCacheBytesUsed() - bytesUsed;
+}
+
+// http://crbug.com/389439
+DEF_TEST(ScaledImageCache_SingleAllocationByteLimit, reporter) {
+ size_t originalByteLimit = SkGraphics::GetImageCacheByteLimit();
+ size_t originalAllocationLimit =
+ SkGraphics::GetImageCacheSingleAllocationByteLimit();
+
+ size_t size = kBitmapSize * kScale * kBitmapSize * kScale
+ * SkColorTypeBytesPerPixel(kN32_SkColorType);
+
+ SkGraphics::SetImageCacheByteLimit(0); // clear cache
+ SkGraphics::SetImageCacheByteLimit(2 * size);
+ SkGraphics::SetImageCacheSingleAllocationByteLimit(0);
+
+ REPORTER_ASSERT(reporter, size == test_scaled_image_cache_useage());
+
+ SkGraphics::SetImageCacheByteLimit(0); // clear cache
+ SkGraphics::SetImageCacheByteLimit(2 * size);
+ SkGraphics::SetImageCacheSingleAllocationByteLimit(size * 2);
+
+ REPORTER_ASSERT(reporter, size == test_scaled_image_cache_useage());
+
+ SkGraphics::SetImageCacheByteLimit(0); // clear cache
+ SkGraphics::SetImageCacheByteLimit(2 * size);
+ SkGraphics::SetImageCacheSingleAllocationByteLimit(size / 2);
+
+ REPORTER_ASSERT(reporter, 0 == test_scaled_image_cache_useage());
+
+ SkGraphics::SetImageCacheSingleAllocationByteLimit(originalAllocationLimit);
+ SkGraphics::SetImageCacheByteLimit(originalByteLimit);
+}