Big Cleanup: SkBitmapFactory, SkLazyPixelRef, SkImageCache

Removed SkBitmapFactory since no clients were using it.  New cache
selection mechanism can simply pass a SkDiscardableMemory::Factory
into the SkDiscardablePixelRef if non-default SkDiscardableMemory
should be used.  Removed BitmapFactoryTest.

SkDiscardableMemory::Factory interface.  Android will need this
functionality in the future inside their BitmapFactory.

Removed SkLazyPixelRef, since it's functionality is now subsumed into
SkDiscardablePixelRef.  Removed LazyPixelRef test.

Modified SkDiscardablePixelRef to optionally allow it to use a
SkDiscardableMemory::Factory.  This tiny change makes it a replacement
for SkLazyPixelRef.  This functioanlity is also necessary for moving
Android over to SkDiscardablePixelRef from SkImageRef in a later CL.
Added a test for this.

SkDecodingImageGenerator::Install can optionally pass a factory in to
SkDiscardablePixelRef.

Removed SkImageCache, SkLruImageCache, and SkPurgeableImageCache.
This functionality can be handled much more cleanly by
SkDiscardableMemory.

New SkDiscardableMemoryPool class to replace SkLruImageCache.  In a
later CL, we will replace SkImageRef_GlobalPool (used by android) as
well.  This is a concrete implementation of
SkDiscardableMemory::Factory.  Added a test for this.

modified gm/factory.cpp to remove dependnce on SkBitmapFactory +
SkLruImageCache.  Now uses SkDecodingImageGenerator +
SkDiscardablePixelRef + SkDiscardableMemoryPool.

SkImageDecoder::Target replaces SkBitmapFactory::Target.  The
DecodeMemoryToTarget function may disappear in the future.

Moved SkLazyCachingPixelRef::DecodeProc replaces
SkBitmapFactory::DecodeProc.  This is a short term change, since
another CL changes SkLazyCachingPixelRef to use SkImageGenerator
instead of DecodeProc.

Modified DrawBitmapRectTest to use SkDiscardablePixelRef instead of
SkLazyPixelRef.

tools/LazyDecodeBitmap.cpp now uses SkDecodingImageGenerator +
SkDiscardablePixelRef instead of a SkBitmapFactory.

bench_pictures uses the Global SkDiscardableMemoryPool instead of a
global gLruImageCache.

R=reed@google.com, scroggo@google.com

Review URL: https://codereview.chromium.org/103033002

git-svn-id: http://skia.googlecode.com/svn/trunk@12515 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
halcanary@google.com 2013-12-05 18:31:42 +00:00
parent 3e0446ccbf
commit 2c7c7ee47d
35 changed files with 639 additions and 1644 deletions

View File

@ -6,11 +6,12 @@
*/
#include "gm.h"
#include "SkBitmapFactory.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDecodingImageGenerator.h"
#include "SkDiscardableMemoryPool.h"
#include "SkDiscardablePixelRef.h"
#include "SkImageDecoder.h"
#include "SkLruImageCache.h"
#include "SkOSFile.h"
#include "SkStream.h"
@ -28,20 +29,14 @@ protected:
// Copyright-free file from http://openclipart.org/detail/29213/paper-plane-by-ddoo
SkString filename = SkOSPath::SkPathJoin(INHERITED::gResourcePath.c_str(),
"plane.png");
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(filename.c_str()));
if (NULL != stream.get()) {
stream->rewind();
size_t length = stream->getLength();
void* buffer = sk_malloc_throw(length);
stream->read(buffer, length);
SkAutoDataUnref data(SkData::NewFromMalloc(buffer, length));
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
SkAutoDataUnref data(SkData::NewFromFileName(filename.c_str()));
if (NULL != data.get()) {
// Create a cache which will boot the pixels out anytime the
// bitmap is unlocked.
SkAutoTUnref<SkLruImageCache> cache(SkNEW_ARGS(SkLruImageCache, (1)));
factory.setImageCache(cache);
factory.installPixelRef(data, &fBitmap);
SkAutoTUnref<SkDiscardableMemoryPool> pool(
SkNEW_ARGS(SkDiscardableMemoryPool, (1)));
SkAssertResult(SkDecodingImageGenerator::Install(data,
&fBitmap, pool));
}
}

View File

@ -12,6 +12,8 @@
'include_dirs' : [
'../src/core',
'../src/effects', #needed for BlurMask.h
'../src/images',
'../src/lazy',
'../gm', # needed to pull gm.h
'../samplecode', # To pull SampleApp.h and SampleCode.h
'../src/pipe/utils', # For TiledPipeController

View File

@ -71,6 +71,7 @@
'<(skia_src_path)/core/SkDevice.cpp',
'<(skia_src_path)/core/SkDeviceLooper.cpp',
'<(skia_src_path)/core/SkDeviceProfile.cpp',
'<(skia_src_path)/lazy/SkDiscardableMemoryPool.cpp',
'<(skia_src_path)/lazy/SkDiscardablePixelRef.cpp',
'<(skia_src_path)/core/SkDither.cpp',
'<(skia_src_path)/core/SkDraw.cpp',
@ -307,19 +308,6 @@
'<(skia_include_path)/core/SkXfermode.h',
# Lazy decoding:
'<(skia_include_path)/lazy/SkBitmapFactory.h',
'<(skia_include_path)/lazy/SkImageCache.h',
'<(skia_include_path)/lazy/SkLruImageCache.h',
'<(skia_include_path)/lazy/SkPurgeableImageCache.h',
'<(skia_src_path)/lazy/SkBitmapFactory.cpp',
'<(skia_src_path)/lazy/SkLazyPixelRef.h',
'<(skia_src_path)/lazy/SkLazyPixelRef.cpp',
'<(skia_src_path)/lazy/SkLruImageCache.cpp',
'<(skia_src_path)/lazy/SkPurgeableMemoryBlock.h',
'<(skia_src_path)/lazy/SkPurgeableMemoryBlock_common.cpp',
'<(skia_src_path)/lazy/SkPurgeableImageCache.cpp',
'<(skia_src_path)/lazy/SkCachingPixelRef.cpp',
'<(skia_src_path)/lazy/SkCachingPixelRef.h',

View File

@ -9,6 +9,8 @@
'include_dirs': [
'../dm',
'../gm',
'../src/images',
'../src/lazy',
'../src/core',
'../src/effects',
'../src/pipe/utils/',

View File

@ -29,6 +29,8 @@
'type': 'executable',
'include_dirs' : [
'../src/core',
'../src/images',
'../src/lazy',
'../src/effects',
'../src/pipe/utils/',
'../src/utils/',

View File

@ -38,6 +38,7 @@
'../src/images/bmpdecoderhelper.h',
'../src/images/SkDecodingImageGenerator.cpp',
'../src/images/SkDecodingImageGenerator.h',
'../src/images/SkForceLinking.cpp',
'../src/images/SkImageDecoder.cpp',
'../src/images/SkImageDecoder_FactoryDefault.cpp',

View File

@ -297,10 +297,6 @@
'views/SkOSWindow_Unix.h',
'views/SkWidget.h',
'views/SkOSWindow_Win.h',
'lazy/SkBitmapFactory.h',
'lazy/SkImageCache.h',
'lazy/SkPurgeableImageCache.h',
'lazy/SkLruImageCache.h',
],
},
}

View File

@ -33,7 +33,6 @@
'../tests/ARGBImageEncoderTest.cpp',
'../tests/AtomicTest.cpp',
'../tests/BitmapCopyTest.cpp',
'../tests/BitmapFactoryTest.cpp',
'../tests/BitmapGetColorTest.cpp',
'../tests/BitmapHasherTest.cpp',
'../tests/BitmapHeapTest.cpp',
@ -56,6 +55,7 @@
'../tests/DeferredCanvasTest.cpp',
'../tests/DequeTest.cpp',
'../tests/DeviceLooperTest.cpp',
'../tests/DiscardableMemoryPool.cpp',
'../tests/DocumentTest.cpp',
'../tests/DrawBitmapRectTest.cpp',
'../tests/DrawPathTest.cpp',

View File

@ -211,6 +211,9 @@
'../tools/render_pictures_main.cpp',
],
'include_dirs': [
'../src/core',
'../src/images',
'../src/lazy',
'../src/pipe/utils/',
],
'dependencies': [
@ -232,6 +235,7 @@
'../tools/PictureBenchmark.cpp',
],
'include_dirs': [
'../src/core/',
'../bench',
'../src/lazy/',
],
@ -258,7 +262,9 @@
'../src/pipe/utils/SamplePipeControllers.cpp',
],
'include_dirs': [
'../src/core/',
'../src/core',
'../src/images',
'../src/lazy',
'../src/pipe/utils/',
'../src/utils/',
],

View File

@ -11,7 +11,6 @@
#define SkImageDecoder_DEFINED
#include "SkBitmap.h"
#include "SkBitmapFactory.h"
#include "SkImage.h"
#include "SkRect.h"
#include "SkRefCnt.h"
@ -335,6 +334,21 @@ public:
kDecodePixels_Mode, NULL);
}
/**
* Struct containing information about a pixel destination.
*/
struct Target {
/**
* Pre-allocated memory.
*/
void* fAddr;
/**
* Rowbytes of the allocated memory.
*/
size_t fRowBytes;
};
/**
* Decode memory.
* @param info Output parameter. Returns info about the encoded image.
@ -360,7 +374,7 @@ public:
* </code>
*/
static bool DecodeMemoryToTarget(const void* buffer, size_t size, SkImageInfo* info,
const SkBitmapFactory::Target* target);
const Target* target);
/** Decode the image stored in the specified SkStreamRewindable, and store the result
in bitmap. Return true for success or false on failure.

View File

@ -1,99 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkBitmapFactory_DEFINED
#define SkBitmapFactory_DEFINED
#include "SkImage.h"
#include "SkTypes.h"
class SkBitmap;
class SkData;
class SkImageCache;
/**
* Factory for creating a bitmap from encoded data.
*/
class SkBitmapFactory {
public:
/**
* Struct containing information about a pixel destination.
*/
struct Target {
/**
* Pre-allocated memory.
*/
void* fAddr;
/**
* Rowbytes of the allocated memory.
*/
size_t fRowBytes;
};
/**
* Signature for a function to decode an image from encoded data.
*/
typedef bool (*DecodeProc)(const void* data, size_t length, SkImageInfo*, const Target*);
/**
* Create a bitmap factory which uses DecodeProc for decoding.
* @param DecodeProc Must not be NULL.
*/
SkBitmapFactory(DecodeProc);
~SkBitmapFactory();
/**
* Set an image cache to use on pixelrefs provided by installPixelRef. Mutually exclusive
* with fCacheSelector.
*/
void setImageCache(SkImageCache* cache);
/**
* Sets up an SkBitmap from encoded data. On success, the SkBitmap will have its Config,
* width, height, rowBytes and pixelref set. If fImageCache is non-NULL, or if fCacheSelector
* is set and returns non-NULL, the pixelref will lazily decode, and that SkImageCache will
* handle the pixel memory. Otherwise installPixelRef will do an immediate decode.
* @param SkData Encoded data.
* @param SkBitmap to install the pixel ref on.
* @return bool Whether or not a pixel ref was successfully installed.
*/
bool installPixelRef(SkData*, SkBitmap*);
/**
* An object for selecting an SkImageCache to use based on an SkImageInfo.
*/
class CacheSelector : public SkRefCnt {
public:
SK_DECLARE_INST_COUNT(CacheSelector)
/**
* Return an SkImageCache to use based on the provided SkImageInfo. If the caller decides
* to hang on to the result, it will call ref, so the implementation should not add a ref
* as a result of this call.
*/
virtual SkImageCache* selectCache(const SkImageInfo&) = 0;
private:
typedef SkRefCnt INHERITED;
};
/**
* Set the function to be used to select which SkImageCache to use. Mutually exclusive with
* fImageCache.
*/
void setCacheSelector(CacheSelector*);
private:
DecodeProc fDecodeProc;
SkImageCache* fImageCache;
CacheSelector* fCacheSelector;
};
#endif // SkBitmapFactory_DEFINED

