From ca79535dcb36ab3691eadc8664b687f4cd8a56b5 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Wed, 16 Nov 2016 14:45:34 -0500 Subject: External SkImageGenerator API Introduce an SkImageGenerator API to support the implementation of externally-managed image decode and scale caches. BUG=skia:5806 R=reed@google.com GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4720 Change-Id: Ibfe37af5471f78f28f88f9d5e80938882be1a344 Reviewed-on: https://skia-review.googlesource.com/4720 Commit-Queue: Florin Malita Reviewed-by: Mike Reed --- src/core/SkBitmapController.cpp | 31 +++++++++++++++++++++- src/core/SkBitmapProvider.cpp | 34 ++++++++++++++++++++++++ src/core/SkBitmapProvider.h | 4 +++ src/core/SkDevice.cpp | 58 +++++++++++++++++++++++++++++++++++++++++ src/core/SkImageCacherator.cpp | 7 +++++ src/core/SkImageCacherator.h | 3 +++ src/core/SkImageGenerator.cpp | 7 +++++ 7 files changed, 143 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp index 4fee11db28..ed9903e7d3 100644 --- a/src/core/SkBitmapController.cpp +++ b/src/core/SkBitmapController.cpp @@ -48,6 +48,7 @@ private: SkDestinationSurfaceColorMode fColorMode; sk_sp fCurrMip; + bool processExternalRequest(const SkBitmapProvider&); bool processHQRequest(const SkBitmapProvider&); bool processMediumRequest(const SkBitmapProvider&); }; @@ -67,6 +68,28 @@ static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMat return size < (maximumAllocation * SkScalarAbs(invScaleSqr)); } +/* + * Image generators can provide access to externally managed pixels + * (external scale/decode caches). + */ +bool SkDefaultBitmapControllerState::processExternalRequest(const SkBitmapProvider& provider) { + // TODO: actual srcRect + + const SkRect src = SkRect::MakeIWH(provider.width(), provider.height()); + SkRect adjustedSrc; + + if (!provider.accessScaledImage(src, fInvMatrix, fQuality, + &fResultBitmap, &adjustedSrc, &fQuality)) { + return false; + } + + fInvMatrix.postConcat(SkMatrix::MakeRectToRect(src, adjustedSrc, SkMatrix::kFill_ScaleToFit)); + fResultBitmap.lockPixels(); + SkASSERT(fResultBitmap.getPixels()); + + return true; +} + /* * High quality is implemented by performing up-right scale-only filtering and then * using bilerp for any remaining transformations. @@ -205,7 +228,13 @@ SkDefaultBitmapControllerState::SkDefaultBitmapControllerState( fQuality = qual; fColorMode = colorMode; - if (this->processHQRequest(provider) || this->processMediumRequest(provider)) { + bool processed = this->processExternalRequest(provider); + + // Externally handled requests are not guaranteed to reduce quality below kMedium -- so we + // always give our internal processors a shot. + processed |= this->processHQRequest(provider) || this->processMediumRequest(provider); + + if (processed) { SkASSERT(fResultBitmap.getPixels()); } else { (void)provider.asBitmap(&fResultBitmap); diff --git a/src/core/SkBitmapProvider.cpp b/src/core/SkBitmapProvider.cpp index 8347d32d7f..11b1ac98d1 100644 --- a/src/core/SkBitmapProvider.cpp +++ b/src/core/SkBitmapProvider.cpp @@ -7,6 +7,7 @@ #include "SkBitmapProvider.h" #include "SkImage_Base.h" +#include "SkImageCacherator.h" #include "SkPixelRef.h" int SkBitmapProvider::width() const { @@ -46,3 +47,36 @@ void SkBitmapProvider::notifyAddedToCache() const { bool SkBitmapProvider::asBitmap(SkBitmap* bm) const { return as_IB(fImage)->getROPixels(bm, SkImage::kAllow_CachingHint); } + +bool SkBitmapProvider::accessScaledImage(const SkRect& srcRect, + const SkMatrix& invMatrix, + SkFilterQuality fq, + SkBitmap* scaledBitmap, + SkRect* adjustedSrcRect, + SkFilterQuality* adjustedFilterQuality) const { + if (!fImage) { + return false; + } + + SkImageCacherator* cacherator = as_IB(fImage)->peekCacherator(); + if (!cacherator) { + return false; + } + + // TODO: stash the matrix someplace to avoid invert()? + SkMatrix m; + if (!invMatrix.invert(&m)) { + return false; + } + + SkImageGenerator::ScaledImageRec rec; + if (!cacherator->directAccessScaledImage(srcRect, m, fq, &rec) || + !rec.fImage->asLegacyBitmap(scaledBitmap, SkImage::kRO_LegacyBitmapMode)) { + return false; + } + + *adjustedSrcRect = rec.fSrcRect; + *adjustedFilterQuality = rec.fQuality; + + return true; +} diff --git a/src/core/SkBitmapProvider.h b/src/core/SkBitmapProvider.h index 2e878b18ab..fd5b66219e 100644 --- a/src/core/SkBitmapProvider.h +++ b/src/core/SkBitmapProvider.h @@ -33,6 +33,10 @@ public: // ... cause a decode and cache, or gpu-readback bool asBitmap(SkBitmap*) const; + bool accessScaledImage(const SkRect& srcRect, const SkMatrix& invMatrix, SkFilterQuality fq, + SkBitmap* scaledBitmap, SkRect* adjustedSrcRect, + SkFilterQuality* adjustedFilterQuality) const; + private: // Stack-allocated only. void* operator new(size_t) = delete; diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index b2ff2423ed..4caa8756cb 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -10,6 +10,7 @@ #include "SkDraw.h" #include "SkDrawFilter.h" #include "SkImage_Base.h" +#include "SkImageCacherator.h" #include "SkImageFilter.h" #include "SkImageFilterCache.h" #include "SkImagePriv.h" @@ -24,6 +25,7 @@ #include "SkSpecialImage.h" #include "SkTextBlobRunIterator.h" #include "SkTextToPathIter.h" +#include "SkTLazy.h" SkBaseDevice::SkBaseDevice(const SkImageInfo& info, const SkSurfaceProps& surfaceProps) : fInfo(info) @@ -177,9 +179,60 @@ void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkSc } } +bool SkBaseDevice::drawExternallyScaledImage(const SkDraw& draw, + const SkImage* image, + const SkRect* src, + const SkRect& dst, + const SkPaint& paint, + SkCanvas::SrcRectConstraint constraint) { + SkImageCacherator* cacherator = as_IB(image)->peekCacherator(); + if (!cacherator) { + return false; + } + + SkTLazy tmpSrc(src); + if (!tmpSrc.isValid()) { + tmpSrc.init(SkRect::Make(image->bounds())); + } + + SkMatrix m = *draw.fMatrix; + m.preConcat(SkMatrix::MakeRectToRect(*tmpSrc.get(), dst, SkMatrix::kFill_ScaleToFit)); + + // constrain src to our bounds + if (!image->bounds().contains(*tmpSrc.get()) && + !tmpSrc.get()->intersect(SkRect::Make(image->bounds()))) { + return false; + } + + SkImageGenerator::ScaledImageRec rec; + if (!cacherator->directAccessScaledImage(*tmpSrc.get(), m, paint.getFilterQuality(), &rec)) { + return false; + } + + SkBitmap bm; + if (!as_IB(rec.fImage)->getROPixels(&bm)) { + return false; + } + + SkTCopyOnFirstWrite adjustedPaint(paint); + if (rec.fQuality != paint.getFilterQuality()) { + adjustedPaint.writable()->setFilterQuality(rec.fQuality); + } + + this->drawBitmapRect(draw, bm, &rec.fSrcRect, dst, *adjustedPaint, constraint); + + return true; +} void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y, const SkPaint& paint) { // Default impl : turns everything into raster bitmap + + if (this->drawExternallyScaledImage(draw, image, nullptr, + SkRect::Make(image->bounds()).makeOffset(x, y), + paint, SkCanvas::kFast_SrcRectConstraint)) { + return; + } + SkBitmap bm; if (as_IB(image)->getROPixels(&bm)) { this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint); @@ -190,6 +243,11 @@ void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { // Default impl : turns everything into raster bitmap + + if (this->drawExternallyScaledImage(draw, image, src, dst, paint, constraint)) { + return; + } + SkBitmap bm; if (as_IB(image)->getROPixels(&bm)) { this->drawBitmapRect(draw, bm, src, dst, paint, constraint); diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp index 54045d5821..471dbd6d33 100644 --- a/src/core/SkImageCacherator.cpp +++ b/src/core/SkImageCacherator.cpp @@ -157,6 +157,13 @@ bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixe return generator->getPixels(info, pixels, rb); } +bool SkImageCacherator::directAccessScaledImage(const SkRect& srcRect, + const SkMatrix& totalMatrix, + SkFilterQuality fq, + SkImageGenerator::ScaledImageRec* rec) { + return ScopedGenerator(fSharedGenerator)->accessScaledImage(srcRect, totalMatrix, fq, rec); +} + ////////////////////////////////////////////////////////////////////////////////////////////////// bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap) { diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h index a8a05a1f90..0e8a953f37 100644 --- a/src/core/SkImageCacherator.h +++ b/src/core/SkImageCacherator.h @@ -69,6 +69,9 @@ public: bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int srcX, int srcY); + bool directAccessScaledImage(const SkRect& srcRect, const SkMatrix& totalMatrix, + SkFilterQuality, SkImageGenerator::ScaledImageRec*); + private: // Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing of one generator // among several cacherators. diff --git a/src/core/SkImageGenerator.cpp b/src/core/SkImageGenerator.cpp index ffa845b4db..5bb385e51b 100644 --- a/src/core/SkImageGenerator.cpp +++ b/src/core/SkImageGenerator.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkImage.h" #include "SkImageGenerator.h" #include "SkNextID.h" @@ -107,6 +108,12 @@ bool SkImageGenerator::generateScaledPixels(const SkISize& scaledSize, return this->onGenerateScaledPixels(scaledSize, subsetOrigin, subsetPixels); } +bool SkImageGenerator::accessScaledImage(const SkRect& src, const SkMatrix& matrix, + SkFilterQuality fq, ScaledImageRec* rec) { + SkASSERT(fInfo.bounds().contains(src)); + return this->onAccessScaledImage(src, matrix, fq, rec); +} + ///////////////////////////////////////////////////////////////////////////////////////////// SkData* SkImageGenerator::onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) { -- cgit v1.2.3