aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2014-08-14 10:31:26 -0400
committerDerek Sollenberger <djsollen@google.com>2014-08-14 10:40:13 -0400
commit4e4a89dab47d74874d6a79c4b34d89ffbfb386af (patch)
treef901914eb9bf246253079529dc6fbb79c80c41d8
parentd9bff96e7785b872e2dc701256e3ac7d7b563b30 (diff)
downloadskia-4e4a89dab47d74874d6a79c4b34d89ffbfb386af.tar.gz
Merge changes from Skia's m37 branch into lmp-dev.
This merge includes the following CLs... 0d78ac2 Set maximum output size for scaled-image-cache images 0c1c911 Adding 64 bit checks f2d87ba Get additional DW font metrics when available. Bug: 17024392 Change-Id: I7956f50e5b95b1e012cf56614b12f1d834b423c1
-rw-r--r--bench/ImageCacheBench.cpp2
-rw-r--r--dm/Android.mk1
-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/SkConvolver.cpp2
-rw-r--r--src/core/SkScaledImageCache.cpp85
-rw-r--r--src/core/SkScaledImageCache.h35
-rw-r--r--src/effects/SkColorMatrixFilter.cpp4
-rw-r--r--src/ports/SkScalerContext_win_dw.cpp32
-rw-r--r--src/ports/SkTypeface_win_dw.h10
-rw-r--r--tests/Android.mk1
-rw-r--r--tests/ImageCacheTest.cpp2
-rw-r--r--tests/ScaledImageCache.cpp60
14 files changed, 246 insertions, 52 deletions
diff --git a/bench/ImageCacheBench.cpp b/bench/ImageCacheBench.cpp
index 5f1715fc31..e65d1fc3e8 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/dm/Android.mk b/dm/Android.mk
index f6a3219c84..f751f3fa50 100644
--- a/dm/Android.mk
+++ b/dm/Android.mk
@@ -447,6 +447,7 @@ LOCAL_SRC_FILES := \
../tests/RuntimeConfigTest.cpp \
../tests/SHA1Test.cpp \
../tests/ScalarTest.cpp \
+ ../tests/ScaledImageCache.cpp \
../tests/SerializationTest.cpp \
../tests/ShaderImageFilterTest.cpp \
../tests/ShaderOpacityTest.cpp \
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index f135107c34..bb3092d50d 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 2667a388d2..e7865ca5af 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 f6f9f3fa06..137bcda91f 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/SkConvolver.cpp b/src/core/SkConvolver.cpp
index 0e97fac07f..0f5cf9021b 100644
--- a/src/core/SkConvolver.cpp
+++ b/src/core/SkConvolver.cpp
@@ -434,7 +434,7 @@ void BGRAConvolve2D(const unsigned char* sourceData,
}
// Compute where in the output image this row of final data will go.
- unsigned char* curOutputRow = &output[outY * outputByteRowStride];
+ unsigned char* curOutputRow = &output[(uint64_t)outY * outputByteRowStride];
// Get the list of rows that the circular buffer has, in order.
int firstRowInCircularBuffer;
diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp
index f266f97171..43ff7ef890 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;
}
@@ -266,7 +267,8 @@ private:
bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
SkColorTable* ctable) {
size_t size = bitmap->getSize();
- if (0 == size) {
+ uint64_t size64 = bitmap->computeSize64();
+ if (0 == size || size64 > (uint64_t)size) {
return false;
}
@@ -296,7 +298,7 @@ SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
this->init();
- fByteLimit = byteLimit;
+ fTotalByteLimit = byteLimit;
}
SkScaledImageCache::~SkScaledImageCache() {
@@ -501,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;
@@ -530,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();
}
@@ -596,7 +598,7 @@ void SkScaledImageCache::addToHead(Rec* rec) {
if (!fTail) {
fTail = rec;
}
- fBytesUsed += rec->bytesUsed();
+ fTotalBytesUsed += rec->bytesUsed();
fCount += 1;
this->validate();
@@ -608,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;
}
@@ -630,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);
@@ -660,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"
@@ -750,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() {
@@ -775,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 fe072306d3..817147e3b8 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/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index b60fa84b68..bd1df79d14 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -450,6 +450,8 @@ public:
private:
GrGLUniformManager::UniformHandle fMatrixHandle;
GrGLUniformManager::UniformHandle fVectorHandle;
+
+ typedef GrGLEffect INHERITED;
};
private:
@@ -462,7 +464,7 @@ private:
SkColorMatrix fMatrix;
- typedef GrGLEffect INHERITED;
+ typedef GrEffect INHERITED;
};
GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index f9f4392b75..2abd8667cb 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -26,6 +26,7 @@
#include "SkTypeface_win_dw.h"
#include <dwrite.h>
+#include <dwrite_1.h>
static bool isLCD(const SkScalerContext::Rec& rec) {
return SkMask::kLCD16_Format == rec.fMaskFormat ||
@@ -489,10 +490,8 @@ void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* mx,
}
if (my) {
- my->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
- my->fAscent = my->fTop;
+ my->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
my->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
- my->fBottom = my->fDescent;
my->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
my->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
my->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
@@ -500,6 +499,33 @@ void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* mx,
my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
+
+ if (NULL != fTypeface->fDWriteFontFace1.get()) {
+ DWRITE_FONT_METRICS1 dwfm1;
+ fTypeface->fDWriteFontFace1->GetMetrics(&dwfm1);
+ my->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
+ my->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
+ my->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
+ my->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
+
+ my->fMaxCharWidth = my->fXMax - my->fXMin;
+ } else {
+ AutoTDWriteTable<SkOTTableHead> head(fTypeface->fDWriteFontFace.get());
+ if (head.fExists &&
+ head.fSize >= sizeof(SkOTTableHead) &&
+ head->version == SkOTTableHead::version1)
+ {
+ my->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
+ my->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
+ my->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
+ my->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
+
+ my->fMaxCharWidth = my->fXMax - my->fXMin;
+ } else {
+ my->fTop = my->fAscent;
+ my->fBottom = my->fDescent;
+ }
+ }
}
}
diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h
index b064ce5770..465db3446b 100644
--- a/src/ports/SkTypeface_win_dw.h
+++ b/src/ports/SkTypeface_win_dw.h
@@ -17,6 +17,7 @@
#include "SkTypes.h"
#include <dwrite.h>
+#include <dwrite_1.h>
class SkFontDescriptor;
struct SkScalerContextRec;
@@ -50,7 +51,13 @@ private:
, fDWriteFontFamily(SkRefComPtr(fontFamily))
, fDWriteFont(SkRefComPtr(font))
, fDWriteFontFace(SkRefComPtr(fontFace))
- { }
+ {
+ if (!SUCCEEDED(fDWriteFontFace->QueryInterface(&fDWriteFontFace1))) {
+ // IUnknown::QueryInterface states that if it fails, punk will be set to NULL.
+ // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/26/96777.aspx
+ SK_ALWAYSBREAK(NULL == fDWriteFontFace1.get());
+ }
+ }
public:
SkTScopedComPtr<IDWriteFactory> fFactory;
@@ -59,6 +66,7 @@ public:
SkTScopedComPtr<IDWriteFontFamily> fDWriteFontFamily;
SkTScopedComPtr<IDWriteFont> fDWriteFont;
SkTScopedComPtr<IDWriteFontFace> fDWriteFontFace;
+ SkTScopedComPtr<IDWriteFontFace1> fDWriteFontFace1;
static DWriteFontTypeface* Create(IDWriteFactory* factory,
IDWriteFontFace* fontFace,
diff --git a/tests/Android.mk b/tests/Android.mk
index fa43c43eda..ad6eb6d96d 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -183,6 +183,7 @@ LOCAL_SRC_FILES := \
RuntimeConfigTest.cpp \
SHA1Test.cpp \
ScalarTest.cpp \
+ ScaledImageCache.cpp \
SerializationTest.cpp \
ShaderImageFilterTest.cpp \
ShaderOpacityTest.cpp \
diff --git a/tests/ImageCacheTest.cpp b/tests/ImageCacheTest.cpp
index 92d0b519d0..00f6c77aa3 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 0000000000..2040afea27
--- /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);
+}