add applyFilter() to SkImage
Result:
- clients can get a filtered version of an image without having to setup a temp drawing environment
- for some cases, the process is more efficient even than (deprecated) drawSprite, since there is no need to draw/copy the result
Impl:
- made Proxy virtual so we don't need to have an existing device to use it
This, in conjunction with LocalMatrixImageFilter, should allow us to simplify and optimize ApplyImageFilter() in cc/output/gl_renderer.cc
BUG=skia:
Review URL: https://codereview.chromium.org/1390913005
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 4952ae2..6914963 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1300,7 +1300,7 @@
SkImageFilter* filter = paint->getImageFilter();
SkIPoint pos = { x - iter.getX(), y - iter.getY() };
if (filter && !dstDev->canHandleImageFilter(filter)) {
- SkImageFilter::Proxy proxy(dstDev);
+ SkImageFilter::DeviceProxy proxy(dstDev);
SkBitmap dst;
SkIPoint offset = SkIPoint::Make(0, 0);
const SkBitmap& src = srcDev->accessBitmap(false);
@@ -1352,7 +1352,7 @@
SkImageFilter* filter = paint->getImageFilter();
SkIPoint pos = { x - iter.getX(), y - iter.getY() };
if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
- SkImageFilter::Proxy proxy(iter.fDevice);
+ SkImageFilter::DeviceProxy proxy(iter.fDevice);
SkBitmap dst;
SkIPoint offset = SkIPoint::Make(0, 0);
SkMatrix matrix = *iter.fMatrix;
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 4ad7f0e..08d46c7 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -578,7 +578,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
-SkBaseDevice* SkImageFilter::Proxy::createDevice(int w, int h) {
+SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h) {
SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
SkBaseDevice::kNever_TileUsage,
kUnknown_SkPixelGeometry,
@@ -592,7 +592,7 @@
return dev;
}
-bool SkImageFilter::Proxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
+bool SkImageFilter::DeviceProxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
const SkImageFilter::Context& ctx,
SkBitmap* result, SkIPoint* offset) {
return fDevice->filterImage(filter, src, ctx, result, offset);
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
index baeb378..5bddc03 100644
--- a/src/gpu/GrLayerHoister.cpp
+++ b/src/gpu/GrLayerHoister.cpp
@@ -313,7 +313,7 @@
SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(kDefaultCacheSize));
SkImageFilter::Context filterContext(totMat, clipBounds, cache);
- SkImageFilter::Proxy proxy(device);
+ SkImageFilter::DeviceProxy proxy(device);
const SkBitmap src = wrap_texture(layer->texture());
if (!layer->filter()->filterImage(&proxy, src, filterContext, &filteredBitmap, &offset)) {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 4d15059..ab91316 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1395,7 +1395,7 @@
SkBitmap* result, SkIPoint* offset) {
SkASSERT(filter);
- SkImageFilter::Proxy proxy(this);
+ SkImageFilter::DeviceProxy proxy(this);
if (filter->canFilterImageGPU()) {
return filter->filterImageGPU(&proxy, wrap_texture(texture, width, height),
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index af9c275..3ad8004 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -67,6 +67,81 @@
}
}
+SkImage* SkImage::applyFilter(SkImageFilter* filter, SkIPoint* offset,
+ bool forceResultToOriginalSize) const {
+ if (!filter) {
+ return nullptr;
+ }
+
+ SkIPoint offsetStorage;
+ if (!offset) {
+ offset = &offsetStorage;
+ }
+ return as_IB(this)->onApplyFilter(filter, offset, forceResultToOriginalSize);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageFilter.h"
+#include "SkBitmapDevice.h"
+
+static SkIRect compute_fast_ibounds(SkImageFilter* filter, const SkIRect& srcBounds) {
+ SkRect fastBounds;
+ fastBounds.set(srcBounds);
+ filter->computeFastBounds(fastBounds, &fastBounds);
+ return fastBounds.roundOut();
+}
+
+class SkRasterImageFilterProxy : public SkImageFilter::Proxy {
+public:
+ SkBaseDevice* createDevice(int width, int height) override {
+ return SkBitmapDevice::Create(SkImageInfo::MakeN32Premul(width, height));
+ }
+
+ bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
+ SkBitmap*, SkIPoint*) override {
+ return false;
+ }
+};
+
+SkImage* SkImage_Base::onApplyFilter(SkImageFilter* filter, SkIPoint* offsetResult,
+ bool forceResultToOriginalSize) const {
+ SkBitmap src;
+ if (!this->getROPixels(&src)) {
+ return nullptr;
+ }
+
+ const SkIRect srcBounds = SkIRect::MakeWH(this->width(), this->height());
+
+ if (forceResultToOriginalSize) {
+ const SkIRect clipBounds = srcBounds;
+ SkRasterImageFilterProxy proxy;
+ SkImageFilter::Context ctx(SkMatrix::I(), clipBounds, SkImageFilter::Cache::Get());
+
+ SkBitmap dst;
+ if (filter->filterImage(&proxy, src, ctx, &dst, offsetResult)) {
+ dst.setImmutable();
+ return SkImage::NewFromBitmap(dst);
+ }
+ } else {
+ const SkIRect dstR = compute_fast_ibounds(filter, srcBounds);
+
+ SkImageInfo info = SkImageInfo::MakeN32Premul(dstR.width(), dstR.height());
+ SkAutoTUnref<SkSurface> surface(this->onNewSurface(info));
+
+ SkPaint paint;
+ paint.setImageFilter(filter);
+ surface->getCanvas()->drawImage(this, SkIntToScalar(-dstR.x()), SkIntToScalar(-dstR.y()),
+ &paint);
+
+ offsetResult->set(dstR.x(), dstR.y());
+ return surface->newImageSnapshot();
+ }
+ return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
SkShader* SkImage::newShader(SkShader::TileMode tileX,
SkShader::TileMode tileY,
const SkMatrix* localMatrix) const {
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 7819e9c..3fe3444 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -37,6 +37,13 @@
// but only inspect them (or encode them).
virtual bool getROPixels(SkBitmap*) const = 0;
+ virtual SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset,
+ bool forceResultToOriginalSize) const;
+
+ virtual SkSurface* onNewSurface(const SkImageInfo& info) const {
+ return SkSurface::NewRaster(info);
+ }
+
// Caller must call unref when they are done.
virtual GrTexture* asTextureRef(GrContext*, const GrTextureParams&) const = 0;
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index ccb27ee..529df3a 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -38,6 +38,10 @@
}
}
+static SkImageInfo make_info(int w, int h, bool isOpaque) {
+ return SkImageInfo::MakeN32(w, h, isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
+}
+
bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
if (SkBitmapCache::Find(this->uniqueID(), dst)) {
SkASSERT(dst->getGenerationID() == this->uniqueID());
@@ -46,8 +50,7 @@
return true;
}
- SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
- if (!dst->tryAllocPixels(SkImageInfo::MakeN32(this->width(), this->height(), at))) {
+ if (!dst->tryAllocPixels(make_info(this->width(), this->height(), this->isOpaque()))) {
return false;
}
if (!fTexture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
@@ -183,6 +186,69 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkBitmapDevice.h"
+#include "SkGrPixelRef.h"
+#include "SkImageFilter.h"
+
+class SkGpuImageFilterProxy : public SkImageFilter::Proxy {
+ GrContext* fCtx;
+
+public:
+ SkGpuImageFilterProxy(GrContext* ctx) : fCtx(ctx) {}
+
+ SkBaseDevice* createDevice(int width, int height) override {
+ GrSurfaceDesc desc;
+ desc.fConfig = kSkia8888_GrPixelConfig;
+ desc.fFlags = kRenderTarget_GrSurfaceFlag;
+ desc.fWidth = width;
+ desc.fHeight = height;
+ desc.fSampleCnt = 0;
+
+ SkAutoTUnref<GrTexture> texture(fCtx->textureProvider()->createTexture(desc, true));
+
+ if (texture) {
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+ return SkGpuDevice::Create(texture->asRenderTarget(), width, height, &props,
+ SkGpuDevice::kClear_InitContents);
+ } else {
+ return nullptr;
+ }
+ }
+
+ bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
+ SkBitmap*, SkIPoint*) override {
+ return false;
+ }
+};
+
+SkImage* SkImage_Gpu::onApplyFilter(SkImageFilter* filter, SkIPoint* offsetResult,
+ bool forceResultToOriginalSize) const {
+ if (!forceResultToOriginalSize || !filter->canFilterImageGPU()) {
+ return this->INHERITED::onApplyFilter(filter, offsetResult, forceResultToOriginalSize);
+ }
+
+ const SkImageInfo info = make_info(this->width(), this->height(), this->isOpaque());
+ SkAutoTUnref<SkGrPixelRef> pr(new SkGrPixelRef(info, fTexture));
+ SkBitmap src;
+ src.setInfo(info);
+ src.setPixelRef(pr, 0, 0);
+
+ GrContext* context = fTexture->getContext();
+ SkGpuImageFilterProxy proxy(context);
+ SkImageFilter::Context ctx(SkMatrix::I(),
+ SkIRect::MakeWH(this->width(), this->height()),
+ SkImageFilter::Cache::Get());
+
+ SkBitmap dst;
+ if (filter->filterImageGPU(&proxy, src, ctx, &dst, offsetResult)) {
+ return new SkImage_Gpu(dst.width(), dst.height(), kNeedNewImageUniqueID, info.alphaType(),
+ dst.getTexture(), SkSurface::kNo_Budgeted);
+ }
+ return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
static SkImage* new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc,
SkAlphaType at, GrWrapOwnership ownership,
SkImage::TextureReleaseProc releaseProc,
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 530c044..d5080d7 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -43,6 +43,12 @@
bool isOpaque() const override;
bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
int srcX, int srcY) const override;
+ SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset,
+ bool forceResultToOriginalSize) const override;
+
+ SkSurface* onNewSurface(const SkImageInfo& info) const override {
+ return SkSurface::NewRenderTarget(fTexture->getContext(), SkSurface::kNo_Budgeted, info);
+ }
private:
SkAutoTUnref<GrTexture> fTexture;