View File

@ -1,132 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkImageCache_DEFINED
#define SkImageCache_DEFINED
#include "SkRefCnt.h"
#include "SkTypes.h"
/**
* Interface for a cache that manages pixel memory.
*/
class SkImageCache : public SkRefCnt {
public:
SK_DECLARE_INST_COUNT(SkImageCache)
typedef intptr_t ID;
/**
* Allocate memory whose lifetime is managed by the cache. On success, MUST be balanced with a
* call to releaseCache and a call to throwAwayCache.
* @param bytes Number of bytes needed.
* @param ID Output parameter which must not be NULL. On success, ID will be set to a value
* associated with that memory which can be used as a parameter to the other functions
* in SkImageCache. On failure, ID is unchanged.
* @return Pointer to the newly allocated memory, or NULL. This memory is safe to use until
* releaseCache is called with ID.
*/
virtual void* allocAndPinCache(size_t bytes, ID*) = 0;
/**
* Output parameter for pinCache, stating whether the memory still contains the data it held
* when releaseCache was last called for the same ID.
*/
enum DataStatus {
/**
* The data has been purged, and therefore needs to be rewritten to the returned memory.
*/
kUninitialized_DataStatus,
/**
* The memory still contains the data it held when releaseCache was last called with the
* same ID.
*/
kRetained_DataStatus,
};
/**
* Re-request the memory associated with ID and pin it so that it will not be reclaimed until
* the next call to releaseCache with the same ID.
* @param ID Unique ID for the memory block.
* @param status Output parameter which must not be NULL. On success (i.e. the return value is
* not NULL), status will be set to one of two states representing the cached memory. If
* status is set to kRetained_DataStatus, the memory contains the same data it did
* before releaseCache was called with this ID. If status is set to
* kUninitialized_DataStatus, the memory is still pinned, but the previous data is no
* longer available. If the return value is NULL, status is unchanged.
* @return Pointer: If non-NULL, points to the previously allocated memory, in which case
* this call must be balanced with a call to releaseCache. If NULL, the memory
* has been reclaimed, and throwAwayCache MUST NOT be called.
*/
virtual void* pinCache(ID, DataStatus* status) = 0;
/**
* Inform the cache that it is safe to free the block of memory corresponding to ID. After
* calling this function, the pointer returned by allocAndPinCache or pinCache must not be
* used again. In order to access the same memory after this, pinCache must be called with
* the same ID.
* @param ID Unique ID for the memory block which is now safe to age out of the cache.
*/
virtual void releaseCache(ID) = 0;
/**
* Inform the cache that the block of memory associated with ID will not be asked for again.
* After this call, ID is no longer valid. Must not be called while the associated memory is
* pinned. Must be called to balance a successful allocAndPinCache.
*/
virtual void throwAwayCache(ID) = 0;
/**
* ID which does not correspond to any valid cache.
*/
static const ID UNINITIALIZED_ID = 0;
#ifdef SK_DEBUG
/**
* Debug only status of a memory block.
*/
enum MemoryStatus {
/**
* It is safe to use the pointer returned by the most recent of allocAndPinCache(ID) or
* pinCache(ID) with the same ID.
*/
kPinned_MemoryStatus,
/**
* The pointer returned by the most recent call to allocAndPinCache(ID) or pinCache(ID) has
* since been released by releaseCache(ID). In order to reuse it, pinCache(ID) must be
* called again. Note that after calling releaseCache(ID), the status of that particular
* ID may not be kUnpinned_MemoryStatus, depending on the implementation, but it will not
* be kPinned_MemoryStatus.
*/
kUnpinned_MemoryStatus,
/**
* The memory associated with ID has been thrown away. No calls should be made using the
* same ID.
*/
kFreed_MemoryStatus,
};
/**
* Debug only function to get the status of a particular block of memory. Safe to call after
* throwAwayCache has been called with this ID.
*/
virtual MemoryStatus getMemoryStatus(intptr_t ID) const = 0;
/**
* Debug only function to clear all unpinned caches.
*/
virtual void purgeAllUnpinnedCaches() = 0;
#endif
private:
typedef SkRefCnt INHERITED;
};
#endif // SkImageCache_DEFINED

View File

@ -1,92 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkLruImageCache_DEFINED
#define SkLruImageCache_DEFINED
#include "SkImageCache.h"
#include "SkThread.h"
#include "SkTInternalLList.h"
class CachedPixels;
/**
* SkImageCache implementation that uses an LRU cache to age out old images.
*/
class SkLruImageCache : public SkImageCache {
public:
SK_DECLARE_INST_COUNT(SkLruImageCache)
SkLruImageCache(size_t budget);
virtual ~SkLruImageCache();
#ifdef SK_DEBUG
virtual MemoryStatus getMemoryStatus(ID) const SK_OVERRIDE;
virtual void purgeAllUnpinnedCaches() SK_OVERRIDE;
#endif
/**
* Set the byte limit on cached pixels. If more bytes are used than this, the cache will free
* unpinned memory until under the new limit or until all unpinned memory is freed. This will
* never free pinned memory, so the cache can potentially remain over the limit. The limit is
* enforced each time memory is allocated or released.
* 0 is a special flag for an infinite budget.
* @return size_t The previous limit.
*/
size_t setImageCacheLimit(size_t newLimit);
/**
* Return the number of bytes of memory currently in use by the cache. Can include memory that
* is no longer pinned, but has not been freed.
*/
size_t getImageCacheUsed() const { return fRamUsed; }
virtual void* allocAndPinCache(size_t bytes, ID*) SK_OVERRIDE;
virtual void* pinCache(ID, SkImageCache::DataStatus*) SK_OVERRIDE;
virtual void releaseCache(ID) SK_OVERRIDE;
virtual void throwAwayCache(ID) SK_OVERRIDE;
private:
// Linked list of recently used. Head is the most recently used, and tail is the least.
SkTInternalLList<CachedPixels> fLRU;
typedef SkTInternalLList<CachedPixels>::Iter Iter;
#ifdef SK_DEBUG
// fMutex is mutable so that getMemoryStatus can be const
mutable
#endif
SkMutex fMutex;
size_t fRamBudget;
size_t fRamUsed;
/**
* Find the CachedPixels represented by ID, or NULL if not in the cache. Mutex must be locked
* before calling.
*/
CachedPixels* findByID(ID) const;
/**
* If over budget, throw away pixels which are not currently in use until below budget or there
* are no more pixels eligible to be thrown away. Mutex must be locked before calling.
*/
void purgeIfNeeded();
/**
* Purge until below limit. Mutex must be locked before calling.
*/
void purgeTilAtOrBelow(size_t limit);
/**
* Remove a set of CachedPixels. Mutex must be locked before calling.
*/
void removePixels(CachedPixels*);
typedef SkImageCache INHERITED;
};
#endif // SkLruImageCache_DEFINED

View File

@ -1,48 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkPurgeableImageCache_DEFINED
#define SkPurgeableImageCache_DEFINED
#include "SkImageCache.h"
#ifdef SK_DEBUG
#include "SkTDArray.h"
#endif
/**
* Implementation for SkImageCache that uses system defined purgeable memory.
*/
class SkPurgeableImageCache : public SkImageCache {
public:
SK_DECLARE_INST_COUNT(SkPurgeableImageCache)
static SkImageCache* Create();
virtual void* allocAndPinCache(size_t bytes, ID*) SK_OVERRIDE;
virtual void* pinCache(ID, SkImageCache::DataStatus*) SK_OVERRIDE;
virtual void releaseCache(ID) SK_OVERRIDE;
virtual void throwAwayCache(ID) SK_OVERRIDE;
#ifdef SK_DEBUG
virtual MemoryStatus getMemoryStatus(ID) const SK_OVERRIDE;
virtual void purgeAllUnpinnedCaches() SK_OVERRIDE;
virtual ~SkPurgeableImageCache();
#endif
private:
SkPurgeableImageCache();
#ifdef SK_DEBUG
SkTDArray<ID> fRecs;
int findRec(ID) const;
#endif
void removeRec(ID);
typedef SkImageCache INHERITED;
};
#endif // SkPurgeableImageCache_DEFINED

View File

