}
virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
- if (fCache.getBytesUsed() == 0) {
+ if (fCache.getTotalBytesUsed() == 0) {
this->populateCache();
}
'../tests/SHA1Test.cpp',
'../tests/SListTest.cpp',
'../tests/ScalarTest.cpp',
+ '../tests/ScaledImageCache.cpp',
'../tests/SerializationTest.cpp',
'../tests/ShaderImageFilterTest.cpp',
'../tests/ShaderOpacityTest.cpp',
*/
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
};
#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.
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();
#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;
}
SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
this->init();
- fByteLimit = byteLimit;
+ fTotalByteLimit = byteLimit;
}
SkScaledImageCache::~SkScaledImageCache() {
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;
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();
}
if (!fTail) {
fTail = rec;
}
- fBytesUsed += rec->bytesUsed();
+ fTotalBytesUsed += rec->bytesUsed();
fCount += 1;
this->validate();
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;
}
while (rec) {
count += 1;
used += rec->bytesUsed();
- SkASSERT(used <= fBytesUsed);
+ SkASSERT(used <= fTotalBytesUsed);
rec = rec->fNext;
}
SkASSERT(fCount == count);
}
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"
// 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() {
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);
+}
+
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();
/**
* 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);
* 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);
*/
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; };
// 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,
}
}
- cache.setByteLimit(0);
+ cache.setTotalByteLimit(0);
}
#include "SkDiscardableMemoryPool.h"
--- /dev/null
+/*
+ * 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);
+}