/* * 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) { return { bm.getGenerationID(), width, height, get_bounds_from_bitmap(bm) }; } SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm) { return Make(bm, bm.width(), bm.height()); } SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image, int width, int height) { return { image->uniqueID(), width, height, get_bounds_from_image(image) }; } 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) : fDesc({ genID, width, height, bounds }) { this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID), sizeof(fDesc)); } BitmapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) { this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID), sizeof(fDesc)); } void dump() const { SkDebugf("-- add [%d %d] %d [%d %d %d %d]\n", fDesc.fWidth, fDesc.fHeight, fDesc.fImageID, fDesc.fBounds.x(), fDesc.fBounds.y(), fDesc.fBounds.width(), fDesc.fBounds.height()); } const SkBitmapCacheDesc fDesc; }; 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, SkDestinationSurfaceColorMode colorMode, const SkIRect& bounds) : fGenID(genID), fColorMode(static_cast(colorMode)), fBounds(bounds) { this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID), sizeof(fGenID) + sizeof(fColorMode) + sizeof(fBounds)); } uint32_t fGenID; uint32_t fColorMode; SkIRect fBounds; }; struct MipMapRec : public SkResourceCache::Rec { MipMapRec(const SkBitmap& src, SkDestinationSurfaceColorMode colorMode, const SkMipMap* result) : fKey(src.getGenerationID(), colorMode, get_bounds_from_bitmap(src)) , fMipMap(result) { fMipMap->attachToCacheAndRef(); } ~MipMapRec() override { 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, SkDestinationSurfaceColorMode colorMode, SkResourceCache* localCache) { // Note: we ignore width/height from desc, just need id and bounds MipMapKey key(desc.fImageID, colorMode, 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, SkDestinationSurfaceColorMode colorMode, SkResourceCache* localCache) { SkMipMap* mipmap = SkMipMap::Build(src, colorMode, get_fact(localCache)); if (mipmap) { MipMapRec* rec = new MipMapRec(src, colorMode, mipmap); CHECK_LOCAL(localCache, add, Add, rec); src.pixelRef()->notifyAddedToCache(); } return mipmap; }