@ -8,6 +8,7 @@
#ifndef SkDiscardableMemory_DEFINED
#define SkDiscardableMemory_DEFINED
#include "SkRefCnt.h"
#include "SkTypes.h"
/**
@ -15,7 +16,6 @@
* embedder.
*/
class SK_API SkDiscardableMemory {
public:
/**
* Factory method that creates, initializes and locks an SkDiscardableMemory
@ -23,6 +23,17 @@ public:
*/
static SkDiscardableMemory* Create(size_t bytes);
/**
* Factory class that creates, initializes and locks an SkDiscardableMemory
* object. If either of these steps fails, a NULL pointer will be returned.
*/
class Factory : public SkRefCnt {
public:
virtual SkDiscardableMemory* create(size_t bytes) = 0;
private:
typedef SkRefCnt INHERITED;
};
/** Must not be called while locked.
*/
virtual ~SkDiscardableMemory() {}

View File

@ -6,8 +6,6 @@
*/
#include "SkDecodingImageGenerator.h"
#include "SkBitmapFactory.h"
#include "SkData.h"
#include "SkDiscardablePixelRef.h"
#include "SkImageDecoder.h"
@ -23,6 +21,7 @@ SkDecodingImageGenerator::~SkDecodingImageGenerator() {
}
SkData* SkDecodingImageGenerator::refEncodedData() {
// This functionality is used in `gm --serialize`
fData->ref();
return fData;
}
@ -38,15 +37,16 @@ bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) {
SkASSERT(pixels != NULL);
SkBitmapFactory::Target target = {pixels, rowBytes};
SkImageDecoder::Target target = {pixels, rowBytes};
SkImageInfo tmpInfo = info;
return SkImageDecoder::DecodeMemoryToTarget(fData->data(),
fData->size(),
&tmpInfo, &target);
}
bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst) {
bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst,
SkDiscardableMemory::Factory* factory) {
SkASSERT(data != NULL);
SkASSERT(dst != NULL);
SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data)));
return SkDiscardablePixelRef::Install(gen, dst);
return SkDiscardablePixelRef::Install(gen, dst, factory);
}

View File

@ -8,6 +8,7 @@
#ifndef SkDecodingImageGenerator_DEFINED
#define SkDecodingImageGenerator_DEFINED
#include "SkDiscardableMemory.h"
#include "SkImageGenerator.h"
class SkBitmap;
@ -36,8 +37,21 @@ public:
/**
* Install the SkData into the destination bitmap, using a new
* SkDiscardablePixelRef and a new SkDecodingImageGenerator.
*
* @param data Contains the encoded image data that will be used
* by the SkDecodingImageGenerator. Will be ref()ed.
*
* @param destination Upon success, this bitmap will be
* configured and have a pixelref installed.
*
* @param factory If not NULL, this object will be used as a
* source of discardable memory when decoding. If NULL, then
* SkDiscardableMemory::Create() will be called.
*
* @return true iff successful.
*/
static bool Install(SkData* data, SkBitmap* destination);
static bool Install(SkData* data, SkBitmap* destination,
SkDiscardableMemory::Factory* factory = NULL);
private:
SkData* fData;

View File

@ -387,7 +387,7 @@ static bool decode_pixels_to_8888(SkImageDecoder* decoder, SkStream* stream,
bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
SkImageInfo* info,
const SkBitmapFactory::Target* target) {
const SkImageDecoder::Target* target) {
// FIXME: Just to get this working, implement in terms of existing
// ImageDecoder calls.
SkBitmap bm;

View File

@ -1,78 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBitmapFactory.h"
#include "SkBitmap.h"
#include "SkData.h"
#include "SkImageCache.h"
#include "SkImagePriv.h"
#include "SkLazyPixelRef.h"
SkBitmapFactory::SkBitmapFactory(SkBitmapFactory::DecodeProc proc)
: fDecodeProc(proc)
, fImageCache(NULL)
, fCacheSelector(NULL) {
SkASSERT(fDecodeProc != NULL);
}
SkBitmapFactory::~SkBitmapFactory() {
SkSafeUnref(fImageCache);
SkSafeUnref(fCacheSelector);
}
void SkBitmapFactory::setImageCache(SkImageCache *cache) {
SkRefCnt_SafeAssign(fImageCache, cache);
if (cache != NULL) {
SkSafeUnref(fCacheSelector);
fCacheSelector = NULL;
}
}
void SkBitmapFactory::setCacheSelector(CacheSelector* selector) {
SkRefCnt_SafeAssign(fCacheSelector, selector);
if (selector != NULL) {
SkSafeUnref(fImageCache);
fImageCache = NULL;
}
}
bool SkBitmapFactory::installPixelRef(SkData* data, SkBitmap* dst) {
if (NULL == data || 0 == data->size() || dst == NULL) {
return false;
}
SkImageInfo info;
if (!fDecodeProc(data->data(), data->size(), &info, NULL)) {
return false;
}
SkBitmap::Config config = SkImageInfoToBitmapConfig(info);
Target target;
// FIMXE: There will be a problem if this rowbytes is calculated differently from
// in SkLazyPixelRef.
target.fRowBytes = SkImageMinRowBytes(info);
dst->setConfig(config, info.fWidth, info.fHeight, target.fRowBytes, info.fAlphaType);
// fImageCache and fCacheSelector are mutually exclusive.
SkASSERT(NULL == fImageCache || NULL == fCacheSelector);
SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector->selectCache(info);
if (cache != NULL) {
// Now set a new LazyPixelRef on dst.
SkAutoTUnref<SkLazyPixelRef> lazyRef(SkNEW_ARGS(SkLazyPixelRef,
(data, fDecodeProc, cache)));
dst->setPixelRef(lazyRef);
return true;
} else {
dst->allocPixels();
target.fAddr = dst->getPixels();
return fDecodeProc(data->data(), data->size(), &info, &target);
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkDiscardableMemoryPool.h"
#include "SkOnce.h"
// Note:
// A PoolDiscardableMemory is memory that is counted in a pool.
// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
/**
* A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on
* a SkDiscardableMemoryPool object to manage the memory.
*/
class SkPoolDiscardableMemory : public SkDiscardableMemory {
public:
SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool,
void* pointer, size_t bytes);
virtual ~SkPoolDiscardableMemory();
virtual bool lock() SK_OVERRIDE;
virtual void* data() SK_OVERRIDE;
virtual void unlock() SK_OVERRIDE;
friend class SkDiscardableMemoryPool;
private:
SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory);
SkDiscardableMemoryPool* const fPool;
bool fLocked;
void* fPointer;
const size_t fBytes;
};
SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool,
void* pointer,
size_t bytes)
: fPool(pool)
, fLocked(true)
, fPointer(pointer)
, fBytes(bytes) {
SkASSERT(fPool != NULL);
SkASSERT(fPointer != NULL);
SkASSERT(fBytes > 0);
fPool->ref();
}
SkPoolDiscardableMemory::~SkPoolDiscardableMemory() {
fPool->free(this);
fPool->unref();
}
bool SkPoolDiscardableMemory::lock() {
return fPool->lock(this);
}
void* SkPoolDiscardableMemory::data() {
return fLocked ? fPointer : NULL;
}
void SkPoolDiscardableMemory::unlock() {
fPool->unlock(this);
}
////////////////////////////////////////////////////////////////////////////////
SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget,
SkBaseMutex* mutex)
: fMutex(mutex)
, fBudget(budget)
, fUsed(0) {
#if LAZY_CACHE_STATS
fCacheHits = 0;
fCacheMisses = 0;
#endif // LAZY_CACHE_STATS
}
SkDiscardableMemoryPool::~SkDiscardableMemoryPool() {
// SkPoolDiscardableMemory objects that belong to this pool are
// always deleted before deleting this pool since each one has a
// ref to the pool.
SkASSERT(fList.isEmpty());
}
void SkDiscardableMemoryPool::dumpDownTo(size_t budget) {
// assert((NULL = fMutex) || fMutex->isLocked());
// TODO(halcanary) implement bool fMutex::isLocked().
// WARNING: only call this function after aquiring lock.
if (fUsed <= budget) {
return;
}
typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter;
Iter iter;
SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
while ((fUsed > budget) && (NULL != cur)) {
if (!cur->fLocked) {
SkPoolDiscardableMemory* dm = cur;
SkASSERT(dm->fPointer != NULL);
sk_free(dm->fPointer);
dm->fPointer = NULL;
SkASSERT(fUsed >= dm->fBytes);
fUsed -= dm->fBytes;
cur = iter.prev();
// Purged DMs are taken out of the list. This saves times
// looking them up. Purged DMs are NOT deleted.
fList.remove(dm);
} else {
cur = iter.prev();
}
}
}
SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) {
void* addr = sk_malloc_flags(bytes, 0);
if (NULL == addr) {
return NULL;
}
SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory,
(this, addr, bytes));
SkAutoMutexAcquire autoMutexAcquire(fMutex);
fList.addToHead(dm);
fUsed += bytes;
this->dumpDownTo(fBudget);
return dm;
}
void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) {
// This is called by dm's destructor.
if (dm->fPointer != NULL) {
SkAutoMutexAcquire autoMutexAcquire(fMutex);
sk_free(dm->fPointer);
dm->fPointer = NULL;
SkASSERT(fUsed >= dm->fBytes);
fUsed -= dm->fBytes;
fList.remove(dm);
} else {
SkASSERT(!fList.isInList(dm));
}
}
bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) {
SkASSERT(dm != NULL);
if (NULL == dm->fPointer) {
#if LAZY_CACHE_STATS
SkAutoMutexAcquire autoMutexAcquire(fMutex);
++fCacheMisses;
#endif // LAZY_CACHE_STATS
return false;
}
SkAutoMutexAcquire autoMutexAcquire(fMutex);
if (NULL == dm->fPointer) {
// May have been purged while waiting for lock.
#if LAZY_CACHE_STATS
++fCacheMisses;
#endif // LAZY_CACHE_STATS
return false;
}
dm->fLocked = true;
fList.remove(dm);
fList.addToHead(dm);
#if LAZY_CACHE_STATS
++fCacheHits;
#endif // LAZY_CACHE_STATS
return true;
}
void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) {
SkASSERT(dm != NULL);
SkAutoMutexAcquire autoMutexAcquire(fMutex);
dm->fLocked = false;
this->dumpDownTo(fBudget);
}
size_t SkDiscardableMemoryPool::getRAMUsed() {
return fUsed;
}
void SkDiscardableMemoryPool::setRAMBudget(size_t budget) {
SkAutoMutexAcquire autoMutexAcquire(fMutex);
fBudget = budget;
this->dumpDownTo(fBudget);
}
void SkDiscardableMemoryPool::dumpPool() {
SkAutoMutexAcquire autoMutexAcquire(fMutex);
this->dumpDownTo(0);
}
////////////////////////////////////////////////////////////////////////////////
SK_DECLARE_STATIC_MUTEX(gMutex);
static void create_pool(SkDiscardableMemoryPool** pool) {
SkASSERT(NULL == *pool);
*pool = SkNEW_ARGS(SkDiscardableMemoryPool,
(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
&gMutex));
}
SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
static SkDiscardableMemoryPool* gPool(NULL);
SK_DECLARE_STATIC_ONCE(create_pool_once);
SkOnce(&create_pool_once, create_pool, &gPool);
SkASSERT(NULL != gPool);
return gPool;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,71 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDiscardableMemoryPool_DEFINED
#define SkDiscardableMemoryPool_DEFINED
#include "SkDiscardableMemory.h"
#include "SkTInternalLList.h"
#include "SkThread.h"
#ifdef SK_DEBUG
#define LAZY_CACHE_STATS 1
#elif !defined(LAZY_CACHE_STATS)
#define LAZY_CACHE_STATS 0
#endif
/**
* This non-global pool can be used for unit tests to verify that the
* pool works.
*/
class SkPoolDiscardableMemory;
class SkDiscardableMemoryPool : public SkDiscardableMemory::Factory {
public:
/**
* Without mutex, will be not be thread safe.
*/
SkDiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL);
~SkDiscardableMemoryPool();
SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE;
size_t getRAMUsed();
void setRAMBudget(size_t budget);
/** purges all unlocked DMs */
void dumpPool();
friend class SkPoolDiscardableMemory;
#if LAZY_CACHE_STATS
int fCacheHits;
int fCacheMisses;
#endif // LAZY_CACHE_STATS
private:
SkBaseMutex* fMutex;
size_t fBudget;
size_t fUsed;
SkTInternalLList<SkPoolDiscardableMemory> fList;
/** Function called to free memory if needed */
void dumpDownTo(size_t budget);
/** called by SkDiscardableMemoryPool upon destruction */
void free(SkPoolDiscardableMemory* dm);
/** called by SkDiscardableMemoryPool::lock() */
bool lock(SkPoolDiscardableMemory* dm);
/** called by SkDiscardableMemoryPool::unlock() */
void unlock(SkPoolDiscardableMemory* dm);
typedef SkDiscardableMemory::Factory INHERITED;
};
/**
* Returns (and creates if needed) a threadsafe global
* SkDiscardableMemoryPool.
*/
SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool();
#if !defined(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE)
#define SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE (128 * 1024 * 1024)
#endif
#endif // SkDiscardableMemoryPool_DEFINED

