Set maximum output size for scaled-image-cache images
Accessable via: SkScaledImageCache::{G,S}etMaximumOutputSizeForHighQualityFilter Also, a unit test. BUG=389439 R=humper@google.com, tomhudson@google.com, reveman@chromium.org, vangelis@chromium.org, reed@google.com Author: halcanary@google.com Review URL: https://codereview.chromium.org/394003003
This commit is contained in:
parent
3f376a5500
commit
805ef159d1
@ -37,7 +37,7 @@ protected:
|
||||
}
|
||||
|
||||
virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
|
||||
if (fCache.getBytesUsed() == 0) {
|
||||
if (fCache.getTotalBytesUsed() == 0) {
|
||||
this->populateCache();
|
||||
}
|
||||
|
||||
|
@ -162,6 +162,7 @@
|
||||
'../tests/SHA1Test.cpp',
|
||||
'../tests/SListTest.cpp',
|
||||
'../tests/ScalarTest.cpp',
|
||||
'../tests/ScaledImageCache.cpp',
|
||||
'../tests/SerializationTest.cpp',
|
||||
'../tests/ShaderImageFilterTest.cpp',
|
||||
'../tests/ShaderOpacityTest.cpp',
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -140,12 +140,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;
|
||||
}
|
||||
|
||||
@ -270,7 +271,7 @@ SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
|
||||
|
||||
SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
|
||||
this->init();
|
||||
fByteLimit = byteLimit;
|
||||
fTotalByteLimit = byteLimit;
|
||||
}
|
||||
|
||||
SkScaledImageCache::~SkScaledImageCache() {
|
||||
@ -475,10 +476,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;
|
||||
@ -504,13 +505,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();
|
||||
}
|
||||
@ -570,7 +571,7 @@ void SkScaledImageCache::addToHead(Rec* rec) {
|
||||
if (!fTail) {
|
||||
fTail = rec;
|
||||
}
|
||||
fBytesUsed += rec->bytesUsed();
|
||||
fTotalBytesUsed += rec->bytesUsed();
|
||||
fCount += 1;
|
||||
|
||||
this->validate();
|
||||
@ -582,14 +583,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;
|
||||
}
|
||||
|
||||
@ -604,7 +605,7 @@ void SkScaledImageCache::validate() const {
|
||||
while (rec) {
|
||||
count += 1;
|
||||
used += rec->bytesUsed();
|
||||
SkASSERT(used <= fBytesUsed);
|
||||
SkASSERT(used <= fTotalBytesUsed);
|
||||
rec = rec->fNext;
|
||||
}
|
||||
SkASSERT(fCount == count);
|
||||
@ -634,10 +635,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"
|
||||
@ -724,19 +735,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() {
|
||||
@ -749,18 +760,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::GetImageCacheByteLimit() {
|
||||
return SkScaledImageCache::GetByteLimit();
|
||||
size_t SkGraphics::GetImageCacheTotalByteLimit() {
|
||||
return SkScaledImageCache::GetTotalByteLimit();
|
||||
}
|
||||
|
||||
size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
|
||||
return SkScaledImageCache::SetByteLimit(newLimit);
|
||||
size_t SkGraphics::SetImageCacheTotalByteLimit(size_t newLimit) {
|
||||
return SkScaledImageCache::SetTotalByteLimit(newLimit);
|
||||
}
|
||||
|
||||
size_t SkGraphics::GetImageCacheSingleAllocationByteLimit() {
|
||||
return SkScaledImageCache::GetSingleAllocationByteLimit();
|
||||
}
|
||||
|
||||
size_t SkGraphics::SetImageCacheSingleAllocationByteLimit(size_t newLimit) {
|
||||
return SkScaledImageCache::SetSingleAllocationByteLimit(newLimit);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -74,7 +74,7 @@ static void test_cache(skiatest::Reporter* reporter, SkScaledImageCache& cache,
|
||||
}
|
||||
}
|
||||
|
||||
cache.setByteLimit(0);
|
||||
cache.setTotalByteLimit(0);
|
||||
}
|
||||
|
||||
#include "SkDiscardableMemoryPool.h"
|
||||
|
60
tests/ScaledImageCache.cpp
Normal file
60
tests/ScaledImageCache.cpp
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user