Create SkLazyPixelRef which performs lazy decoding.

The new pixel ref behaves similarly to SkImageRef, with some key differences:
It does not depend on the images project.
It requires an SkImageCache, which handles allocation and caching of the pixel
memory.
It takes a function signature for decoding which decodes into already allocated
pixel memory rather than into an SkBitmap.

Add two implementations of SkImageCache: SkLruImageCache and SkAshmemImageCache.

Replace SkSerializationHelpers::DecodeBitmap with SkPicture::InstallPixelRefProc,
and update sites that referenced it.

SkBitmapFactory now sets the pixel ref to a new object of the new
class SkLazyPixelRef, provided it has an SkImageCache for caching.

Provide an option to do lazy decodes in render_pictures and bench_pictures.

SkPicture:
Eliminate the default parameters in the constructor.
If a proc for decoding bitmaps is installed, use it to decode any encoded
data in subpictures.
When parsing deserializing subpictures, check for success.
When serializing subpictures, pass the picture's bitmap encoder to the
subpicture's call to serialize.

Update BitmapFactoryTest to test its new behavior.

BUG=https://code.google.com/p/skia/issues/detail?id=1008
BUG=https://code.google.com/p/skia/issues/detail?id=1009

Review URL: https://codereview.appspot.com/7060052

git-svn-id: http://skia.googlecode.com/svn/trunk@7835 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
scroggo@google.com 2013-02-22 21:38:35 +00:00
parent 5c90e29142
commit f8d7d27313
36 changed files with 1268 additions and 187 deletions

View File