View File

@ -11,8 +11,10 @@
SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator,
const SkImageInfo& info,
size_t size,
size_t rowBytes)
size_t rowBytes,
SkDiscardableMemory::Factory* fact)
: fGenerator(generator)
, fDMFactory(fact)
, fInfo(info)
, fSize(size)
, fRowBytes(rowBytes)
@ -23,9 +25,12 @@ SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator,
// The SkImageGenerator contract requires fGenerator to always
// decode the same image on each call to getPixels().
this->setImmutable();
SkSafeRef(fDMFactory);
}
SkDiscardablePixelRef::~SkDiscardablePixelRef() {
SkDELETE(fDiscardableMemory);
SkSafeUnref(fDMFactory);
SkDELETE(fGenerator);
}
@ -34,9 +39,14 @@ void* SkDiscardablePixelRef::onLockPixels(SkColorTable**) {
if (fDiscardableMemory->lock()) {
return fDiscardableMemory->data();
}
SkDELETE(fDiscardableMemory);
fDiscardableMemory = NULL;
}
fDiscardableMemory = SkDiscardableMemory::Create(fSize);
if (fDMFactory != NULL) {
fDiscardableMemory = fDMFactory->create(fSize);
} else {
fDiscardableMemory = SkDiscardableMemory::Create(fSize);
}
if (NULL == fDiscardableMemory) {
return NULL; // Memory allocation failed.
}
@ -53,19 +63,22 @@ void SkDiscardablePixelRef::onUnlockPixels() {
}
bool SkDiscardablePixelRef::Install(SkImageGenerator* generator,
SkBitmap* dst) {
SkBitmap* dst,
SkDiscardableMemory::Factory* factory) {
SkImageInfo info;
SkASSERT(generator != NULL);
if ((NULL == generator)
|| (!generator->getInfo(&info))
|| (!dst->setConfig(info, 0))) {
|| (!dst->setConfig(info, 0))
|| (0 == dst->getSize())) { // dst->getSize=0 Probably a bad config
SkDELETE(generator);
return false;
}
SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef,
(generator, info,
dst->getSize(),
dst->rowBytes())));
dst->rowBytes(),
factory)));
dst->setPixelRef(ref);
return true;
}

View File

@ -8,16 +8,18 @@
#ifndef SkDiscardablePixelRef_DEFINED
#define SkDiscardablePixelRef_DEFINED
#include "SkDiscardableMemory.h"
#include "SkPixelRef.h"
#include "SkImageGenerator.h"
#include "SkImageInfo.h"
class SkDiscardableMemory;
/**
* An interface that allows a purgable PixelRef to re-decode an image.
*/
typedef SkDiscardableMemory* (*SkDiscardableMemoryFactory)(size_t bytes);
class SkDiscardablePixelRef : public SkPixelRef {
public:
/**
@ -30,8 +32,19 @@ public:
* installed into destination is destroyed, it will call
* SkDELETE() on the generator. Therefore, generator should be
* allocated with SkNEW() or SkNEW_ARGS().
*
* @param destination Upon success, this bitmap will be
* configured and have a pixelref installed.
*
* @param factory If not NULL, this object will be used as a
* source of discardable memory when decoding. If NULL, then
* SkDiscardableMemory::Create() will be called.
*
* @return true iff successful.
*/
static bool Install(SkImageGenerator* generator, SkBitmap* destination);
static bool Install(SkImageGenerator* generator,
SkBitmap* destination,
SkDiscardableMemory::Factory* factory = NULL);
SK_DECLARE_UNFLATTENABLE_OBJECT()
@ -47,6 +60,7 @@ protected:
private:
SkImageGenerator* const fGenerator;
SkDiscardableMemory::Factory* const fDMFactory;
const SkImageInfo fInfo;
const size_t fSize; // size of memory to be allocated
const size_t fRowBytes;
@ -59,6 +73,7 @@ private:
SkDiscardablePixelRef(SkImageGenerator* generator,
const SkImageInfo& info,
size_t size,
size_t rowBytes);
size_t rowBytes,
SkDiscardableMemory::Factory* factory);
};
#endif // SkDiscardablePixelRef_DEFINED

View File

@ -1,311 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Sk64.h"
#include "SkLazyPixelRef.h"
#include "SkColorTable.h"
#include "SkData.h"
#include "SkImageCache.h"
#include "SkImagePriv.h"
#include "SkScaledImageCache.h"
#if LAZY_CACHE_STATS
#include "SkThread.h"
int32_t SkLazyPixelRef::gCacheHits;
int32_t SkLazyPixelRef::gCacheMisses;
#endif
SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache)
// Pass NULL for the Mutex so that the default (ring buffer) will be used.
: INHERITED(NULL)
, fErrorInDecoding(false)
, fDecodeProc(proc)
, fImageCache(cache)
, fRowBytes(0) {
SkASSERT(fDecodeProc != NULL);
if (NULL == data) {
fData = SkData::NewEmpty();
fErrorInDecoding = true;
} else {
fData = data;
fData->ref();
fErrorInDecoding = data->size() == 0;
}
if (fImageCache != NULL) {
fImageCache->ref();
fCacheId = SkImageCache::UNINITIALIZED_ID;
} else {
fScaledCacheId = NULL;
}
// mark as uninitialized -- all fields are -1
memset(&fLazilyCachedInfo, 0xFF, sizeof(fLazilyCachedInfo));
// Since this pixel ref bases its data on encoded data, it should never change.
this->setImmutable();
}
SkLazyPixelRef::~SkLazyPixelRef() {
SkASSERT(fData != NULL);
fData->unref();
if (NULL == fImageCache) {
if (fScaledCacheId != NULL) {
SkScaledImageCache::Unlock(fScaledCacheId);
// TODO(halcanary): SkScaledImageCache needs a
// throwAwayCache(id) method.
}
return;
}
SkASSERT(fImageCache);
if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
fImageCache->throwAwayCache(fCacheId);
}
fImageCache->unref();
}
static size_t ComputeMinRowBytesAndSize(const SkImageInfo& info, size_t* rowBytes) {
*rowBytes = SkImageMinRowBytes(info);
Sk64 safeSize;
safeSize.setZero();
if (info.fHeight > 0) {
safeSize.setMul(info.fHeight, SkToS32(*rowBytes));
}
SkASSERT(!safeSize.isNeg());
return safeSize.is32() ? safeSize.get32() : 0;
}
const SkImageInfo* SkLazyPixelRef::getCachedInfo() {
if (fLazilyCachedInfo.fWidth < 0) {
SkImageInfo info;
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
if (fErrorInDecoding) {
return NULL;
}
fLazilyCachedInfo = info;
}
return &fLazilyCachedInfo;
}
/**
Returns bitmap->getPixels() on success; NULL on failure */
static void* decode_into_bitmap(SkImageInfo* info,
SkBitmapFactory::DecodeProc decodeProc,
size_t* rowBytes,
SkData* data,
SkBitmap* bm) {
SkASSERT(info && decodeProc && rowBytes && data && bm);
if (!(bm->setConfig(SkImageInfoToBitmapConfig(*info), info->fWidth,
info->fHeight, *rowBytes, info->fAlphaType)
&& bm->allocPixels(NULL, NULL))) {
// Use the default allocator. It may be necessary for the
// SkLazyPixelRef to have a allocator field which is passed
// into allocPixels().
return NULL;
}
SkBitmapFactory::Target target;
target.fAddr = bm->getPixels();
target.fRowBytes = bm->rowBytes();
*rowBytes = target.fRowBytes;
if (!decodeProc(data->data(), data->size(), info, &target)) {
return NULL;
}
return target.fAddr;
}
void* SkLazyPixelRef::lockScaledImageCachePixels() {
SkASSERT(!fErrorInDecoding);
SkASSERT(NULL == fImageCache);
SkBitmap bitmap;
const SkImageInfo* info = this->getCachedInfo();
if (info == NULL) {
return NULL;
}
// If this is the first time though, this is guaranteed to fail.
// Maybe we should have a flag that says "don't even bother looking"
fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(),
info->fWidth,
info->fHeight,
&bitmap);
if (fScaledCacheId != NULL) {
SkAutoLockPixels autoLockPixels(bitmap);
void* pixels = bitmap.getPixels();
SkASSERT(NULL != pixels);
// At this point, the autoLockPixels will unlockPixels()
// to remove bitmap's lock on the pixels. We will then
// destroy bitmap. The *only* guarantee that this pointer
// remains valid is the guarantee made by
// SkScaledImageCache that it will not destroy the *other*
// bitmap (SkScaledImageCache::Rec.fBitmap) that holds a
// reference to the concrete PixelRef while this record is
// locked.
return pixels;
} else {
// Cache has been purged, must re-decode.
void* pixels = decode_into_bitmap(const_cast<SkImageInfo*>(info),
fDecodeProc, &fRowBytes, fData,
&bitmap);
if (NULL == pixels) {
fErrorInDecoding = true;
return NULL;
}
fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(),
info->fWidth,
info->fHeight,
bitmap);
SkASSERT(fScaledCacheId != NULL);
return pixels;
}
}
void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
if (fErrorInDecoding) {
return NULL;
}
if (NULL == fImageCache) {
return this->lockScaledImageCachePixels();
} else {
return this->lockImageCachePixels();
}
}
void* SkLazyPixelRef::lockImageCachePixels() {
SkASSERT(fImageCache != NULL);
SkASSERT(!fErrorInDecoding);
SkBitmapFactory::Target target;
// Check to see if the pixels still exist in the cache.
if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
target.fAddr = NULL;
} else {
SkImageCache::DataStatus status;
target.fAddr = fImageCache->pinCache(fCacheId, &status);
if (target.fAddr == NULL) {
fCacheId = SkImageCache::UNINITIALIZED_ID;
} else {
if (SkImageCache::kRetained_DataStatus == status) {
#if LAZY_CACHE_STATS
sk_atomic_inc(&gCacheHits);
#endif
return target.fAddr;
}
SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
}
// Cache miss. Either pinCache returned NULL or it returned a memory address without the old
// data
#if LAZY_CACHE_STATS
sk_atomic_inc(&gCacheMisses);
#endif
}
SkASSERT(fData != NULL && fData->size() > 0);
if (NULL == target.fAddr) {
const SkImageInfo* info = this->getCachedInfo();
if (NULL == info) {
SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
return NULL;
}
size_t bytes = ComputeMinRowBytesAndSize(*info, &target.fRowBytes);
target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
if (NULL == target.fAddr) {
// Space could not be allocated.
// Just like the last assert, fCacheId must be UNINITIALIZED_ID.
SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
return NULL;
}
} else {
// pinCache returned purged memory to which target.fAddr already points. Set
// target.fRowBytes properly.
target.fRowBytes = fRowBytes;
// Assume that the size is correct, since it was determined by this same function
// previously.
}
SkASSERT(target.fAddr != NULL);
SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), NULL, &target);
if (fErrorInDecoding) {
fImageCache->throwAwayCache(fCacheId);
fCacheId = SkImageCache::UNINITIALIZED_ID;
return NULL;
}
// Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
fRowBytes = target.fRowBytes;
return target.fAddr;
}
void SkLazyPixelRef::onUnlockPixels() {
if (fErrorInDecoding) {
return;
}
if (NULL == fImageCache) {
// onUnlockPixels() should never be called a second time from
// PixelRef::Unlock() without calling onLockPixels() first.
SkASSERT(NULL != fScaledCacheId);
if (NULL != fScaledCacheId) {
SkScaledImageCache::Unlock(fScaledCacheId);
fScaledCacheId = NULL;
}
} else { // use fImageCache
SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
if (SkImageCache::UNINITIALIZED_ID != fCacheId) {
fImageCache->releaseCache(fCacheId);
}
}
}
SkData* SkLazyPixelRef::onRefEncodedData() {
fData->ref();
return fData;
}
static bool init_from_info(SkBitmap* bm, const SkImageInfo& info,
size_t rowBytes) {
SkBitmap::Config config = SkImageInfoToBitmapConfig(info);
if (SkBitmap::kNo_Config == config) {
return false;
}
return bm->setConfig(config, info.fWidth, info.fHeight, rowBytes, info.fAlphaType)
&&
bm->allocPixels();
}
bool SkLazyPixelRef::onImplementsDecodeInto() {
return true;
}
bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
SkASSERT(fData != NULL && fData->size() > 0);
if (fErrorInDecoding) {
return false;
}
SkImageInfo info;
// Determine the size of the image in order to determine how much memory to allocate.
// FIXME: As an optimization, only do this part once.
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
if (fErrorInDecoding) {
return false;
}
SkBitmapFactory::Target target;
(void)ComputeMinRowBytesAndSize(info, &target.fRowBytes);
SkBitmap tmp;
if (!init_from_info(&tmp, info, target.fRowBytes)) {
return false;
}
target.fAddr = tmp.getPixels();
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
if (fErrorInDecoding) {
return false;
}
*bitmap = tmp;
return true;
}

