/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkBitmapCache.h" #include "SkImage.h" #include "SkResourceCache.h" #include "SkMipMap.h" #include "SkPixelRef.h" #include "SkRect.h" /** * Use this for bitmapcache and mipmapcache entries. */ uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) { uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p'); return (sharedID << 32) | bitmapGenID; } void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) { SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID)); } /////////////////////////////////////////////////////////////////////////////////////////////////// SkBitmap::Allocator* SkBitmapCache::GetAllocator() { return SkResourceCache::GetAllocator(); } /** This function finds the bounds of the bitmap *within its pixelRef*. If the bitmap lacks a pixelRef, it will return an empty rect, since that doesn't make sense. This may be a useful enough function that it should be somewhere else (in SkBitmap?). */ static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) { if (!(bm.pixelRef())) { return SkIRect::MakeEmpty(); } SkIPoint origin = bm.pixelRefOrigin(); return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height()); } /** * This function finds the bounds of the image. Today this is just the entire bounds, * but in the future we may support subsets within an image, in which case this should * return that subset (see get_bounds_from_bitmap). */ static SkIRect get_bounds_from_image(const SkImage* image) { return SkIRect::MakeWH(image->width(), image->height()); } SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm, int width, int height) { SkBitmapCacheDesc desc; desc.fImageID = bm.getGenerationID(); desc.fWidth = width; desc.fHeight = height; desc.fBounds = get_bounds_from_bitmap(bm); return desc; } SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm) { return Make(bm, bm.width(), bm.height()); } SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image, int width, int height) { SkBitmapCacheDesc desc; desc.fImageID = image->uniqueID(); desc.fWidth = width; desc.fHeight = height; desc.fBounds = get_bounds_from_image(image); return desc; } SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) { return Make(image, image->width(), image->height()); } namespace { static unsigned gBitmapKeyNamespaceLabel; struct BitmapKey : public SkResourceCache::Key { public: BitmapKey(uint32_t genID, int width, int height, const SkIRect& bounds) : fGenID(genID) , fWidth(width) , fHeight(height) , fBounds(bounds) { this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fGenID), sizeof(fGenID) + sizeof(fWidth) + sizeof(fHeight) + sizeof(fBounds)); } BitmapKey(const SkBitmapCacheDesc& desc) : fGenID(desc.fImageID) , fWidth(desc.fWidth) , fHeight(desc.fHeight) , fBounds(desc.fBounds) { this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fGenID), sizeof(fGenID) + sizeof(fWidth) + sizeof(fHeight) + sizeof(fBounds)); } void dump() const { SkDebugf("-- add [%d %d] %d [%d %d %d %d]\n", fWidth, fHeight, fGenID, fBounds.x(), fBounds.y(), fBounds.width(), fBounds.height()); } const uint32_t fGenID; const int fWidth; const int fHeight; const SkIRect fBounds; }; struct BitmapRec : public SkResourceCache::Rec { BitmapRec(uint32_t genID, int width, int height, const SkIRect& bounds, const SkBitmap& result) : fKey(genID, width, height, bounds) , fBitmap(result) { #ifdef TRACE_NEW_BITMAP_CACHE_RECS fKey.dump(); #endif } BitmapRec(const SkBitmapCacheDesc& desc, const SkBitmap& result) : fKey(desc) , fBitmap(result) { #ifdef TRACE_NEW_BITMAP_CACHE_RECS fKey.dump(); #endif } const Key& getKey() const override { return fKey; } size_t bytesUsed() const override { return sizeof(fKey) + fBitmap.getSize(); } const char* getCategory() const override { return "bitmap"; } SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return fBitmap.pixelRef()->diagnostic_only_getDiscardable(); } static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) { const BitmapRec& rec = static_cast(baseRec); SkBitmap* result = (SkBitmap*)contextBitmap; *result = rec.fBitmap; result->lockPixels(); return SkToBool(result->getPixels()); } private: BitmapKey fKey; SkBitmap fBitmap; }; } // namespace #define CHECK_LOCAL(localCache, localName, globalName, ...) \ ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__)) bool SkBitmapCache::FindWH(const SkBitmapCacheDesc& desc, SkBitmap* result, SkResourceCache* localCache) { if (0 == desc.fWidth || 0 == desc.fHeight) { // degenerate return false; } return CHECK_LOCAL(localCache, find, Find, BitmapKey(desc), BitmapRec::Finder, result); } bool SkBitmapCache::AddWH(const SkBitmapCacheDesc& desc, const SkBitmap& result, SkResourceCache* localCache) { if (0 == desc.fWidth || 0 == desc.fHeight) { // degenerate, and the key we use for mipmaps return false; } SkASSERT(result.isImmutable()); BitmapRec* rec = new BitmapRec(desc, result); CHECK_LOCAL(localCache, add, Add, rec); return true; } bool SkBitmapCache::Find(uint32_t genID, const SkIRect& subset, SkBitmap* result, SkResourceCache* localCache) { BitmapKey key(genID, SK_Scalar1, SK_Scalar1, subset); return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result); } bool SkBitmapCache::Add(SkPixelRef* pr, const SkIRect& subset, const SkBitmap& result, SkResourceCache* localCache) { SkASSERT(result.isImmutable()); if (subset.isEmpty() || subset.top() < 0 || subset.left() < 0 || result.width() != subset.width() || result.height() != subset.height()) { return false; } else { BitmapRec* rec = new BitmapRec(pr->getGenerationID(), 1, 1, subset, result); CHECK_LOCAL(localCache, add, Add, rec); pr->notifyAddedToCache(); return true; } } bool SkBitmapCache::Find(uint32_t genID, SkBitmap* result, SkResourceCache* localCache) { BitmapKey key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeEmpty()); return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result); } void SkBitmapCache::Add(uint32_t genID, const SkBitmap& result, SkResourceCache* localCache) { SkASSERT(result.isImmutable()); BitmapRec* rec = new BitmapRec(genID, 1, 1, SkIRect::MakeEmpty(), result); CHECK_LOCAL(localCache, add, Add, rec); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// namespace { static unsigned gMipMapKeyNamespaceLabel; struct MipMapKey : public SkResourceCache::Key { public: MipMapKey(uint32_t genID, const SkIRect& bounds) : fGenID(genID), fBounds(bounds) { this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID), sizeof(fGenID) + sizeof(fBounds)); } uint32_t fGenID; SkIRect fBounds; }; struct MipMapRec : public SkResourceCache::Rec { MipMapRec(const SkBitmap& src, const SkMipMap* result) : fKey(src.getGenerationID(), get_bounds_from_bitmap(src)) , fMipMap(result) { fMipMap->attachToCacheAndRef(); } virtual ~MipMapRec() { fMipMap->detachFromCacheAndUnref(); } const Key& getKey() const override { return fKey; } size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); } const char* getCategory() const override { return "mipmap"; } SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return fMipMap->diagnostic_only_getDiscardable(); } static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) { const MipMapRec& rec = static_cast(baseRec); const SkMipMap* mm = SkRef(rec.fMipMap); // the call to ref() above triggers a "lock" in the case of discardable memory, // which means we can now check for null (in case the lock failed). if (nullptr == mm->data()) { mm->unref(); // balance our call to ref() return false; } // the call must call unref() when they are done. *(const SkMipMap**)contextMip = mm; return true; } private: MipMapKey fKey; const SkMipMap* fMipMap; }; } const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc, SkResourceCache* localCache) { // Note: we ignore width/height from desc, just need id and bounds MipMapKey key(desc.fImageID, desc.fBounds); const SkMipMap* result; if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) { result = nullptr; } return result; } static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) { return localCache ? localCache->GetDiscardableFactory() : SkResourceCache::GetDiscardableFactory(); } const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, SkResourceCache* localCache) { SkMipMap* mipmap = SkMipMap::Build(src, get_fact(localCache)); if (mipmap) { MipMapRec* rec = new MipMapRec(src, mipmap); CHECK_LOCAL(localCache, add, Add, rec); src.pixelRef()->notifyAddedToCache(); } return mipmap; }