@ -144,8 +144,7 @@ void SkDebuggerGUI::showDeletes() {
class SkTimedPicturePlayback : public SkPicturePlayback {
public:
SkTimedPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
SkSerializationHelpers::DecodeBitmap decoder,
const SkTDArray<size_t>& offsets,
SkPicture::InstallPixelRefProc proc, const SkTDArray<size_t>& offsets,
const SkTDArray<bool>& deletedCommands)
: INHERITED(stream, info, isValid, decoder)
, fOffsets(offsets)
@ -251,9 +250,7 @@ private:
// Wrap SkPicture to allow installation of an SkTimedPicturePlayback object
class SkTimedPicture : public SkPicture {
public:
explicit SkTimedPicture(SkStream* stream,
bool* success,
SkSerializationHelpers::DecodeBitmap decoder,
explicit SkTimedPicture(SkStream* stream, bool* success, SkPicture::InstallPixelRefProc,
const SkTDArray<size_t>& offsets,
const SkTDArray<bool>& deletedCommands) {
if (success) {
@ -362,7 +359,7 @@ void SkDebuggerGUI::actionProfile() {
}
bool success = false;
SkTimedPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream,
SkTimedPicture picture(&inputStream, &success, &SkImageDecoder::DecodeMemory,
fOffsets, fSkipCommands);
if (!success) {
return;
@ -928,7 +925,7 @@ void SkDebuggerGUI::setupDirectoryWidget(const QString& path) {
class SkOffsetPicturePlayback : public SkPicturePlayback {
public:
SkOffsetPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
SkSerializationHelpers::DecodeBitmap decoder)
SkPicture::InstallPixelRefProc proc)
: INHERITED(stream, info, isValid, decoder) {
}
@ -949,9 +946,7 @@ private:
// Picture to wrap an SkOffsetPicturePlayback.
class SkOffsetPicture : public SkPicture {
public:
SkOffsetPicture(SkStream* stream,
bool* success,
SkSerializationHelpers::DecodeBitmap decoder) {
SkOffsetPicture(SkStream* stream, bool* success, SkPicture::InstallPixelRefProc proc) {
if (success) {
*success = false;
}
@ -1008,7 +1003,8 @@ void SkDebuggerGUI::loadPicture(const SkString& fileName) {
bool success = false;
SkOffsetPicture* picture = SkNEW_ARGS(SkOffsetPicture, (stream, &success, &SkImageDecoder::DecodeStream));
SkOffsetPicture* picture = SkNEW_ARGS(SkOffsetPicture,
(stream, &success, &SkImageDecoder::DecodeMemory));
if (!success) {
QMessageBox::critical(this, "Error loading file", "Couldn't read file, sorry.");

View File

@ -9,6 +9,8 @@
#include "SkBitmapFactory.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkImageDecoder.h"
#include "SkLruImageCache.h"
#include "SkStream.h"
namespace skiagm {
@ -37,7 +39,12 @@ protected:
void* buffer = sk_malloc_throw(length);
stream.read(buffer, length);
SkAutoDataUnref data(SkData::NewFromMalloc(buffer, length));
SkBitmapFactory::DecodeBitmap(&fBitmap, data);
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
// Create a cache which will boot the pixels out anytime the
// bitmap is unlocked.
SkAutoTUnref<SkLruImageCache> cache(SkNEW_ARGS(SkLruImageCache, (0)));
factory.setImageCache(cache);
factory.installPixelRef(data, &fBitmap);
}
}

View File

@ -10,14 +10,15 @@
'type': 'static_library',
'standalone_static_library': 1,
'include_dirs': [
'../include/animator',
'../include/config',
'../include/core',
'../include/effects',
'../include/animator',
'../include/images',
'../include/lazy',
'../include/utils',
'../include/views',
'../include/xml',
'../include/utils',
'../include/images',
'../src/utils',
],
'sources': [

View File

@ -15,6 +15,7 @@
'include_dirs': [
'../include/config',
'../include/core',
'../include/lazy',
'../include/pipe',
'../include/ports',
'../include/utils',
@ -106,6 +107,7 @@
'config',
'../include/config',
'../include/core',
'../include/lazy',
'../include/pipe',
'ext',
],

View File

@ -281,6 +281,16 @@
'<(skia_include_path)/core/SkWeakRefCnt.h',
'<(skia_include_path)/core/SkWriter32.h',
'<(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_src_path)/lazy/SkBitmapFactory.cpp',
'<(skia_src_path)/lazy/SkLazyPixelRef.h',
'<(skia_src_path)/lazy/SkLazyPixelRef.cpp',
'<(skia_src_path)/lazy/SkLruImageCache.cpp',
],
}

View File

@ -16,9 +16,11 @@
'../include/config',
'../include/core',
'../include/images',
'../include/lazy',
# for access to SkImagePriv.h
'../src/image/',
],
'sources': [
'../include/images/SkBitmapFactory.h',
'../include/images/SkImageDecoder.h',
'../include/images/SkImageEncoder.h',
'../include/images/SkImageRef.h',
@ -29,7 +31,6 @@
'../src/images/bmpdecoderhelper.cpp',
'../src/images/bmpdecoderhelper.h',
'../src/images/SkBitmapFactory.cpp',
'../src/images/SkFDStream.cpp',
'../src/images/SkImageDecoder.cpp',
'../src/images/SkImageDecoder_Factory.cpp',
@ -136,6 +137,7 @@
'direct_dependent_settings': {
'include_dirs': [
'../include/images',
'../include/lazy',
],
},
},

View File

@ -155,12 +155,15 @@
'../src/ports/SkDebug_stdio.cpp',
],
'sources': [
'../include/ports/SkAshmemImageCache.h',
'../src/ports/SkDebug_android.cpp',
'../src/ports/SkThread_pthread.cpp',
'../src/ports/SkFontHost_android.cpp',
'../src/ports/SkFontHost_FreeType.cpp',
'../src/ports/SkFontHost_FreeType_common.cpp',
'../src/ports/FontHostConfiguration_android.cpp',
'../src/ports/SkAshmemImageCache.cpp',
],
'dependencies': [
'freetype.gyp:freetype',

View File

@ -10,6 +10,7 @@
'include_dirs' : [
'../src/core',
'../src/effects',
'../src/lazy',
'../src/pdf',
'../src/pipe/utils',
'../src/utils',

View File

@ -14,12 +14,13 @@
'include_dirs': [
'../include/config',
'../include/core',
'../include/views',
'../include/xml',
'../include/utils',
'../include/images',
'../include/effects',
'../include/images',
'../include/lazy',
'../include/utils',
'../include/views',
'../include/views/unix',
'../include/xml',
],
'dependencies': [
'angle.gyp:*',

View File

@ -6,16 +6,17 @@
'target_name': 'views_animated',
'type': 'static_library',
'include_dirs': [
'../include/animator',
'../include/config',
'../include/core',
'../include/views',
'../include/xml',
'../include/utils',
'../include/images',
'../include/animator',
'../include/effects',
'../include/views/unix',
'../include/images',
'../include/lazy',
'../include/utils',
'../include/views',
'../include/views/animated',
'../include/views/unix',
'../include/xml',
],
'sources': [
'../include/views/animated/SkBorderView.h',

View File

@ -10,11 +10,11 @@
#ifndef SkPicture_DEFINED
#define SkPicture_DEFINED
#include "SkBitmap.h"
#include "SkRefCnt.h"
#include "SkSerializationHelpers.h"
class SkBBoxHierarchy;
class SkBitmap;
class SkCanvas;
class SkPicturePlayback;
class SkPictureRecord;
@ -39,13 +39,37 @@ public:
this call, those elements will not appear in this picture.
*/
SkPicture(const SkPicture& src);
/**
* Recreate a picture that was serialized into a stream. *success is set to
* true if the picture was deserialized successfully and false otherwise.
* decoder is used to decode any SkBitmaps that were encoded into the stream.
* Recreate a picture that was serialized into a stream.
* On failure, silently creates an empty picture.
* @param SkStream Serialized picture data.
*/
explicit SkPicture(SkStream*, bool* success = NULL,
SkSerializationHelpers::DecodeBitmap decoder = NULL);
explicit SkPicture(SkStream*);
/**
* Function signature defining a function that sets up an SkBitmap from encoded data. On
* success, the SkBitmap should have its Config, width, height, rowBytes and pixelref set.
* If the installed pixelref has decoded the data into pixels, then the src buffer need not be
* copied. If the pixelref defers the actual decode until its lockPixels() is called, then it
* must make a copy of the src buffer.
* @param src Encoded data.
* @param length Size of the encoded data, in bytes.
* @param dst SkBitmap to install the pixel ref on.
* @param bool Whether or not a pixel ref was successfully installed.
*/
typedef bool (*InstallPixelRefProc)(const void* src, size_t length, SkBitmap* dst);
/**
* Recreate a picture that was serialized into a stream.
* @param SkStream Serialized picture data.
* @param success Output parameter. If non-NULL, will be set to true if the picture was
* deserialized successfully and false otherwise.
* @param proc Function pointer for installing pixelrefs on SkBitmaps representing the
* encoded bitmap data from the stream.
*/
SkPicture(SkStream*, bool* success, InstallPixelRefProc proc);
virtual ~SkPicture();
/**
@ -176,6 +200,7 @@ protected:
virtual SkBBoxHierarchy* createBBoxHierarchy() const;
private:
void initFromStream(SkStream*, bool* success, InstallPixelRefProc);
friend class SkFlatPicture;
friend class SkPicturePlayback;

View File

@ -21,15 +21,6 @@ namespace SkSerializationHelpers {
* another method of storing SkBitmaps.
*/
typedef bool (*EncodeBitmap)(SkWStream*, const SkBitmap&);
/**
* Function to decode an SkBitmap from an SkStream. A function with this signature can be
* passed to the SkStream constructor for SkPicture and SkOrderedReadBuffer to decode SkBitmaps
* which were previously encoded. The function should return true if it succeeds. Otherwise it
* should return false so that SkOrderedReadBuffer can skip the data and provide a dummy
* SkBitmap.
*/
typedef bool (*DecodeBitmap)(SkStream*, SkBitmap*);
}
#endif // SkSerializationHelpers_DEFINED

View File

@ -1,46 +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
class SkBitmap;
class SkData;
/**
* General purpose factory for decoding bitmaps.
*
* Currently only provides a way to decode a bitmap or its dimensions from an SkData. Future plans
* include options to provide a bitmap which caches the pixel data.
*/
class SkBitmapFactory {
public:
enum Constraints {
/**
* Only decode the bounds of the bitmap. No pixels will be allocated.
*/
kDecodeBoundsOnly_Constraint,
/**
* Decode the bounds and pixels of the bitmap.
*/
kDecodePixels_Constraint,
};
/**
* Decodes an SkData into an SkBitmap.
* @param SkBitmap Already created bitmap to encode into.
* @param SkData Encoded SkBitmap data.
* @param constraint Specifications for how to do the decoding.
* @return True on success. If false, passed in SkBitmap is unmodified.
*/
static bool DecodeBitmap(SkBitmap*, const SkData*,
Constraints constraint = kDecodePixels_Constraint);
};
#endif // SkBitmapFactory_DEFINED

View File

@ -11,6 +11,8 @@
#define SkImageDecoder_DEFINED
#include "SkBitmap.h"
#include "SkBitmapFactory.h"
#include "SkImage.h"
#include "SkRefCnt.h"
class SkStream;
@ -223,6 +225,34 @@ public:
return DecodeMemory(buffer, size, bitmap, SkBitmap::kNo_Config,
kDecodePixels_Mode, NULL);
}
/**
* Decode memory.
* @param info Output parameter. Returns info about the encoded image.
* @param target Contains the address of pixel memory to decode into
* (which must be large enough to hold the width in info) and
* the row bytes to use. If NULL, returns info and does not
* decode pixels.
* @return bool Whether the function succeeded.
*
* Sample usage:
* <code>
* // Determine the image's info: width/height/config
* SkImage::Info info;
* bool success = DecodeMemoryToTarget(src, size, &info, NULL);
* if (!success) return;
* // Allocate space for the result:
* SkBitmapFactory::Target target;
* target.fAddr = malloc/other allocation
* target.fRowBytes = ...
* // Now decode the actual pixels into target. &info is optional,
* // and could be NULL
* success = DecodeMemoryToTarget(src, size, &info, &target);
* </code>
*/
static bool DecodeMemoryToTarget(const void* buffer, size_t size, SkImage::Info* info,
const SkBitmapFactory::Target* target);
/** Decode the image stored in the specified SkStream, and store the result
in bitmap. Return true for success or false on failure.

View File

@ -0,0 +1,86 @@
/*
* 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, SkImage::Info*, 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*);
/**
* A function for selecting an SkImageCache to use based on an SkImage::Info.
*/
typedef SkImageCache* (*CacheSelector)(const SkImage::Info&);
/**
* 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

@ -0,0 +1,76 @@
/*
* 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:
/**
* 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, intptr_t* ID) = 0;
/**
* Re-request the memory associated with ID.
* @param ID Unique ID for the memory block.
* @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, so allocAndPinCache must be called again, and ID is no
* longer valid (thus throwAwayCache need not be called).
*/
virtual void* pinCache(intptr_t ID) = 0;
/**
* Inform the cache that it is safe to free the block of memory corresponding to ID. After
* calling this function, the pointer returnted by allocAndPinCache or pinCache must not be
* used again. In order to access the same memory after this, pinCache must be called.
* @param ID Unique ID for the memory block which is now safe to age out of the cache.
*/
virtual void releaseCache(intptr_t 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, unless a later pinCache
* returns NULL.
*/
virtual void throwAwayCache(intptr_t ID) = 0;
/**
* ID which does not correspond to any valid cache.
*/
static const intptr_t UNINITIALIZED_ID = 0;
#ifdef SK_DEBUG
enum CacheStatus {
kPinned_CacheStatus,
kUnpinned_CacheStatus,
kThrownAway_CacheStatus,
};
/**
* Debug only function to get the status of a particular block of memory.
*/
virtual CacheStatus getCacheStatus(intptr_t ID) const = 0;
#endif
};
#endif // SkImageCache_DEFINED

View File

@ -0,0 +1,74 @@
/*
* 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:
SkLruImageCache(size_t budget);
virtual ~SkLruImageCache();
#ifdef SK_DEBUG
CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
#endif
void setBudget(size_t newBudget);
virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
virtual void throwAwayCache(intptr_t 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 getCacheStatus 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(intptr_t 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*);
};
#endif // SkLruImageCache_DEFINED

View File

@ -0,0 +1,73 @@
/*
* 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 SkAshmemImageCache_DEFINED
#define SkAshmemImageCache_DEFINED
#include "SkImageCache.h"
#include "SkTDArray.h"
#include "SkTypes.h"
class SkAshmemImageCache : public SkImageCache {
public:
/**
* Get a pointer to the single global instance of SkAshmemImageCache.
*/
static SkAshmemImageCache* GetAshmemImageCache();
virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
#ifdef SK_DEBUG
SkImageCache::CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
virtual ~SkAshmemImageCache();
#endif
private:
struct AshmemRec {
int fFD;
void* fAddr;
size_t fSize;
#ifdef SK_DEBUG
bool fPinned;
static int Compare(const AshmemRec*, const AshmemRec*);
#endif
};
/**
* Constructor is private. The correct way to get this cache is through
* GetAshmemImageCache, so that all callers can get the single global.
*/
SkAshmemImageCache();
#ifdef SK_DEBUG
// Stores a list of AshmemRecs to track deletion.
SkTDArray<AshmemRec*> fRecs;
/**
* Debug only function to add an AshmemRec to the list.
*/
void appendRec(AshmemRec*);
/**
* Return the index of AshmemRec.
*/
int findRec(const AshmemRec*) const;
#endif
/**
* Deletes AshmemRec. In debug, also removes from the list.
*/
void removeRec(AshmemRec*);
};
#endif // SkAshmemImageCache_DEFINED

View File

@ -48,8 +48,7 @@ class PictFileView : public SampleView {
} else {
SkFILEStream stream(path);
if (stream.isValid()) {
pic = SkNEW_ARGS(SkPicture,
(&stream, NULL, &SkImageDecoder::DecodeStream));
pic = SkNEW_ARGS(SkPicture, (&stream, NULL, &SkImageDecoder::DecodeMemory));
}
if (false) { // re-record

View File

@ -6,6 +6,7 @@
* found in the LICENSE file.
*/
#include "SkBitmap.h"
#include "SkOrderedReadBuffer.h"
#include "SkStream.h"
#include "SkTypeface.h"
@ -167,19 +168,18 @@ uint32_t SkOrderedReadBuffer::getArrayCount() {
}
void SkOrderedReadBuffer::readBitmap(SkBitmap* bitmap) {
size_t length = this->readUInt();
const size_t length = this->readUInt();
if (length > 0) {
// Bitmap was encoded.
SkMemoryStream stream(const_cast<void*>(this->skip(length)), length, false);
if (fBitmapDecoder != NULL && fBitmapDecoder(&stream, bitmap)) {
// Skip the width and height, which were written in case of failure.
fReader.skip(2 * sizeof(int));
const void* data = this->skip(length);
const int width = this->readInt();
const int height = this->readInt();
if (fBitmapDecoder != NULL && fBitmapDecoder(data, length, bitmap)) {
SkASSERT(bitmap->width() == width && bitmap->height() == height);
} else {
// This bitmap was encoded when written, but we are unable to decode, possibly due to
// not having a decoder. Use a placeholder bitmap.
SkDebugf("Could not decode bitmap. Resulting bitmap will be red.\n");
int width = this->readInt();
int height = this->readInt();
bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
bitmap->allocPixels();
bitmap->eraseColor(SK_ColorRED);

View File

@ -10,13 +10,15 @@
#define SkOrderedReadBuffer_DEFINED
#include "SkRefCnt.h"
#include "SkBitmap.h"
#include "SkBitmapHeap.h"
#include "SkFlattenableBuffers.h"
#include "SkPath.h"
#include "SkPicture.h"
#include "SkReader32.h"
#include "SkSerializationHelpers.h"
class SkBitmap;
class SkOrderedReadBuffer : public SkFlattenableReadBuffer {
public:
SkOrderedReadBuffer();
@ -99,11 +101,11 @@ public:
}
/**
* Provide a function to decode an SkBitmap from an SkStream. Only used if the writer encoded
* the SkBitmap. If the proper decoder cannot be used, a red bitmap with the appropriate size
* will be used.
* Provide a function to decode an SkBitmap from encoded data. Only used if the writer
* encoded the SkBitmap. If the proper decoder cannot be used, a red bitmap with the
* appropriate size will be used.
*/
void setBitmapDecoder(SkSerializationHelpers::DecodeBitmap bitmapDecoder) {
void setBitmapDecoder(SkPicture::InstallPixelRefProc bitmapDecoder) {
fBitmapDecoder = bitmapDecoder;
}
@ -119,7 +121,7 @@ private:
SkFlattenable::Factory* fFactoryArray;
int fFactoryCount;
SkSerializationHelpers::DecodeBitmap fBitmapDecoder;
SkPicture::InstallPixelRefProc fBitmapDecoder;
typedef SkFlattenableReadBuffer INHERITED;
};

View File

@ -121,7 +121,7 @@ SkPicture::SkPicture() {
fWidth = fHeight = 0;
}
SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
SkPicture::SkPicture(const SkPicture& src) {
fWidth = src.fWidth;
fHeight = src.fHeight;
fRecord = NULL;
@ -264,7 +264,15 @@ void SkPicture::draw(SkCanvas* surface) {
#include "SkStream.h"
SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::DecodeBitmap decoder) : SkRefCnt() {
SkPicture::SkPicture(SkStream* stream) {
this->initFromStream(stream, NULL, NULL);
}
SkPicture::SkPicture(SkStream* stream, bool* success, InstallPixelRefProc proc) {
this->initFromStream(stream, success, proc);
}
void SkPicture::initFromStream(SkStream* stream, bool* success, InstallPixelRefProc proc) {
if (success) {
*success = false;
}
@ -283,7 +291,7 @@ SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::De
if (stream->readBool()) {
bool isValid = false;
fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, decoder));
fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, proc));
if (!isValid) {
SkDELETE(fPlayback);
fPlayback = NULL;

View File

@ -416,7 +416,7 @@ void SkPicturePlayback::serialize(SkWStream* stream,
if (fPictureCount > 0) {
writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount);
for (int i = 0; i < fPictureCount; i++) {
fPictureRefs[i]->serialize(stream);
fPictureRefs[i]->serialize(stream, encoder);
}
}
@ -473,9 +473,8 @@ static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) {
return rbMask;
}
bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info,
uint32_t tag, size_t size,
SkSerializationHelpers::DecodeBitmap decoder) {
bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info, uint32_t tag,
size_t size, SkPicture::InstallPixelRefProc proc) {
/*
* By the time we encounter BUFFER_SIZE_TAG, we need to have already seen
* its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required
@ -515,8 +514,23 @@ bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info,
case PICT_PICTURE_TAG: {
fPictureCount = size;
fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
for (int i = 0; i < fPictureCount; i++) {
fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream));
bool success = true;
int i = 0;
for ( ; i < fPictureCount; i++) {
fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream, &success, proc));
if (!success) {
break;
}
}
if (!success) {
// Delete all of the pictures that were already created (up through i):
for (int j = 0; j <= i; j++) {
fPictureRefs[j]->unref();
}
// Delete the array
SkDELETE_ARRAY(fPictureRefs);
fPictureCount = 0;
return false;
}
} break;
case PICT_BUFFER_SIZE_TAG: {
@ -528,7 +542,7 @@ bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info,
fFactoryPlayback->setupBuffer(buffer);
fTFPlayback.setupBuffer(buffer);
buffer.setBitmapDecoder(decoder);
buffer.setBitmapDecoder(proc);
while (!buffer.eof()) {
tag = buffer.readUInt();
@ -581,8 +595,8 @@ bool SkPicturePlayback::parseBufferTag(SkOrderedReadBuffer& buffer,
return true; // success
}
SkPicturePlayback::SkPicturePlayback(SkStream* stream, const SkPictInfo& info,
bool* isValid, SkSerializationHelpers::DecodeBitmap decoder) {
SkPicturePlayback::SkPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
SkPicture::InstallPixelRefProc proc) {
this->init();
*isValid = false; // wait until we're done parsing to mark as true
@ -593,7 +607,7 @@ SkPicturePlayback::SkPicturePlayback(SkStream* stream, const SkPictInfo& info,
}
uint32_t size = stream->readU32();
if (!this->parseStreamTag(stream, info, tag, size, decoder)) {
if (!this->parseStreamTag(stream, info, tag, size, proc)) {
return; // we're invalid
}
}

View File

@ -63,8 +63,7 @@ public:
SkPicturePlayback();
SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInfo* deepCopyInfo = NULL);
explicit SkPicturePlayback(const SkPictureRecord& record, bool deepCopy = false);
SkPicturePlayback(SkStream*, const SkPictInfo&, bool* isValid,
SkSerializationHelpers::DecodeBitmap decoder);
SkPicturePlayback(SkStream*, const SkPictInfo&, bool* isValid, SkPicture::InstallPixelRefProc);
virtual ~SkPicturePlayback();
@ -192,7 +191,7 @@ public:
private: // these help us with reading/writing
bool parseStreamTag(SkStream*, const SkPictInfo&, uint32_t tag, size_t size,
SkSerializationHelpers::DecodeBitmap decoder);
SkPicture::InstallPixelRefProc);
bool parseBufferTag(SkOrderedReadBuffer&, uint32_t tag, size_t size);
void flattenToBuffer(SkOrderedWriteBuffer&) const;

View File

@ -1,41 +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 "SkImageDecoder.h"
#include "SkStream.h"
#include "SkTemplates.h"
bool SkBitmapFactory::DecodeBitmap(SkBitmap* dst, const SkData* data, Constraints constraint) {
if (NULL == data || data->size() == 0 || dst == NULL) {
return false;
}
SkMemoryStream stream(data->data(), data->size());
SkAutoTDelete<SkImageDecoder> decoder (SkImageDecoder::Factory(&stream));
if (decoder.get() == NULL) {
return false;
}
SkBitmap tmp;
SkImageDecoder::Mode mode;
if (kDecodeBoundsOnly_Constraint == constraint) {
mode = SkImageDecoder::kDecodeBounds_Mode;
} else {
mode = SkImageDecoder::kDecodePixels_Mode;
}
if (decoder->decode(&stream, &tmp, mode)) {
tmp.swap(*dst);
return true;
} else {
return false;
}
}

View File

@ -1,4 +1,3 @@
/*
* Copyright 2006 The Android Open Source Project
*
@ -9,6 +8,7 @@
#include "SkImageDecoder.h"
#include "SkBitmap.h"
#include "SkImagePriv.h"
#include "SkPixelRef.h"
#include "SkStream.h"
#include "SkTemplates.h"
@ -174,6 +174,69 @@ bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
}
class TargetAllocator : public SkBitmap::Allocator {
public:
TargetAllocator(void* target)
: fTarget(target) {}
virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
// SkColorTable is not supported by Info/Target model.
SkASSERT(NULL == ct);
bm->setPixels(fTarget);
return true;
}
private:
void* fTarget;
};
bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
SkImage::Info* info,
const SkBitmapFactory::Target* target) {
if (NULL == info) {
return false;
}
// FIXME: Just to get this working, implement in terms of existing
// ImageDecoder calls.
SkBitmap bm;
SkMemoryStream stream(buffer, size);
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
if (decoder.get() != NULL && decoder->decode(&stream, &bm, kDecodeBounds_Mode)) {
// Now set info properly
if (!SkBitmapToImageInfo(bm, info)) {
return false;
}
// SkBitmapToImageInfo will return false if Index8 is used. kIndex8
// is not supported by the Info/Target model, since kIndex8 requires
// an SkColorTable, which this model does not keep track of.
SkASSERT(bm.config() != SkBitmap::kIndex8_Config);
if (NULL == target) {
return true;
}
if (target->fRowBytes != (uint32_t) bm.rowBytes()) {
if (target->fRowBytes < SkImageMinRowBytes(*info)) {
SkASSERT(!"Desired row bytes is too small");
return false;
}
bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
}
TargetAllocator allocator(target->fAddr);
decoder->setAllocator(&allocator);
stream.rewind();
bool success = decoder->decode(&stream, &bm, kDecodePixels_Mode);
// Remove the allocator, since it's on the stack.
decoder->setAllocator(NULL);
return success;
}
return false;
}
bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
SkBitmap::Config pref, Mode mode, Format* format) {
SkASSERT(stream);

View File

@ -0,0 +1,79 @@
/*
* 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);
}
void SkBitmapFactory::setImageCache(SkImageCache *cache) {
SkRefCnt_SafeAssign(fImageCache, cache);
if (cache != NULL) {
fCacheSelector = NULL;
}
}
void SkBitmapFactory::setCacheSelector(CacheSelector selector) {
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;
}
SkImage::Info info;
if (!fDecodeProc(data->data(), data->size(), &info, NULL)) {
return false;
}
bool isOpaque = false;
SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
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);
dst->setIsOpaque(isOpaque);
// fImageCache and fCacheSelector are mutually exclusive.
SkASSERT(NULL == fImageCache || NULL == fCacheSelector);
SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector(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);
}
}

107
src/lazy/SkLazyPixelRef.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* 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"
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)
, fDecodeProc(proc)
, fImageCache(cache)
, fCacheId(SkImageCache::UNINITIALIZED_ID) {
SkASSERT(fDecodeProc != NULL);
if (NULL == data) {
fData = SkData::NewEmpty();
fErrorInDecoding = true;
} else {
fData = data;
fData->ref();
fErrorInDecoding = data->size() == 0;
}
SkASSERT(cache != NULL);
cache->ref();
// Since this pixel ref bases its data on encoded data, it should never change.
this->setImmutable();
}
SkLazyPixelRef::~SkLazyPixelRef() {
SkASSERT(fData != NULL);
fData->unref();
SkASSERT(fImageCache);
if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
fImageCache->throwAwayCache(fCacheId);
}
fImageCache->unref();
}
static size_t ComputeMinRowBytesAndSize(const SkImage::Info& info, size_t* rowBytes) {
*rowBytes = SkImageMinRowBytes(info);
Sk64 safeSize;
safeSize.setZero();
if (info.fHeight > 0) {
safeSize.setMul(info.fHeight, *rowBytes);
}
SkASSERT(!safeSize.isNeg());
return safeSize.is32() ? safeSize.get32() : 0;
}
void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
if (fErrorInDecoding) {
return NULL;
}
SkBitmapFactory::Target target;
// Check to see if the pixels still exist in the cache.
target.fAddr = SkImageCache::UNINITIALIZED_ID == fCacheId ?
NULL : fImageCache->pinCache(fCacheId);
if (NULL == target.fAddr) {
SkImage::Info info;
SkASSERT(fData != NULL && fData->size() > 0);
// FIXME: As an optimization, only do this part once.
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
if (fErrorInDecoding) {
fCacheId = SkImageCache::UNINITIALIZED_ID;
return NULL;
}
// Allocate the memory.
size_t bytes = ComputeMinRowBytesAndSize(info, &target.fRowBytes);
target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
if (NULL == target.fAddr) {
// Space could not be allocated.
fCacheId = SkImageCache::UNINITIALIZED_ID;
return NULL;
}
SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
if (fErrorInDecoding) {
fImageCache->throwAwayCache(fCacheId);
fCacheId = SkImageCache::UNINITIALIZED_ID;
return NULL;
}
}
return target.fAddr;
}
void SkLazyPixelRef::onUnlockPixels() {
if (fErrorInDecoding) {
return;
}
if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
fImageCache->releaseCache(fCacheId);
}
}
SkData* SkLazyPixelRef::onRefEncodedData() {
fData->ref();
return fData;
}

63
src/lazy/SkLazyPixelRef.h Normal file
View File

@ -0,0 +1,63 @@
/*
* 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 "SkPixelRef.h"
#include "SkFlattenable.h"
class SkColorTable;
class SkData;
class SkImageCache;
/**
* 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.
* Must not be NULL.
*/
SkLazyPixelRef(SkData*, SkBitmapFactory::DecodeProc, SkImageCache*);
virtual ~SkLazyPixelRef();
#ifdef SK_DEBUG
intptr_t getCacheId() const { return fCacheId; }
#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;
private:
bool fErrorInDecoding;
SkData* fData;
SkBitmapFactory::DecodeProc fDecodeProc;
SkImageCache* fImageCache;
intptr_t fCacheId;
typedef SkPixelRef INHERITED;
};
#endif // SkLazyPixelRef_DEFINED

View File

@ -0,0 +1,189 @@
/*
* 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());
#if SK_DEBUG
fRamUsed -= pixels->getLength();
#endif
SkDELETE(pixels);
pixels = prev;
}
#if SK_DEBUG
SkASSERT(fRamUsed == 0);
#endif
}
#ifdef SK_DEBUG
SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
SkAutoMutexAcquire ac(&fMutex);
CachedPixels* pixels = this->findByID(ID);
if (NULL == pixels) {
return SkImageCache::kThrownAway_CacheStatus;
}
if (pixels->isLocked()) {
return SkImageCache::kPinned_CacheStatus;
}
return SkImageCache::kUnpinned_CacheStatus;
}
#endif
void SkLruImageCache::setBudget(size_t newBudget) {
SkAutoMutexAcquire ac(&fMutex);
fRamBudget = newBudget;
this->purgeIfNeeded();
}
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) {
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);
}
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.
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

@ -0,0 +1,155 @@
/*
* 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 "SkAshmemImageCache.h"
#include "SkThread.h"
#ifdef SK_DEBUG
#include "SkTSearch.h"
#endif
#include "android/ashmem.h"
#include <sys/mman.h>
#include <unistd.h>
SkAshmemImageCache::SkAshmemImageCache() {}
SK_DECLARE_STATIC_MUTEX(gAshmemMutex);
SkAshmemImageCache* SkAshmemImageCache::GetAshmemImageCache() {
SkAutoMutexAcquire ac(&gAshmemMutex);
static SkAshmemImageCache gCache;
return &gCache;
}
#ifdef SK_DEBUG
SkAshmemImageCache::~SkAshmemImageCache() {
SkASSERT(fRecs.count() == 0);
}
#endif
// ashmem likes lengths on page boundaries.
static size_t roundToPageSize(size_t size) {
const size_t mask = getpagesize() - 1;
size_t newSize = (size + mask) & ~mask;
return newSize;
}
void* SkAshmemImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
AshmemRec rec;
rec.fSize = roundToPageSize(bytes);
SkAutoMutexAcquire ac(&gAshmemMutex);
rec.fFD = ashmem_create_region(NULL, rec.fSize);
if (-1 == rec.fFD) {
SkDebugf("ashmem_create_region failed\n");
return NULL;
}
int err = ashmem_set_prot_region(rec.fFD, PROT_READ | PROT_WRITE);
if (err != 0) {
SkDebugf("ashmem_set_prot_region failed\n");
close(rec.fFD);
return NULL;
}
rec.fAddr = mmap(NULL, rec.fSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, rec.fFD, 0);
if (-1 == (long) rec.fAddr) {
SkDebugf("mmap failed\n");
close(rec.fFD);
return NULL;
}
(void) ashmem_pin_region(rec.fFD, 0, 0);
#ifdef SK_DEBUG
rec.fPinned = true;
#endif
// In release mode, we do not keep a pointer to this object. It will be destroyed
// either when pinCache returns NULL or when throwAwayCache is called.
AshmemRec* pRec = SkNEW_ARGS(AshmemRec, (rec));
SkASSERT(ID != NULL);
*ID = reinterpret_cast<intptr_t>(pRec);
#ifdef SK_DEBUG
this->appendRec(pRec);
#endif
return rec.fAddr;
}
void* SkAshmemImageCache::pinCache(intptr_t ID) {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
const int fd = rec->fFD;
int pin = ashmem_pin_region(fd, 0, 0);
if (ASHMEM_NOT_PURGED == pin) {
#ifdef SK_DEBUG
rec->fPinned = true;
#endif
return rec->fAddr;
}
// Purged. Remove the associated AshmemRec:
this->removeRec(rec);
ashmem_unpin_region(fd, 0, 0);
return NULL;
}
void SkAshmemImageCache::releaseCache(intptr_t ID) {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
ashmem_unpin_region(rec->fFD, 0, 0);
#ifdef SK_DEBUG
rec->fPinned = false;
#endif
}
void SkAshmemImageCache::throwAwayCache(intptr_t ID) {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
#ifdef SK_DEBUG
SkASSERT(!rec->fPinned);
#endif
this->removeRec(rec);
}
void SkAshmemImageCache::removeRec(SkAshmemImageCache::AshmemRec* rec) {
munmap(rec->fAddr, rec->fSize);
close(rec->fFD);
#ifdef SK_DEBUG
int index = this->findRec(rec);
SkASSERT(index >= 0);
fRecs.remove(index);
#endif
SkDELETE(rec);
}
#ifdef SK_DEBUG
void SkAshmemImageCache::appendRec(SkAshmemImageCache::AshmemRec* rec) {
int index = this->findRec(rec);
// Should not already exist.
SkASSERT(index < 0);
fRecs.insert(~index, 1, &rec);
}
int SkAshmemImageCache::AshmemRec::Compare(const SkAshmemImageCache::AshmemRec* a,
const SkAshmemImageCache::AshmemRec* b) {
return reinterpret_cast<intptr_t>(a) - reinterpret_cast<intptr_t>(b);
}
int SkAshmemImageCache::findRec(const SkAshmemImageCache::AshmemRec* rec) const {
return SkTSearch<AshmemRec>((const AshmemRec**)fRecs.begin(), fRecs.count(), rec,
sizeof(intptr_t), AshmemRec::Compare);
}
SkImageCache::CacheStatus SkAshmemImageCache::getCacheStatus(intptr_t ID) const {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
int index = this->findRec(rec);
if (index < 0) {
return SkImageCache::kThrownAway_CacheStatus;
}
return rec->fPinned ? SkImageCache::kPinned_CacheStatus
: SkImageCache::kUnpinned_CacheStatus;
}
#endif

View File

@ -1,4 +1,3 @@
/*
* Copyright 2012 Google Inc.
*
@ -6,17 +5,26 @@
* 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 "SkStream.h"
#include "SkTemplates.h"
#include "Test.h"
#ifdef SK_BUILD_FOR_ANDROID
#include "SkAshmemImageCache.h"
#endif
static SkBitmap* create_bitmap() {
SkBitmap* bm = SkNEW(SkBitmap);
const int W = 100, H = 100;
@ -44,6 +52,45 @@ static void assert_bounds_equal(skiatest::Reporter* reporter, const SkBitmap& bm
REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
}
static void test_cache(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);
int32_t cacheID = lazyRef->getCacheId();
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
!= SkImageCache::kPinned_CacheStatus);
{
SkAutoLockPixels alp(*bitmapFromFactory.get());
REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
cacheID = lazyRef->getCacheId();
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
== SkImageCache::kPinned_CacheStatus);
}
REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
!= SkImageCache::kPinned_CacheStatus);
bitmapFromFactory.free();
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
== SkImageCache::kThrownAway_CacheStatus);
}
}
static void TestBitmapFactory(skiatest::Reporter* reporter) {
SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
SkASSERT(bitmap.get() != NULL);
@ -54,23 +101,15 @@ static void TestBitmapFactory(skiatest::Reporter* reporter) {
return;
}
SkBitmap bitmapFromFactory;
bool success = SkBitmapFactory::DecodeBitmap(&bitmapFromFactory, encodedBitmap);
// 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, *bitmap.get(), bitmapFromFactory);
REPORTER_ASSERT(reporter, bitmapFromFactory.pixelRef() != NULL);
// When only requesting that the bounds be decoded, the bounds should be set properly while
// the pixels should be empty.
SkBitmap boundedBitmap;
success = SkBitmapFactory::DecodeBitmap(&boundedBitmap, encodedBitmap,
SkBitmapFactory::kDecodeBoundsOnly_Constraint);
REPORTER_ASSERT(reporter, success);
assert_bounds_equal(reporter, *bitmap.get(), boundedBitmap);
REPORTER_ASSERT(reporter, boundedBitmap.pixelRef() == NULL);
SkAutoTUnref<SkLruImageCache> lruCache(SkNEW_ARGS(SkLruImageCache, (1024 * 1024)));
test_cache(reporter, lruCache, encodedBitmap, *bitmap.get());
test_cache(reporter, NULL, encodedBitmap, *bitmap.get());
#ifdef SK_BUILD_FOR_ANDROID
test_cache(reporter, SkAshmemImageCache::GetAshmemImageCache(), encodedBitmap, *bitmap.get());
#endif
}
#include "TestClassDef.h"
DEFINE_TESTCLASS("BitmapFactory", TestBitmapFactoryClass, TestBitmapFactory)
#endif // SK_DEBUG

View File

@ -9,6 +9,7 @@
#include "CopyTilesRenderer.h"
#include "PictureBenchmark.h"
#include "SkBenchLogger.h"
#include "SkBitmapFactory.h"
#include "SkCanvas.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
@ -126,6 +127,7 @@ static void usage(const char* argv0) {
" [--pipe]\n"
" [--bbh bbhType]\n"
" [--multi numThreads]\n"
" [--enable-deferred-image-decoding]\n"
" [--viewport width height][--scale sf]\n"
" [--device bitmap"
#if SK_SUPPORT_GPU
@ -186,6 +188,8 @@ static void usage(const char* argv0) {
SkDebugf(
" --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
" than 1. Only works with tiled rendering.\n"
" --enable-deferred-image-decoding : Defer decoding until drawing images. Has no effect if\n"
" the provided skp does not have its images encoded.\n"
" --viewport width height : Set the viewport.\n"
" --scale sf : Scale drawing by sf.\n"
" --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
@ -227,6 +231,22 @@ static void usage(const char* argv0) {
SkBenchLogger gLogger;
bool lazy_decode = false;
#include "SkData.h"
#include "SkLruImageCache.h"
static SkLruImageCache gLruImageCache(1024*1024);
static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
void* copiedBuffer = sk_malloc_throw(size);
memcpy(copiedBuffer, buffer, size);
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
factory.setImageCache(&gLruImageCache);
return factory.installPixelRef(data, bitmap);
}
static bool run_single_benchmark(const SkString& inputPath,
sk_tools::PictureBenchmark& benchmark) {
SkFILEStream inputStream;
@ -240,7 +260,14 @@ static bool run_single_benchmark(const SkString& inputPath,
}
bool success = false;
SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
SkPicture* picture;
if (lazy_decode) {
picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap));
} else {
picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory));
}
SkAutoTDelete<SkPicture> ad(picture);
if (!success) {
SkString err;
err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
@ -252,11 +279,11 @@ static bool run_single_benchmark(const SkString& inputPath,
sk_tools::get_basename(&filename, inputPath);
SkString result;
result.printf("running bench [%i %i] %s ", picture.width(),
picture.height(), filename.c_str());
result.printf("running bench [%i %i] %s ", picture->width(), picture->height(),
filename.c_str());
gLogger.logProgress(result);
benchmark.run(&picture);
benchmark.run(picture);
return true;
}
@ -538,6 +565,8 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
benchmark->setTimeIndividualTiles(true);
} else if (0 == strcmp(*argv, "--min")) {
benchmark->setPrintMin(true);
} else if (0 == strcmp(*argv, "--enable-deferred-image-decoding")) {
lazy_decode = true;
} else if (0 == strcmp(*argv, "--logPerIter")) {
++argv;
if (argv < stop) {

View File

@ -181,7 +181,7 @@ static int filter_picture(const SkString& inFile, const SkString& outFile) {
SkFILEStream inStream(inFile.c_str());
if (inStream.isValid()) {
inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeStream));
inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeMemory));
}
if (NULL == inPicture) {

View File

@ -8,6 +8,7 @@
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkStream.h"
@ -129,7 +130,7 @@ static bool render_pdf(const SkString& inputPath, const SkString& outputDir,
bool success = false;
SkAutoTUnref<SkPicture>
picture(SkNEW_ARGS(SkPicture, (&inputStream, &success)));
picture(SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory)));
if (!success) {
SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());

View File

@ -7,6 +7,7 @@
#include "CopyTilesRenderer.h"
#include "SkBitmap.h"
#include "SkBitmapFactory.h"
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkGraphics.h"
@ -35,6 +36,7 @@ static void usage(const char* argv0) {
" [--validate [--maxComponentDiff n]]\n"
" [--writeWholeImage]\n"
" [--clone n]\n"
" [--enable-deferred-image-decoding]\n"
" [--viewport width height][--scale sf]\n"
" [--device bitmap"
#if SK_SUPPORT_GPU
@ -83,6 +85,8 @@ static void usage(const char* argv0) {
SkDebugf(
" --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
" than 1. Only works with tiled rendering.\n"
" --enable-deferred-image-decoding : Defer decoding until drawing images. Has no effect if\n"
" the provided skp does not have its images encoded.\n"
" --viewport width height : Set the viewport.\n"
" --scale sf : Scale drawing by sf.\n"
" --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
@ -127,6 +131,39 @@ static void make_output_filepath(SkString* path, const SkString& dir,
path->remove(path->size() - 4, 4);
}
bool lazy_decode = false;
#include "SkData.h"
#include "SkLruImageCache.h"
static SkLruImageCache gLruImageCache(1024*1024);
#ifdef SK_BUILD_FOR_ANDROID
#include "SkAshmemImageCache.h"
#include "SkImage.h"
static SkImageCache* cache_selector(const SkImage::Info& info) {
if (info.fWidth * info.fHeight > 32 * 1024) {
return SkAshmemImageCache::GetAshmemImageCache();
}
return &gLruImageCache;
}
#endif
static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
void* copiedBuffer = sk_malloc_throw(size);
memcpy(copiedBuffer, buffer, size);
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
#ifdef SK_BUILD_FOR_ANDROID
factory.setCacheSelector(&cache_selector);
#else
factory.setImageCache(&gLruImageCache);
#endif
return factory.installPixelRef(data, bitmap);
}
static bool render_picture(const SkString& inputPath, const SkString* outputDir,
sk_tools::PictureRenderer& renderer,
SkBitmap** out,
@ -142,8 +179,12 @@ static bool render_picture(const SkString& inputPath, const SkString* outputDir,
}
bool success = false;
SkPicture* picture = SkNEW_ARGS(SkPicture,
(&inputStream, &success, &SkImageDecoder::DecodeStream));
SkPicture* picture;
if (lazy_decode) {
picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap));
} else {
picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory));
}
if (!success) {
SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
return false;
@ -536,7 +577,8 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
usage(argv0);
exit(-1);
}
} else if (0 == strcmp(*argv, "--enable-deferred-image-decoding")) {
lazy_decode = true;
} else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
SkSafeUnref(renderer);
usage(argv0);