View File

@ -1,96 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkLazyPixelRef_DEFINED
#define SkLazyPixelRef_DEFINED
#include "SkBitmapFactory.h"
#include "SkImage.h"
#include "SkImageCache.h"
#include "SkPixelRef.h"
#include "SkFlattenable.h"
#include "SkScaledImageCache.h"
class SkColorTable;
class SkData;
class SkImageCache;
#ifdef SK_DEBUG
#define LAZY_CACHE_STATS 1
#elif !defined(LAZY_CACHE_STATS)
#define LAZY_CACHE_STATS 0
#endif
/**
* PixelRef which defers decoding until SkBitmap::lockPixels() is called.
*/
class SkLazyPixelRef : public SkPixelRef {
public:
/**
* Create a new SkLazyPixelRef.
* @param SkData Encoded data representing the pixels.
* @param DecodeProc Called to decode the pixels when needed. Must be non-NULL.
* @param SkImageCache Object that handles allocating and freeing
* the pixel memory, as needed. If NULL, use the global
* SkScaledImageCache.
*/
SkLazyPixelRef(SkData*, SkBitmapFactory::DecodeProc, SkImageCache*);
virtual ~SkLazyPixelRef();
#ifdef SK_DEBUG
intptr_t getCacheId() const { return fCacheId; }
#endif
#if LAZY_CACHE_STATS
static int32_t GetCacheHits() { return gCacheHits; }
static int32_t GetCacheMisses() { return gCacheMisses; }
static void ResetCacheStats() { gCacheHits = gCacheMisses = 0; }
#endif
// No need to flatten this object. When flattening an SkBitmap, SkOrderedWriteBuffer will check
// the encoded data and write that instead.
// Future implementations of SkFlattenableWriteBuffer will need to special case for
// onRefEncodedData as well.
SK_DECLARE_UNFLATTENABLE_OBJECT()
protected:
virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
virtual void onUnlockPixels() SK_OVERRIDE;
virtual bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; }
virtual SkData* onRefEncodedData() SK_OVERRIDE;
virtual bool onImplementsDecodeInto() SK_OVERRIDE;
virtual bool onDecodeInto(int pow2, SkBitmap*) SK_OVERRIDE;
private:
bool fErrorInDecoding;
SkData* fData;
SkBitmapFactory::DecodeProc fDecodeProc;
SkImageCache* fImageCache;
union {
SkImageCache::ID fCacheId;
SkScaledImageCache::ID* fScaledCacheId;
};
size_t fRowBytes;
SkImageInfo fLazilyCachedInfo;
#if LAZY_CACHE_STATS
static int32_t gCacheHits;
static int32_t gCacheMisses;
#endif
// lazily initialized our cached info. Returns NULL on failure.
const SkImage::Info* getCachedInfo();
void* lockScaledImageCachePixels();
void* lockImageCachePixels();
typedef SkPixelRef INHERITED;
};
#endif // SkLazyPixelRef_DEFINED

View File

@ -1,203 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkLruImageCache.h"
static intptr_t NextGenerationID() {
static intptr_t gNextID;
do {
gNextID++;
} while (SkImageCache::UNINITIALIZED_ID == gNextID);
return gNextID;
}
class CachedPixels : public SkNoncopyable {
public:
CachedPixels(size_t length)
: fLength(length)
, fID(NextGenerationID())
, fLocked(false) {
fAddr = sk_malloc_throw(length);
}
~CachedPixels() {
sk_free(fAddr);
}
void* getData() { return fAddr; }
intptr_t getID() const { return fID; }
size_t getLength() const { return fLength; }
void lock() { SkASSERT(!fLocked); fLocked = true; }
void unlock() { SkASSERT(fLocked); fLocked = false; }
bool isLocked() const { return fLocked; }
private:
void* fAddr;
size_t fLength;
const intptr_t fID;
bool fLocked;
SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels);
};
////////////////////////////////////////////////////////////////////////////////////
SkLruImageCache::SkLruImageCache(size_t budget)
: fRamBudget(budget)
, fRamUsed(0) {}
SkLruImageCache::~SkLruImageCache() {
// Don't worry about updating pointers. All will be deleted.
Iter iter;
CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
while (pixels != NULL) {
CachedPixels* prev = iter.prev();
SkASSERT(!pixels->isLocked());
#ifdef SK_DEBUG
fRamUsed -= pixels->getLength();
#endif
SkDELETE(pixels);
pixels = prev;
}
#ifdef SK_DEBUG
SkASSERT(fRamUsed == 0);
#endif
}
#ifdef SK_DEBUG
SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const {
if (SkImageCache::UNINITIALIZED_ID == ID) {
return SkImageCache::kFreed_MemoryStatus;
}
SkAutoMutexAcquire ac(&fMutex);
CachedPixels* pixels = this->findByID(ID);
if (NULL == pixels) {
return SkImageCache::kFreed_MemoryStatus;
}
if (pixels->isLocked()) {
return SkImageCache::kPinned_MemoryStatus;
}
return SkImageCache::kUnpinned_MemoryStatus;
}
void SkLruImageCache::purgeAllUnpinnedCaches() {
SkAutoMutexAcquire ac(&fMutex);
this->purgeTilAtOrBelow(0);
}
#endif
size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) {
size_t oldLimit = fRamBudget;
SkAutoMutexAcquire ac(&fMutex);
fRamBudget = newLimit;
this->purgeIfNeeded();
return oldLimit;
}
void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
SkAutoMutexAcquire ac(&fMutex);
CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
if (ID != NULL) {
*ID = pixels->getID();
}
pixels->lock();
fRamUsed += bytes;
fLRU.addToHead(pixels);
this->purgeIfNeeded();
return pixels->getData();
}
void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
SkAutoMutexAcquire ac(&fMutex);
CachedPixels* pixels = this->findByID(ID);
if (NULL == pixels) {
return NULL;
}
if (pixels != fLRU.head()) {
fLRU.remove(pixels);
fLRU.addToHead(pixels);
}
SkASSERT(status != NULL);
// This cache will never return pinned memory whose data has been overwritten.
*status = SkImageCache::kRetained_DataStatus;
pixels->lock();
return pixels->getData();
}
void SkLruImageCache::releaseCache(intptr_t ID) {
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
SkAutoMutexAcquire ac(&fMutex);
CachedPixels* pixels = this->findByID(ID);
SkASSERT(pixels != NULL);
pixels->unlock();
this->purgeIfNeeded();
}
void SkLruImageCache::throwAwayCache(intptr_t ID) {
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
SkAutoMutexAcquire ac(&fMutex);
CachedPixels* pixels = this->findByID(ID);
if (pixels != NULL) {
if (pixels->isLocked()) {
pixels->unlock();
}
this->removePixels(pixels);
}
}
void SkLruImageCache::removePixels(CachedPixels* pixels) {
// Mutex is already locked.
SkASSERT(!pixels->isLocked());
const size_t size = pixels->getLength();
SkASSERT(size <= fRamUsed);
fLRU.remove(pixels);
SkDELETE(pixels);
fRamUsed -= size;
}
CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
// Mutex is already locked.
Iter iter;
// Start from the head, most recently used.
CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
while (pixels != NULL) {
if (pixels->getID() == ID) {
return pixels;
}
pixels = iter.next();
}
return NULL;
}
void SkLruImageCache::purgeIfNeeded() {
// Mutex is already locked.
if (fRamBudget > 0) {
this->purgeTilAtOrBelow(fRamBudget);
}
}
void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
// Mutex is already locked.
if (fRamUsed > limit) {
Iter iter;
// Start from the tail, least recently used.
CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
while (pixels != NULL && fRamUsed > limit) {
CachedPixels* prev = iter.prev();
if (!pixels->isLocked()) {
this->removePixels(pixels);
}
pixels = prev;
}
}
}

