From 98ed7b6d01c16ad9ace652ec25861689d765d0b5 Mon Sep 17 00:00:00 2001 From: reed Date: Tue, 15 Sep 2015 12:38:12 -0700 Subject: create SkBitmapProvider to abstract images and bitmaps BUG=skia: Review URL: https://codereview.chromium.org/1340223003 --- src/core/SkBitmapCache.cpp | 86 +++++++++++++++++++----- src/core/SkBitmapCache.h | 22 ++++-- src/core/SkBitmapController.cpp | 144 ++++++++++++++++++++++++++++------------ src/core/SkBitmapController.h | 37 +++++++++-- 4 files changed, 223 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp index dfa387bb39..83eec1b666 100644 --- a/src/core/SkBitmapCache.cpp +++ b/src/core/SkBitmapCache.cpp @@ -6,6 +6,7 @@ */ #include "SkBitmapCache.h" +#include "SkImage.h" #include "SkResourceCache.h" #include "SkMipMap.h" #include "SkPixelRef.h" @@ -43,6 +44,41 @@ static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) { 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; @@ -54,7 +90,17 @@ public: , fHeight(height) , fBounds(bounds) { - this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID), + 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)); } @@ -80,6 +126,15 @@ struct BitmapRec : public SkResourceCache::Rec { #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(); } @@ -106,28 +161,25 @@ private: #define CHECK_LOCAL(localCache, localName, globalName, ...) \ ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__)) -bool SkBitmapCache::FindWH(const SkBitmap& src, int width, int height, SkBitmap* result, +bool SkBitmapCache::FindWH(const SkBitmapCacheDesc& desc, SkBitmap* result, SkResourceCache* localCache) { - if (0 == width || 0 == height) { - // degenerate, and the key we use for mipmaps + if (0 == desc.fWidth || 0 == desc.fHeight) { + // degenerate return false; } - BitmapKey key(src.getGenerationID(), width, height, get_bounds_from_bitmap(src)); - - return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result); + return CHECK_LOCAL(localCache, find, Find, BitmapKey(desc), BitmapRec::Finder, result); } -void SkBitmapCache::AddWH(const SkBitmap& src, int width, int height, - const SkBitmap& result, SkResourceCache* localCache) { - if (0 == width || 0 == height) { +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; + return false; } SkASSERT(result.isImmutable()); - BitmapRec* rec = new BitmapRec(src.getGenerationID(), width, height, - get_bounds_from_bitmap(src), result); + BitmapRec* rec = new BitmapRec(desc, result); CHECK_LOCAL(localCache, add, Add, rec); - src.pixelRef()->notifyAddedToCache(); + return true; } bool SkBitmapCache::Find(uint32_t genID, const SkIRect& subset, SkBitmap* result, @@ -226,8 +278,10 @@ private: }; } -const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmap& src, SkResourceCache* localCache) { - MipMapKey key(src.getGenerationID(), get_bounds_from_bitmap(src)); +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)) { diff --git a/src/core/SkBitmapCache.h b/src/core/SkBitmapCache.h index 12be6fad35..9371e77ca9 100644 --- a/src/core/SkBitmapCache.h +++ b/src/core/SkBitmapCache.h @@ -11,6 +11,7 @@ #include "SkScalar.h" #include "SkBitmap.h" +class SkImage; class SkResourceCache; class SkMipMap; @@ -18,6 +19,18 @@ uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID); void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID); +struct SkBitmapCacheDesc { + uint32_t fImageID; + int32_t fWidth; + int32_t fHeight; + SkIRect fBounds; + + static SkBitmapCacheDesc Make(const SkBitmap&, int width, int height); + static SkBitmapCacheDesc Make(const SkBitmap&); + static SkBitmapCacheDesc Make(const SkImage*, int width, int height); + static SkBitmapCacheDesc Make(const SkImage*); +}; + class SkBitmapCache { public: /** @@ -27,16 +40,16 @@ public: static SkBitmap::Allocator* GetAllocator(); /** - * Search based on the src bitmap and scaled width/height. If found, returns true and + * Search based on the desc. If found, returns true and * result will be set to the matching bitmap with its pixels already locked. */ - static bool FindWH(const SkBitmap& src, int width, int height, SkBitmap* result, + static bool FindWH(const SkBitmapCacheDesc&, SkBitmap* result, SkResourceCache* localCache = nullptr); /* * result must be marked isImmutable() */ - static void AddWH(const SkBitmap& src, int width, int height, const SkBitmap& result, + static bool AddWH(const SkBitmapCacheDesc&, const SkBitmap& result, SkResourceCache* localCache = nullptr); /** @@ -60,7 +73,8 @@ public: class SkMipMapCache { public: - static const SkMipMap* FindAndRef(const SkBitmap& src, SkResourceCache* localCache = nullptr); + static const SkMipMap* FindAndRef(const SkBitmapCacheDesc&, + SkResourceCache* localCache = nullptr); static const SkMipMap* AddAndRef(const SkBitmap& src, SkResourceCache* localCache = nullptr); }; diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp index 3c3e69413a..c75d2ea6bc 100644 --- a/src/core/SkBitmapController.cpp +++ b/src/core/SkBitmapController.cpp @@ -7,7 +7,9 @@ #include "SkBitmap.h" #include "SkBitmapController.h" +#include "SkImage_Base.h" #include "SkMatrix.h" +#include "SkPixelRef.h" #include "SkTemplates.h" // RESIZE_LANCZOS3 is another good option, but chrome prefers mitchell at the moment @@ -15,36 +17,85 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// -static bool valid_for_drawing(const SkBitmap& bm) { - if (0 == bm.width() || 0 == bm.height()) { - return false; // nothing to draw - } - if (nullptr == bm.pixelRef()) { - return false; // no pixels to read - } - if (bm.getTexture()) { - // we can handle texture (ugh) since lockPixels will perform a read-back - return true; - } - if (kIndex_8_SkColorType == bm.colorType()) { - SkAutoLockPixels alp(bm); // but we need to call it before getColorTable() is safe. - if (!bm.getColorTable()) { +int SkBitmapProvider::width() const { + return fImage ? fImage->width() : fBitmap.width(); +} + +int SkBitmapProvider::height() const { + return fImage ? fImage->height() : fBitmap.height(); +} + +uint32_t SkBitmapProvider::getID() const { + return fImage ? fImage->uniqueID() : fBitmap.getGenerationID(); +} + +bool SkBitmapProvider::validForDrawing() const { + if (!fImage) { + if (0 == fBitmap.width() || 0 == fBitmap.height()) { return false; } + if (nullptr == fBitmap.pixelRef()) { + return false; // no pixels to read + } + if (fBitmap.getTexture()) { + // we can handle texture (ugh) since lockPixels will perform a read-back + return true; + } + if (kIndex_8_SkColorType == fBitmap.colorType()) { + SkAutoLockPixels alp(fBitmap); // but we need to call it before getColorTable() is safe. + if (!fBitmap.getColorTable()) { + return false; + } + } } return true; } -SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmap& bm, +SkImageInfo SkBitmapProvider::info() const { + if (fImage) { + SkAlphaType at = fImage->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType; + return SkImageInfo::MakeN32(fImage->width(), fImage->height(), at); + } else { + return fBitmap.info(); + } +} + +SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc(int w, int h) const { + return fImage ? SkBitmapCacheDesc::Make(fImage, w, h) : SkBitmapCacheDesc::Make(fBitmap, w, h); +} + +SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc() const { + return fImage ? SkBitmapCacheDesc::Make(fImage) : SkBitmapCacheDesc::Make(fBitmap); +} + +void SkBitmapProvider::notifyAddedToCache() const { + if (fImage) { + // TODO + } else { + fBitmap.pixelRef()->notifyAddedToCache(); + } +} + +bool SkBitmapProvider::asBitmap(SkBitmap* bm) const { + if (fImage) { + return as_IB(fImage)->getROPixels(bm); + } else { + *bm = fBitmap; + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider, const SkMatrix& inv, SkFilterQuality quality, void* storage, size_t storageSize) { - - if (!valid_for_drawing(bm)) { + if (!provider.validForDrawing()) { return nullptr; } - State* state = this->onRequestBitmap(bm, inv, quality, storage, storageSize); + State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize); if (state) { if (nullptr == state->fPixmap.addr()) { SkInPlaceDeleteCheck(state, storage); @@ -63,19 +114,19 @@ SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmap& bm, class SkDefaultBitmapControllerState : public SkBitmapController::State { public: - SkDefaultBitmapControllerState(const SkBitmap& src, const SkMatrix& inv, SkFilterQuality qual); + SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality); private: SkBitmap fResultBitmap; SkAutoTUnref fCurrMip; - bool processHQRequest(const SkBitmap& orig); - bool processMediumRequest(const SkBitmap& orig); + bool processHQRequest(const SkBitmapProvider&); + bool processMediumRequest(const SkBitmapProvider&); }; // Check to see that the size of the bitmap that would be produced by // scaling by the given inverted matrix is less than the maximum allowed. -static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) { +static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) { size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit(); if (0 == maximumAllocation) { return true; @@ -83,7 +134,7 @@ static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) { // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY); // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); // Skip the division step: - const size_t size = bm.info().getSafeSize(bm.info().minRowBytes()); + const size_t size = provider.info().getSafeSize(provider.info().minRowBytes()); return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); } @@ -91,7 +142,7 @@ static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) { * High quality is implemented by performing up-right scale-only filtering and then * using bilerp for any remaining transformations. */ -bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap) { +bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) { if (fQuality != kHigh_SkFilterQuality) { return false; } @@ -100,7 +151,7 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap // to a valid bitmap. If we succeed, we will set this to Low instead. fQuality = kMedium_SkFilterQuality; - if (kN32_SkColorType != origBitmap.colorType() || !cache_size_okay(origBitmap, fInvMatrix) || + if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) || fInvMatrix.hasPerspective()) { return false; // can't handle the reqeust @@ -120,12 +171,17 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap return false; // no need for HQ } - const int dstW = SkScalarRoundToScalar(origBitmap.width() / invScaleX); - const int dstH = SkScalarRoundToScalar(origBitmap.height() / invScaleY); - - if (!SkBitmapCache::FindWH(origBitmap, dstW, dstH, &fResultBitmap)) { + const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX); + const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY); + const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH); + + if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) { + SkBitmap orig; + if (!provider.asBitmap(&orig)) { + return false; + } SkAutoPixmapUnlock src; - if (!origBitmap.requestLock(&src)) { + if (!orig.requestLock(&src)) { return false; } if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD, @@ -135,13 +191,15 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap SkASSERT(fResultBitmap.getPixels()); fResultBitmap.setImmutable(); - SkBitmapCache::AddWH(origBitmap, dstW, dstH, fResultBitmap); + if (SkBitmapCache::AddWH(desc, fResultBitmap)) { + provider.notifyAddedToCache(); + } } SkASSERT(fResultBitmap.getPixels()); - fInvMatrix.postScale(SkIntToScalar(dstW) / origBitmap.width(), - SkIntToScalar(dstH) / origBitmap.height()); + fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(), + SkIntToScalar(dstH) / provider.height()); fQuality = kLow_SkFilterQuality; return true; } @@ -150,7 +208,7 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap * Modulo internal errors, this should always succeed *if* the matrix is downscaling * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling) */ -bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBitmap) { +bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) { SkASSERT(fQuality <= kMedium_SkFilterQuality); if (fQuality != kMedium_SkFilterQuality) { return false; @@ -167,9 +225,13 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBi SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height()); if (invScale > SK_Scalar1) { - fCurrMip.reset(SkMipMapCache::FindAndRef(origBitmap)); + fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc())); if (nullptr == fCurrMip.get()) { - fCurrMip.reset(SkMipMapCache::AddAndRef(origBitmap)); + SkBitmap orig; + if (!provider.asBitmap(&orig)) { + return false; + } + fCurrMip.reset(SkMipMapCache::AddAndRef(orig)); if (nullptr == fCurrMip.get()) { return false; } @@ -185,7 +247,7 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBi SkScalar invScaleFixup = level.fScale; fInvMatrix.postScale(invScaleFixup, invScaleFixup); - const SkImageInfo info = origBitmap.info().makeWH(level.fWidth, level.fHeight); + const SkImageInfo info = provider.info().makeWH(level.fWidth, level.fHeight); // todo: if we could wrap the fCurrMip in a pixelref, then we could just install // that here, and not need to explicitly track it ourselves. return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes); @@ -197,16 +259,16 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBi return false; } -SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmap& src, +SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider, const SkMatrix& inv, SkFilterQuality qual) { fInvMatrix = inv; fQuality = qual; - if (this->processHQRequest(src) || this->processMediumRequest(src)) { + if (this->processHQRequest(provider) || this->processMediumRequest(provider)) { SkASSERT(fResultBitmap.getPixels()); } else { - fResultBitmap = src; + (void)provider.asBitmap(&fResultBitmap); fResultBitmap.lockPixels(); // lock may fail to give us pixels } @@ -218,7 +280,7 @@ SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmap& s fResultBitmap.getColorTable()); } -SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmap& bm, +SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm, const SkMatrix& inverse, SkFilterQuality quality, void* storage, size_t size) { diff --git a/src/core/SkBitmapController.h b/src/core/SkBitmapController.h index aa663e85f7..435d9e72af 100644 --- a/src/core/SkBitmapController.h +++ b/src/core/SkBitmapController.h @@ -9,9 +9,36 @@ #define SkBitmapController_DEFINED #include "SkBitmap.h" +#include "SkBitmapCache.h" #include "SkFilterQuality.h" +#include "SkImage.h" #include "SkMatrix.h" +class SkBitmapProvider { +public: + SkBitmapProvider(const SkBitmap& bm) : fBitmap(bm) {} + SkBitmapProvider(const SkImage* img) : fImage(SkRef(img)) {} + + int width() const; + int height() const; + uint32_t getID() const; + + bool validForDrawing() const; + SkImageInfo info() const; + + SkBitmapCacheDesc makeCacheDesc(int w, int h) const; + SkBitmapCacheDesc makeCacheDesc() const; + void notifyAddedToCache() const; + + // Only call this if you're sure you need the bits, since it make be expensive + // ... cause a decode and cache, or gpu-readback + bool asBitmap(SkBitmap*) const; + +private: + SkBitmap fBitmap; + SkAutoTUnref fImage; +}; + /** * Handles request to scale, filter, and lock a bitmap to be rasterized. */ @@ -36,15 +63,15 @@ public: virtual ~SkBitmapController() {} - State* requestBitmap(const SkBitmap&, const SkMatrix& inverse, SkFilterQuality, + State* requestBitmap(const SkBitmapProvider&, const SkMatrix& inverse, SkFilterQuality, void* storage, size_t storageSize); - State* requestBitmap(const SkBitmap& bm, const SkMatrix& inverse, SkFilterQuality quality) { - return this->requestBitmap(bm, inverse, quality, nullptr, 0); + State* requestBitmap(const SkBitmapProvider& bp, const SkMatrix& inv, SkFilterQuality quality) { + return this->requestBitmap(bp, inv, quality, nullptr, 0); } protected: - virtual State* onRequestBitmap(const SkBitmap&, const SkMatrix& inverse, SkFilterQuality, + virtual State* onRequestBitmap(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality, void* storage, size_t storageSize) = 0; }; @@ -55,7 +82,7 @@ public: SkDefaultBitmapController() {} protected: - State* onRequestBitmap(const SkBitmap&, const SkMatrix& inverse, SkFilterQuality, + State* onRequestBitmap(const SkBitmapProvider&, const SkMatrix& inverse, SkFilterQuality, void* storage, size_t storageSize) override; }; -- cgit v1.2.3