michael@0: /* michael@0: * Copyright 2013 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #ifndef SkScaledImageCache_DEFINED michael@0: #define SkScaledImageCache_DEFINED michael@0: michael@0: #include "SkBitmap.h" michael@0: michael@0: class SkDiscardableMemory; michael@0: class SkMipMap; michael@0: michael@0: /** michael@0: * Cache object for bitmaps (with possible scale in X Y as part of the key). michael@0: * michael@0: * Multiple caches can be instantiated, but each instance is not implicitly michael@0: * thread-safe, so if a given instance is to be shared across threads, the michael@0: * caller must manage the access itself (e.g. via a mutex). michael@0: * michael@0: * As a convenience, a global instance is also defined, which can be safely michael@0: * access across threads via the static methods (e.g. FindAndLock, etc.). michael@0: */ michael@0: class SkScaledImageCache { michael@0: public: michael@0: struct ID; michael@0: michael@0: /** michael@0: * Returns a locked/pinned SkDiscardableMemory instance for the specified michael@0: * number of bytes, or NULL on failure. michael@0: */ michael@0: typedef SkDiscardableMemory* (*DiscardableFactory)(size_t bytes); michael@0: michael@0: /* michael@0: * The following static methods are thread-safe wrappers around a global michael@0: * instance of this cache. michael@0: */ michael@0: michael@0: static ID* FindAndLock(uint32_t pixelGenerationID, michael@0: int32_t width, michael@0: int32_t height, michael@0: SkBitmap* returnedBitmap); michael@0: michael@0: static ID* FindAndLock(const SkBitmap& original, SkScalar scaleX, michael@0: SkScalar scaleY, SkBitmap* returnedBitmap); michael@0: static ID* FindAndLockMip(const SkBitmap& original, michael@0: SkMipMap const** returnedMipMap); michael@0: michael@0: michael@0: static ID* AddAndLock(uint32_t pixelGenerationID, michael@0: int32_t width, michael@0: int32_t height, michael@0: const SkBitmap& bitmap); michael@0: michael@0: static ID* AddAndLock(const SkBitmap& original, SkScalar scaleX, michael@0: SkScalar scaleY, const SkBitmap& bitmap); michael@0: static ID* AddAndLockMip(const SkBitmap& original, const SkMipMap* mipMap); michael@0: michael@0: static void Unlock(ID*); michael@0: michael@0: static size_t GetBytesUsed(); michael@0: static size_t GetByteLimit(); michael@0: static size_t SetByteLimit(size_t newLimit); michael@0: michael@0: static SkBitmap::Allocator* GetAllocator(); michael@0: michael@0: /** michael@0: * Call SkDebugf() with diagnostic information about the state of the cache michael@0: */ michael@0: static void Dump(); michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: michael@0: /** michael@0: * Construct the cache to call DiscardableFactory when it michael@0: * allocates memory for the pixels. In this mode, the cache has michael@0: * not explicit budget, and so methods like getBytesUsed() and michael@0: * getByteLimit() will return 0, and setByteLimit will ignore its argument michael@0: * and return 0. michael@0: */ michael@0: SkScaledImageCache(DiscardableFactory); michael@0: michael@0: /** michael@0: * Construct the cache, allocating memory with malloc, and respect the michael@0: * byteLimit, purging automatically when a new image is added to the cache michael@0: * that pushes the total bytesUsed over the limit. Note: The limit can be michael@0: * changed at runtime with setByteLimit. michael@0: */ michael@0: SkScaledImageCache(size_t byteLimit); michael@0: michael@0: ~SkScaledImageCache(); michael@0: michael@0: /** michael@0: * Search the cache for a matching bitmap (using generationID, michael@0: * width, and height as a search key). If found, return it in michael@0: * returnedBitmap, and return its ID pointer. Use the returned michael@0: * ptr to unlock the cache when you are done using michael@0: * returnedBitmap. michael@0: * michael@0: * If a match is not found, returnedBitmap will be unmodifed, and michael@0: * NULL will be returned. michael@0: * michael@0: * This is used if there is no scaling or subsetting, for example michael@0: * by SkLazyPixelRef. michael@0: */ michael@0: ID* findAndLock(uint32_t pixelGenerationID, int32_t width, int32_t height, michael@0: SkBitmap* returnedBitmap); michael@0: michael@0: /** michael@0: * Search the cache for a scaled version of original. If found, michael@0: * return it in returnedBitmap, and return its ID pointer. Use michael@0: * the returned ptr to unlock the cache when you are done using michael@0: * returnedBitmap. michael@0: * michael@0: * If a match is not found, returnedBitmap will be unmodifed, and michael@0: * NULL will be returned. michael@0: */ michael@0: ID* findAndLock(const SkBitmap& original, SkScalar scaleX, michael@0: SkScalar scaleY, SkBitmap* returnedBitmap); michael@0: ID* findAndLockMip(const SkBitmap& original, michael@0: SkMipMap const** returnedMipMap); michael@0: michael@0: /** michael@0: * To add a new bitmap (or mipMap) to the cache, call michael@0: * AddAndLock. Use the returned ptr to unlock the cache when you michael@0: * are done using scaled. michael@0: * michael@0: * Use (generationID, width, and height) or (original, scaleX, michael@0: * scaleY) or (original) as a search key michael@0: */ michael@0: ID* addAndLock(uint32_t pixelGenerationID, int32_t width, int32_t height, michael@0: const SkBitmap& bitmap); michael@0: ID* addAndLock(const SkBitmap& original, SkScalar scaleX, michael@0: SkScalar scaleY, const SkBitmap& bitmap); michael@0: ID* addAndLockMip(const SkBitmap& original, const SkMipMap* mipMap); michael@0: michael@0: /** michael@0: * Given a non-null ID ptr returned by either findAndLock or addAndLock, michael@0: * this releases the associated resources to be available to be purged michael@0: * if needed. After this, the cached bitmap should no longer be michael@0: * referenced by the caller. michael@0: */ michael@0: void unlock(ID*); michael@0: michael@0: size_t getBytesUsed() const { return fBytesUsed; } michael@0: size_t getByteLimit() const { return fByteLimit; } michael@0: michael@0: /** michael@0: * Set the maximum number of bytes available to this cache. If the current michael@0: * cache exceeds this new value, it will be purged to try to fit within michael@0: * this new limit. michael@0: */ michael@0: size_t setByteLimit(size_t newLimit); michael@0: michael@0: SkBitmap::Allocator* allocator() const { return fAllocator; }; michael@0: michael@0: /** michael@0: * Call SkDebugf() with diagnostic information about the state of the cache michael@0: */ michael@0: void dump() const; michael@0: michael@0: public: michael@0: struct Rec; michael@0: struct Key; michael@0: private: michael@0: Rec* fHead; michael@0: Rec* fTail; michael@0: michael@0: class Hash; michael@0: Hash* fHash; michael@0: michael@0: DiscardableFactory fDiscardableFactory; michael@0: // the allocator is NULL or one that matches discardables michael@0: SkBitmap::Allocator* fAllocator; michael@0: michael@0: size_t fBytesUsed; michael@0: size_t fByteLimit; michael@0: int fCount; michael@0: michael@0: Rec* findAndLock(uint32_t generationID, SkScalar sx, SkScalar sy, michael@0: const SkIRect& bounds); michael@0: Rec* findAndLock(const Key& key); michael@0: ID* addAndLock(Rec* rec); michael@0: michael@0: void purgeRec(Rec*); michael@0: void purgeAsNeeded(); michael@0: michael@0: // linklist management michael@0: void moveToHead(Rec*); michael@0: void addToHead(Rec*); michael@0: void detach(Rec*); michael@0: michael@0: void init(); // called by constructors michael@0: michael@0: #ifdef SK_DEBUG michael@0: void validate() const; michael@0: #else michael@0: void validate() const {} michael@0: #endif michael@0: }; michael@0: #endif