View File

@ -1,159 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkThread.h"
#include "SkPurgeableImageCache.h"
#include "SkPurgeableMemoryBlock.h"
#ifdef SK_DEBUG
#include "SkTSearch.h"
#endif
SK_DECLARE_STATIC_MUTEX(gPurgeableImageMutex);
SkImageCache* SkPurgeableImageCache::Create() {
if (!SkPurgeableMemoryBlock::IsSupported()) {
return NULL;
}
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
static SkPurgeableImageCache gCache;
gCache.ref();
return &gCache;
}
SkPurgeableImageCache::SkPurgeableImageCache() {}
#ifdef SK_DEBUG
SkPurgeableImageCache::~SkPurgeableImageCache() {
SkASSERT(fRecs.count() == 0);
}
#endif
void* SkPurgeableImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
SkPurgeableMemoryBlock* block = SkPurgeableMemoryBlock::Create(bytes);
if (NULL == block) {
return NULL;
}
SkPurgeableMemoryBlock::PinResult pinResult;
void* data = block->pin(&pinResult);
if (NULL == data) {
SkDELETE(block);
return NULL;
}
SkASSERT(ID != NULL);
*ID = reinterpret_cast<intptr_t>(block);
#ifdef SK_DEBUG
// Insert into the array of all recs:
int index = this->findRec(*ID);
SkASSERT(index < 0);
fRecs.insert(~index, 1, ID);
#endif
return data;
}
void* SkPurgeableImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
SkASSERT(this->findRec(ID) >= 0);
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
SkPurgeableMemoryBlock::PinResult pinResult;
void* data = block->pin(&pinResult);
if (NULL == data) {
this->removeRec(ID);
return NULL;
}
switch (pinResult) {
case SkPurgeableMemoryBlock::kRetained_PinResult:
*status = SkImageCache::kRetained_DataStatus;
break;
case SkPurgeableMemoryBlock::kUninitialized_PinResult:
*status = SkImageCache::kUninitialized_DataStatus;
break;
default:
// Invalid value. Treat as a failure to pin.
SkASSERT(false);
this->removeRec(ID);
return NULL;
}
return data;
}
void SkPurgeableImageCache::releaseCache(intptr_t ID) {
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
SkASSERT(this->findRec(ID) >= 0);
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
block->unpin();
}
void SkPurgeableImageCache::throwAwayCache(intptr_t ID) {
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
this->removeRec(ID);
}
#ifdef SK_DEBUG
SkImageCache::MemoryStatus SkPurgeableImageCache::getMemoryStatus(intptr_t ID) const {
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
if (SkImageCache::UNINITIALIZED_ID == ID || this->findRec(ID) < 0) {
return SkImageCache::kFreed_MemoryStatus;
}
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
if (block->isPinned()) {
return SkImageCache::kPinned_MemoryStatus;
}
return SkImageCache::kUnpinned_MemoryStatus;
}
void SkPurgeableImageCache::purgeAllUnpinnedCaches() {
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
if (SkPurgeableMemoryBlock::PlatformSupportsPurgingAllUnpinnedBlocks()) {
SkPurgeableMemoryBlock::PurgeAllUnpinnedBlocks();
} else {
// Go through the blocks, and purge them individually.
// Rather than deleting the blocks, which would interfere with further calls, purge them
// and keep them around.
for (int i = 0; i < fRecs.count(); i++) {
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(fRecs[i]);
if (!block->isPinned()) {
if (!block->purge()) {
// FIXME: This should be more meaningful (which one, etc...)
SkDebugf("Failed to purge\n");
}
}
}
}
}
int SkPurgeableImageCache::findRec(intptr_t rec) const {
return SkTSearch(fRecs.begin(), fRecs.count(), rec, sizeof(intptr_t));
}
#endif
void SkPurgeableImageCache::removeRec(intptr_t ID) {
#ifdef SK_DEBUG
int index = this->findRec(ID);
SkASSERT(index >= 0);
fRecs.remove(index);
#endif
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
SkASSERT(!block->isPinned());
SkDELETE(block);
}

View File

@ -5,57 +5,9 @@
* found in the LICENSE file.
*/
#include "SkDiscardableMemory.h"
#include "SkDiscardableMemoryPool.h"
#include "SkTypes.h"
namespace {
////////////////////////////////////////////////////////////////////////////////
/**
* Always successful, never purges. Useful for testing.
*/
class SkMockDiscardableMemory : public SkDiscardableMemory {
public:
SkMockDiscardableMemory(void*);
virtual ~SkMockDiscardableMemory();
virtual bool lock() SK_OVERRIDE;
virtual void* data() SK_OVERRIDE;
virtual void unlock() SK_OVERRIDE;
private:
bool fLocked;
void* fPointer;
};
////////////////////////////////////////////////////////////////////////////////
SkMockDiscardableMemory::SkMockDiscardableMemory(void* ptr)
: fLocked(true)
, fPointer(ptr) { // Takes ownership of ptr.
SkASSERT(fPointer != NULL);
}
SkMockDiscardableMemory::~SkMockDiscardableMemory() {
SkASSERT(!fLocked);
sk_free(fPointer);
}
bool SkMockDiscardableMemory::lock() {
SkASSERT(!fLocked);
return fLocked = true;
}
void* SkMockDiscardableMemory::data() {
SkASSERT(fLocked);
return fLocked ? fPointer : NULL;
}
void SkMockDiscardableMemory::unlock() {
SkASSERT(fLocked);
fLocked = false;
}
////////////////////////////////////////////////////////////////////////////////
} // namespace
SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) {
void* ptr = sk_malloc_throw(bytes);
return (ptr != NULL) ? SkNEW_ARGS(SkMockDiscardableMemory, (ptr)) : NULL;
return SkGetGlobalDiscardableMemoryPool()->create(bytes);
}

View File

@ -1,197 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifdef SK_DEBUG
#include "SkBitmap.h"
#include "SkBitmapFactory.h"
#include "SkCanvas.h"
#include "SkColor.h"
#include "SkData.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkLazyPixelRef.h"
#include "SkLruImageCache.h"
#include "SkPaint.h"
#include "SkPurgeableImageCache.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "Test.h"
static SkBitmap* create_bitmap() {
SkBitmap* bm = SkNEW(SkBitmap);
// Use a large bitmap.
const int W = 1000, H = 1000;
bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
bm->allocPixels();
bm->eraseColor(SK_ColorBLACK);
SkCanvas canvas(*bm);
SkPaint paint;
paint.setColor(SK_ColorBLUE);
canvas.drawRectCoords(0, 0, SkIntToScalar(W/2), SkIntToScalar(H/2), paint);
return bm;
}
static SkData* create_data_from_bitmap(const SkBitmap& bm) {
SkDynamicMemoryWStream stream;
if (SkImageEncoder::EncodeStream(&stream, bm, SkImageEncoder::kPNG_Type, 100)) {
return stream.copyToData();
}
return NULL;
}
static void assert_bounds_equal(skiatest::Reporter* reporter, const SkBitmap& bm1,
const SkBitmap& bm2) {
REPORTER_ASSERT(reporter, bm1.width() == bm2.width());
REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
}
static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache) {
// Test the cache directly:
cache->purgeAllUnpinnedCaches();
intptr_t ID = SkImageCache::UNINITIALIZED_ID;
const size_t size = 1000;
char buffer[size];
sk_bzero((void*) buffer, size);
void* memory = cache->allocAndPinCache(size, &ID);
if (memory != NULL) {
memcpy(memory, (void*)buffer, size);
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) == SkImageCache::kPinned_MemoryStatus);
cache->releaseCache(ID);
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) != SkImageCache::kPinned_MemoryStatus);
SkImageCache::DataStatus dataStatus;
memory = cache->pinCache(ID, &dataStatus);
if (memory != NULL) {
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
== SkImageCache::kPinned_MemoryStatus);
if (SkImageCache::kRetained_DataStatus == dataStatus) {
REPORTER_ASSERT(reporter, !memcmp(memory, (void*) buffer, size));
}
cache->releaseCache(ID);
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
!= SkImageCache::kPinned_MemoryStatus);
cache->purgeAllUnpinnedCaches();
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
!= SkImageCache::kPinned_MemoryStatus);
memory = cache->pinCache(ID, &dataStatus);
if (memory != NULL) {
// The memory block may or may not have survived the purging (at the
// memory manager's whim) so we cannot check dataStatus here.
cache->releaseCache(ID);
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
!= SkImageCache::kPinned_MemoryStatus);
cache->throwAwayCache(ID);
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
== SkImageCache::kFreed_MemoryStatus);
} else {
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
== SkImageCache::kFreed_MemoryStatus);
}
}
}
}
static void test_factory(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
const SkBitmap& origBitmap) {
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
factory.setImageCache(cache);
SkAutoTDelete<SkBitmap> bitmapFromFactory(SkNEW(SkBitmap));
bool success = factory.installPixelRef(encodedData, bitmapFromFactory.get());
// This assumes that if the encoder worked, the decoder should also work, so the above call
// should not fail.
REPORTER_ASSERT(reporter, success);
assert_bounds_equal(reporter, origBitmap, *bitmapFromFactory.get());
SkPixelRef* pixelRef = bitmapFromFactory->pixelRef();
REPORTER_ASSERT(reporter, pixelRef != NULL);
if (NULL == cache) {
// This assumes that installPixelRef called lockPixels.
REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
} else {
// Lazy decoding
REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
SkLazyPixelRef* lazyRef = static_cast<SkLazyPixelRef*>(pixelRef);
intptr_t cacheID = lazyRef->getCacheId();
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
!= SkImageCache::kPinned_MemoryStatus);
{
SkAutoLockPixels alp(*bitmapFromFactory.get());
REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
cacheID = lazyRef->getCacheId();
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
== SkImageCache::kPinned_MemoryStatus);
}
REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
!= SkImageCache::kPinned_MemoryStatus);
bitmapFromFactory.free();
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
== SkImageCache::kFreed_MemoryStatus);
}
}
class ImageCacheHolder : public SkNoncopyable {
public:
~ImageCacheHolder() {
fCaches.safeUnrefAll();
}
void addImageCache(SkImageCache* cache) {
SkSafeRef(cache);
*fCaches.append() = cache;
}
int count() const { return fCaches.count(); }
SkImageCache* getAt(int i) {
if (i < 0 || i > fCaches.count()) {
return NULL;
}
return fCaches.getAt(i);
}
private:
SkTDArray<SkImageCache*> fCaches;
};
static void TestBitmapFactory(skiatest::Reporter* reporter) {
SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
SkASSERT(bitmap.get() != NULL);
SkAutoDataUnref encodedBitmap(create_data_from_bitmap(*bitmap.get()));
bool encodeSucceeded = encodedBitmap.get() != NULL;
SkASSERT(encodeSucceeded);
ImageCacheHolder cacheHolder;
SkAutoTUnref<SkLruImageCache> lruCache(SkNEW_ARGS(SkLruImageCache, (1024 * 1024)));
cacheHolder.addImageCache(lruCache);
cacheHolder.addImageCache(NULL);
SkImageCache* purgeableCache = SkPurgeableImageCache::Create();
if (purgeableCache != NULL) {
cacheHolder.addImageCache(purgeableCache);
purgeableCache->unref();
}
for (int i = 0; i < cacheHolder.count(); i++) {
SkImageCache* cache = cacheHolder.getAt(i);
if (cache != NULL) {
test_cache(reporter, cache);
}
if (encodeSucceeded) {
test_factory(reporter, cache, encodedBitmap, *bitmap.get());
}
}
}
#include "TestClassDef.h"
DEFINE_TESTCLASS("BitmapFactory", TestBitmapFactoryClass, TestBitmapFactory)
#endif // SK_DEBUG

View File

@ -9,12 +9,13 @@
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDecodingImageGenerator.h"
#include "SkDiscardablePixelRef.h"
#include "SkDiscardableMemoryPool.h"
#include "SkImageDecoder.h"
#include "SkImagePriv.h"
#include "SkLazyPixelRef.h"
#include "SkCachingPixelRef.h"
#include "SkScaledImageCache.h"
#include "SkStream.h"
#include "SkUtils.h"
#include "Test.h"
#include "TestClassDef.h"
@ -50,23 +51,7 @@ static SkData* create_data_from_bitmap(const SkBitmap& bm,
return NULL;
}
/**
* A simplified version of SkBitmapFactory
*/
static bool simple_bitmap_factory(SkBitmapFactory::DecodeProc proc,
SkData* data,
SkBitmap* dst) {
SkImageInfo info;
if (!proc(data->data(), data->size(), &info, NULL)) {
return false;
}
dst->setConfig(SkImageInfoToBitmapConfig(info), info.fWidth,
info.fHeight, 0, info.fAlphaType);
SkAutoTUnref<SkLazyPixelRef> ref(SkNEW_ARGS(SkLazyPixelRef,
(data, proc, NULL)));
dst->setPixelRef(ref);
return true;
}
////////////////////////////////////////////////////////////////////////////////
static void compare_bitmaps(skiatest::Reporter* reporter,
const SkBitmap& b1, const SkBitmap& b2,
@ -105,7 +90,6 @@ static void compare_bitmaps(skiatest::Reporter* reporter,
REPORTER_ASSERT(reporter, 0 == pixelErrors);
}
typedef bool (*InstallEncoded)(SkData* encoded, SkBitmap* dst);
/**
@ -160,36 +144,169 @@ static void test_three_encodings(skiatest::Reporter* reporter,
}
}
////////////////////////////////////////////////////////////////////////////////
/**
* This checks to see that a SkLazyPixelRef works as advertised.
*/
static bool install_skLazyPixelRef(SkData* encoded, SkBitmap* dst) {
static const SkBitmapFactory::DecodeProc decoder =
&(SkImageDecoder::DecodeMemoryToTarget);
return simple_bitmap_factory(decoder, encoded, dst);
}
DEF_TEST(LazyPixelRef, reporter) {
test_three_encodings(reporter, install_skLazyPixelRef);
}
////////////////////////////////////////////////////////////////////////////////
/**
* This checks to see that a SkCachingPixelRef works as advertised.
*/
static bool install_skCachingPixelRef(SkData* encoded, SkBitmap* dst) {
return SkCachingPixelRef::Install(
SkNEW_ARGS(SkDecodingImageGenerator, (encoded)), dst);
}
DEF_TEST(CachingPixelRef, reporter) {
test_three_encodings(reporter, install_skCachingPixelRef);
static bool install_skDiscardablePixelRef(SkData* encoded, SkBitmap* dst) {
// Use system-default discardable memory.
return SkDecodingImageGenerator::Install(encoded, dst, NULL);
}
////////////////////////////////////////////////////////////////////////////////
/**
* This checks to see that a SkDecodingImageGenerator works as advertised.
* This checks to see that a SkCachingPixelRef and a
* SkDiscardablePixelRef works as advertised with a
* SkDecodingImageGenerator.
*/
DEF_TEST(DecodingImageGenerator, reporter) {
test_three_encodings(reporter, SkDecodingImageGenerator::Install);
test_three_encodings(reporter, install_skCachingPixelRef);
test_three_encodings(reporter, install_skDiscardablePixelRef);
}
////////////////////////////////////////////////////////////////////////////////
namespace {
class TestImageGenerator : public SkImageGenerator {
public:
enum TestType {
kFailGetInfo_TestType,
kFailGetPixels_TestType,
kSucceedGetPixels_TestType,
kLast_TestType = kSucceedGetPixels_TestType
};
static int Width() { return 10; }
static int Height() { return 10; }
static SkColor Color() { return SK_ColorCYAN; }
TestImageGenerator(TestType type, skiatest::Reporter* reporter)
: fType(type), fReporter(reporter) {
SkASSERT((fType <= kLast_TestType) && (fType >= 0));
}
~TestImageGenerator() { }
bool getInfo(SkImageInfo* info) SK_OVERRIDE {
REPORTER_ASSERT(fReporter, NULL != info);
if ((NULL == info) || (kFailGetInfo_TestType == fType)) {
return false;
}
info->fWidth = TestImageGenerator::Width();
info->fHeight = TestImageGenerator::Height();
info->fColorType = kPMColor_SkColorType;
info->fAlphaType = kOpaque_SkAlphaType;
return true;
}
bool getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) SK_OVERRIDE {
REPORTER_ASSERT(fReporter, pixels != NULL);
size_t minRowBytes
= static_cast<size_t>(info.fWidth * info.bytesPerPixel());
REPORTER_ASSERT(fReporter, rowBytes >= minRowBytes);
if ((NULL == pixels)
|| (fType != kSucceedGetPixels_TestType)
|| (info.fColorType != kPMColor_SkColorType)) {
return false;
}
char* bytePtr = static_cast<char*>(pixels);
for (int y = 0; y < info.fHeight; ++y) {
sk_memset32(reinterpret_cast<SkColor*>(bytePtr),
TestImageGenerator::Color(), info.fWidth);
bytePtr += rowBytes;
}
return true;
}
private:
const TestType fType;
skiatest::Reporter* const fReporter;
};
void CheckTestImageGeneratorBitmap(skiatest::Reporter* reporter,
const SkBitmap& bm) {
REPORTER_ASSERT(reporter, TestImageGenerator::Width() == bm.width());
REPORTER_ASSERT(reporter, TestImageGenerator::Height() == bm.height());
SkAutoLockPixels autoLockPixels(bm);
REPORTER_ASSERT(reporter, NULL != bm.getPixels());
if (NULL == bm.getPixels()) {
return;
}
int errors = 0;
for (int y = 0; y < bm.height(); ++y) {
for (int x = 0; x < bm.width(); ++x) {
if (TestImageGenerator::Color() != *bm.getAddr32(x, y)) {
++errors;
}
}
}
REPORTER_ASSERT(reporter, 0 == errors);
}
enum PixelRefType {
kSkCaching_PixelRefType,
kSkDiscardable_PixelRefType,
kLast_PixelRefType = kSkDiscardable_PixelRefType
};
void CheckPixelRef(TestImageGenerator::TestType type,
skiatest::Reporter* reporter,
PixelRefType pixelRefType,
SkDiscardableMemory::Factory* factory) {
SkASSERT((pixelRefType >= 0) && (pixelRefType <= kLast_PixelRefType));
SkAutoTDelete<SkImageGenerator> gen(SkNEW_ARGS(TestImageGenerator,
(type, reporter)));
REPORTER_ASSERT(reporter, gen.get() != NULL);
SkBitmap lazy;
bool success;
if (kSkCaching_PixelRefType == pixelRefType) {
// Ignore factory; use global SkScaledImageCache.
success = SkCachingPixelRef::Install(gen.detach(), &lazy);
} else {
success = SkDiscardablePixelRef::Install(gen.detach(), &lazy, factory);
}
REPORTER_ASSERT(reporter, success
== (TestImageGenerator::kFailGetInfo_TestType != type));
if (TestImageGenerator::kSucceedGetPixels_TestType == type) {
CheckTestImageGeneratorBitmap(reporter, lazy);
} else if (TestImageGenerator::kFailGetPixels_TestType == type) {
SkAutoLockPixels autoLockPixels(lazy);
REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
}
}
} // namespace
/**
* This tests the basic functionality of SkDiscardablePixelRef with a
* basic SkImageGenerator implementation and several
* SkDiscardableMemory::Factory choices.
*/
DEF_TEST(DiscardableAndCachingPixelRef, reporter) {
CheckPixelRef(TestImageGenerator::kFailGetInfo_TestType,
reporter, kSkCaching_PixelRefType, NULL);
CheckPixelRef(TestImageGenerator::kFailGetPixels_TestType,
reporter, kSkCaching_PixelRefType, NULL);
CheckPixelRef(TestImageGenerator::kSucceedGetPixels_TestType,
reporter, kSkCaching_PixelRefType, NULL);
CheckPixelRef(TestImageGenerator::kFailGetInfo_TestType,
reporter, kSkDiscardable_PixelRefType, NULL);
CheckPixelRef(TestImageGenerator::kFailGetPixels_TestType,
reporter, kSkDiscardable_PixelRefType, NULL);
CheckPixelRef(TestImageGenerator::kSucceedGetPixels_TestType,
reporter, kSkDiscardable_PixelRefType, NULL);
SkAutoTUnref<SkDiscardableMemoryPool> pool(
SkNEW_ARGS(SkDiscardableMemoryPool, (1, NULL)));
REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
CheckPixelRef(TestImageGenerator::kFailGetPixels_TestType,
reporter, kSkDiscardable_PixelRefType, pool);
REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
CheckPixelRef(TestImageGenerator::kSucceedGetPixels_TestType,
reporter, kSkDiscardable_PixelRefType, pool);
REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
SkDiscardableMemoryPool* globalPool = SkGetGlobalDiscardableMemoryPool();
CheckPixelRef(TestImageGenerator::kFailGetPixels_TestType,
reporter, kSkDiscardable_PixelRefType, globalPool);
CheckPixelRef(TestImageGenerator::kSucceedGetPixels_TestType,
reporter, kSkDiscardable_PixelRefType, globalPool);
// TODO(halcanary): When ashmem-backed SkDiscardableMemory lands,
// test that here (on platforms where it is availible).
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,37 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkDiscardableMemoryPool.h"
#include "Test.h"
#include "TestClassDef.h"
DEF_TEST(DiscardableMemoryPool, reporter) {
SkAutoTUnref<SkDiscardableMemoryPool> pool(
SkNEW_ARGS(SkDiscardableMemoryPool, (1, NULL)));
pool->setRAMBudget(3);
REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
SkAutoTDelete<SkDiscardableMemory> dm1(pool->create(100));
REPORTER_ASSERT(reporter, dm1->data() != NULL);
REPORTER_ASSERT(reporter, 100 == pool->getRAMUsed());
dm1->unlock();
REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
REPORTER_ASSERT(reporter, !dm1->lock());
SkAutoTDelete<SkDiscardableMemory> dm2(pool->create(200));
REPORTER_ASSERT(reporter, 200 == pool->getRAMUsed());
pool->setRAMBudget(400);
dm2->unlock();
REPORTER_ASSERT(reporter, 200 == pool->getRAMUsed());
REPORTER_ASSERT(reporter, dm2->lock());
dm2->unlock();
pool->dumpPool();
REPORTER_ASSERT(reporter, !dm2->lock());
REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
}

View File

@ -9,41 +9,47 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDiscardableMemoryPool.h"
#include "SkDiscardablePixelRef.h"
#include "SkPaint.h"
#include "SkShader.h"
#include "SkSurface.h"
#include "SkRandom.h"
#include "SkMatrixUtils.h"
#include "SkLazyPixelRef.h"
#include "SkLruImageCache.h"
namespace {
// A BitmapFactory that always fails when asked to return pixels.
static bool FailureDecoder(const void* data, size_t length, SkImageInfo* info,
const SkBitmapFactory::Target* target) {
if (info) {
info->fWidth = info->fHeight = 100;
info->fColorType = kRGBA_8888_SkColorType;
class FailureImageGenerator : public SkImageGenerator {
public:
FailureImageGenerator() { }
virtual ~FailureImageGenerator() { }
virtual bool getInfo(SkImageInfo* info) {
info->fWidth = 100;
info->fHeight = 100;
info->fColorType = kPMColor_SkColorType;
info->fAlphaType = kPremul_SkAlphaType;
return true;
}
// this will deliberately return false if they are asking us to decode
// into pixels.
return NULL == target;
}
virtual bool getPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes) SK_OVERRIDE {
// this will deliberately return false if they are asking us
// to decode into pixels.
return false;
}
};
} // namespace
// crbug.com/295895
// Crashing in skia when a pixelref fails in lockPixels
//
static void test_faulty_pixelref(skiatest::Reporter* reporter) {
// need a cache, but don't expect to use it, so the budget is not critical
SkLruImageCache cache(10 * 1000);
// construct a garbage data represent "bad" encoded data.
SkAutoDataUnref data(SkData::NewFromMalloc(malloc(1000), 1000));
SkAutoTUnref<SkPixelRef> pr(new SkLazyPixelRef(data, FailureDecoder, &cache));
SkAutoTUnref<SkDiscardableMemoryPool> pool(SkNEW_ARGS(SkDiscardableMemoryPool,
(10 * 1000, NULL)));
SkBitmap bm;
bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
bm.setPixelRef(pr);
bool installSuccess = SkDiscardablePixelRef::Install(SkNEW(FailureImageGenerator), &bm, pool);
REPORTER_ASSERT(reporter, installSuccess);
// now our bitmap has a pixelref, but we know it will fail to lock
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(200, 200));

View File

@ -7,70 +7,41 @@
#include "LazyDecodeBitmap.h"
#include "PictureRenderingFlags.h" // --deferImageDecoding is defined here.
#include "SkBitmap.h"
#include "SkData.h"
#include "SkImageDecoder.h"
#include "SkDecodingImageGenerator.h"
#include "SkDiscardableMemoryPool.h"
#include "SkDiscardablePixelRef.h"
#include "SkForceLinking.h"
#include "SkLruImageCache.h"
#include "SkPurgeableImageCache.h"
#include "SkCommandLineFlags.h"
__SK_FORCE_IMAGE_DECODER_LINKING;
// TODO(halcanary) Use this flag when ashmem-backed discardable memory lands.
DEFINE_bool(useVolatileCache, false, "Use a volatile cache for deferred image decoding pixels. "
"Only meaningful if --deferImageDecoding is set to true and the platform has an "
"implementation.");
SkLruImageCache gLruImageCache(1024 * 1024);
namespace sk_tools {
// Simple cache selector to choose between a purgeable cache for large images and the standard one
// for smaller images.
//
class CacheSelector : public SkBitmapFactory::CacheSelector {
public:
CacheSelector() {
fPurgeableImageCache = SkPurgeableImageCache::Create();
// Fits SkPicture::InstallPixelRefProc call signature.
// Used in SkPicturePlayback::CreateFromStream
bool sk_tools::LazyDecodeBitmap(const void* src,
size_t length,
SkBitmap* dst) {
SkAutoDataUnref data(SkData::NewWithCopy(src, length));
if (NULL == data.get()) {
return false;
}
~CacheSelector() {
SkSafeUnref(fPurgeableImageCache);
SkAutoTDelete<SkImageGenerator> gen(SkNEW_ARGS(SkDecodingImageGenerator,
(data)));
SkImageInfo info;
if (!gen->getInfo(&info)) {
return false;
}
virtual SkImageCache* selectCache(const SkImageInfo& info) SK_OVERRIDE {
if (info.fWidth * info.fHeight > 32 * 1024 && fPurgeableImageCache != NULL) {
return fPurgeableImageCache;
}
return &gLruImageCache;
SkDiscardableMemory::Factory* pool = NULL;
if (info.fWidth * info.fHeight > 32 * 1024) {
// how to do switching with SkDiscardableMemory.
pool = SkGetGlobalDiscardableMemoryPool();
}
private:
SkImageCache* fPurgeableImageCache;
};
static CacheSelector gCacheSelector;
static SkBitmapFactory gFactory(&SkImageDecoder::DecodeMemoryToTarget);
bool LazyDecodeBitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
void* copiedBuffer = sk_malloc_throw(size);
memcpy(copiedBuffer, buffer, size);
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
static bool gOnce;
if (!gOnce) {
// Only use the cache selector if there is a purgeable image cache to use for large
// images.
if (FLAGS_useVolatileCache && SkAutoTUnref<SkImageCache>(
SkPurgeableImageCache::Create()).get() != NULL) {
gFactory.setCacheSelector(&gCacheSelector);
} else {
gFactory.setImageCache(&gLruImageCache);
}
gOnce = true;
}
return gFactory.installPixelRef(data, bitmap);
return SkDiscardablePixelRef::Install(gen.detach(), dst, pool);
}
} // namespace sk_tools

View File

@ -10,7 +10,6 @@
#include "CopyTilesRenderer.h"
#include "PictureRenderer.h"
#include "picture_utils.h"
#include "SkBitmapFactory.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
#include "SkImage.h"

View File

@ -12,12 +12,9 @@
#include "PictureRenderingFlags.h"
#include "SkBenchLogger.h"
#include "SkCommandLineFlags.h"
#include "SkDiscardableMemoryPool.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#if LAZY_CACHE_STATS
#include "SkLazyPixelRef.h"
#endif
#include "SkLruImageCache.h"
#include "SkMath.h"
#include "SkOSFile.h"
#include "SkPicture.h"
@ -143,9 +140,6 @@ static SkString filterFlagsUsage() {
return result;
}
// Defined in LazyDecodeBitmap.cpp
extern SkLruImageCache gLruImageCache;
#if LAZY_CACHE_STATS
static int32_t gTotalCacheHits;
static int32_t gTotalCacheMisses;
@ -163,11 +157,12 @@ static bool run_single_benchmark(const SkString& inputPath,
return false;
}
SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
// Since the old picture has been deleted, all pixels should be cleared.
SkASSERT(gLruImageCache.getImageCacheUsed() == 0);
SkASSERT(pool->getRAMUsed() == 0);
if (FLAGS_countRAM) {
// Set the limit to zero, so all pixels will be kept
gLruImageCache.setImageCacheLimit(0);
pool->setRAMBudget(SK_MaxU32);
// Set the limit to max, so all pixels will be kept
}
SkPicture::InstallPixelRefProc proc;
@ -197,9 +192,9 @@ static bool run_single_benchmark(const SkString& inputPath,
#if LAZY_CACHE_STATS
if (FLAGS_trackDeferredCaching) {
int32_t cacheHits = SkLazyPixelRef::GetCacheHits();
int32_t cacheMisses = SkLazyPixelRef::GetCacheMisses();
SkLazyPixelRef::ResetCacheStats();
int32_t cacheHits = pool->fCacheHits;
int32_t cacheMisses = pool->fCacheMisses;
pool->fCacheHits = pool->fCacheMisses = 0;
SkString hitString;
hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses));
gLogger.logProgress(hitString);
@ -209,7 +204,7 @@ static bool run_single_benchmark(const SkString& inputPath,
#endif
if (FLAGS_countRAM) {
SkString ramCount("RAM used for bitmaps: ");
size_t bytes = gLruImageCache.getImageCacheUsed();
size_t bytes = pool->getRAMUsed();
if (bytes > 1024) {
size_t kb = bytes / 1024;
if (kb > 1024) {