From 8926b169f6a0dfa4c2129a98ec2aee205f0c8527 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Fri, 23 Mar 2012 15:36:36 +0000 Subject: [PATCH] apply imagefilter to all draw calls Review URL: https://codereview.appspot.com/5856048 git-svn-id: http://skia.googlecode.com/svn/trunk@3476 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/imagefiltersbase.cpp | 215 +++++++++++++++++++++++++++++++++++++++++++ gyp/gmslides.gypi | 1 + include/core/SkCanvas.h | 6 +- include/core/SkDevice.h | 21 +++-- include/core/SkImageFilter.h | 6 +- include/gpu/SkGpuDevice.h | 6 +- src/core/SkCanvas.cpp | 146 +++++++++++++++++++---------- src/core/SkDevice.cpp | 10 +- src/gpu/SkGpuDevice.cpp | 152 ++++++++++++++++++------------ 9 files changed, 441 insertions(+), 122 deletions(-) create mode 100644 gm/imagefiltersbase.cpp diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp new file mode 100644 index 0000000..bde2612 --- /dev/null +++ b/gm/imagefiltersbase.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkCanvas.h" +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkShader.h" + +#include "SkBlurImageFilter.h" +#include "SkTestImageFilters.h" + +class FailImageFilter : public SkImageFilter { +public: + FailImageFilter() {} + virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }; + +protected: + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, + SkBitmap* result, SkIPoint* offset) { + return false; + } + + FailImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return new FailImageFilter(buffer); + } + + typedef SkImageFilter INHERITED; +}; + +class IdentityImageFilter : public SkImageFilter { +public: + IdentityImageFilter() {} + virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }; + +protected: + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, + SkBitmap* result, SkIPoint* offset) { + *result = src; + return true; + } + + IdentityImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} + +private: + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return new IdentityImageFilter(buffer); + } + + typedef SkImageFilter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static void draw_paint(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + paint.setColor(SK_ColorGREEN); + canvas->save(); + canvas->clipRect(r); + canvas->drawPaint(paint); + canvas->restore(); +} + +static void draw_line(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setColor(SK_ColorBLUE); + paint.setImageFilter(imf); + paint.setStrokeWidth(r.width()/10); + canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); +} + +static void draw_rect(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setColor(SK_ColorYELLOW); + paint.setImageFilter(imf); + SkRect rr(r); + rr.inset(r.width()/10, r.height()/10); + canvas->drawRect(rr, paint); +} + +static void draw_path(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setColor(SK_ColorMAGENTA); + paint.setImageFilter(imf); + paint.setAntiAlias(true); + canvas->drawCircle(r.centerX(), r.centerY(), r.width()*2/5, paint); +} + +static void draw_text(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + paint.setColor(SK_ColorCYAN); + paint.setAntiAlias(true); + paint.setTextSize(r.height()/2); + paint.setTextAlign(SkPaint::kCenter_Align); + canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint); +} + +static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()); + bm.allocPixels(); + bm.eraseColor(0); + SkCanvas c(bm); + draw_path(&c, r, NULL); + + canvas->drawBitmap(bm, 0, 0, &paint); +} + +static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()); + bm.allocPixels(); + bm.eraseColor(0); + SkCanvas c(bm); + draw_path(&c, r, NULL); + + SkPoint loc = { r.fLeft, r.fTop }; + canvas->getTotalMatrix().mapPoints(&loc, 1); + canvas->drawSprite(bm, + SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY), + &paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +class ImageFiltersBaseGM : public skiagm::GM { +public: + ImageFiltersBaseGM () {} + +protected: + + virtual SkString onShortName() { + return SkString("imagefiltersbase"); + } + + virtual SkISize onISize() { return SkISize::Make(700, 460); } + + void draw_frame(SkCanvas* canvas, const SkRect& r) { + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(SK_ColorRED); + canvas->drawRect(r, paint); + } + + virtual void onDraw(SkCanvas* canvas) { + void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = { + draw_paint, + draw_line, draw_rect, draw_path, draw_text, + draw_bitmap, + draw_sprite + }; + + SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorRED, + SkXfermode::kSrcIn_Mode); + SkImageFilter* filters[] = { +#if 1 + NULL, + new IdentityImageFilter, + new FailImageFilter, + new SkColorFilterImageFilter(cf), +#endif + new SkBlurImageFilter(12.0f, 0.0f), + }; + cf->unref(); + + SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64)); + SkScalar MARGIN = SkIntToScalar(16); + SkScalar DX = r.width() + MARGIN; + SkScalar DY = r.height() + MARGIN; + + canvas->translate(MARGIN, MARGIN); + for (size_t i = 0; i < SK_ARRAY_COUNT(drawProc); ++i) { + canvas->save(); + for (size_t j = 0; j < SK_ARRAY_COUNT(filters); ++j) { + drawProc[i](canvas, r, filters[j]); + + draw_frame(canvas, r); + canvas->translate(0, DY); + } + canvas->restore(); + canvas->translate(DX, 0); + } + } + +private: + typedef GM INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static skiagm::GM* MyFactory(void*) { return new ImageFiltersBaseGM; } +static skiagm::GMRegistry reg(MyFactory); + + diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 5beac2b..491af68 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -26,6 +26,7 @@ '../gm/gradtext.cpp', '../gm/hairmodes.cpp', '../gm/imageblur.cpp', + '../gm/imagefiltersbase.cpp', '../gm/lcdtext.cpp', '../gm/linepaths.cpp', '../gm/morphology.cpp', diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 7ad88f6..5224217 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -977,6 +977,7 @@ private: void updateDeviceCMCache(); friend class SkDrawIter; // needs setupDrawForLayerDevice() + friend class AutoDrawLooper; SkDevice* createLayerDevice(SkBitmap::Config, int width, int height, bool isOpaque); @@ -992,9 +993,10 @@ private: void internalDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint); void internalDrawPaint(const SkPaint& paint); + int internalSaveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags, bool justForImageFilter); + void internalDrawDevice(SkDevice*, int x, int y, const SkPaint*); - - void drawDevice(SkDevice*, int x, int y, const SkPaint*); // shared by save() and saveLayer() int internalSave(SaveFlags flags); void internalRestore(); diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index 3303981..db9c542 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -321,13 +321,22 @@ protected: virtual bool allowImageFilter(SkImageFilter*); /** - * Override and return true for filters that the device handles - * intrinsically. Returning false means call the filter. - * Default impl returns false. This will only be called if allowImageFilter() - * returned true. + * Override and return true for filters that the device can handle + * intrinsically. Doing so means that SkCanvas will pass-through this + * filter to drawSprite and drawDevice (and potentially filterImage). + * Returning false means the SkCanvas will have apply the filter itself, + * and just pass the resulting image to the device. */ - virtual bool filterImage(SkImageFilter*, const SkBitmap& src, - const SkMatrix& ctm, + virtual bool canHandleImageFilter(SkImageFilter*); + + /** + * Related (but not required) to canHandleImageFilter, this method returns + * true if the device could apply the filter to the src bitmap and return + * the result (and updates offset as needed). + * If the device does not recognize or support this filter, + * it just returns false and leaves result and offset unchanged. + */ + virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&, SkBitmap* result, SkIPoint* offset); // This is equal kBGRA_Premul_Config8888 or kRGBA_Premul_Config8888 if diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index 7d7c140..1b282d2 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -40,14 +40,16 @@ class SK_API SkImageFilter : public SkFlattenable { public: class Proxy { public: + virtual ~Proxy() {}; + virtual SkDevice* createDevice(int width, int height) = 0; - + // returns true if the proxy can handle this filter natively + virtual bool canHandleImageFilter(SkImageFilter*) = 0; // returns true if the proxy handled the filter itself. if this returns // false then the filter's code will be called. virtual bool filterImage(SkImageFilter*, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* offset) = 0; - virtual ~Proxy() {}; }; /** diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 24e6e5a..409e3b3 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -110,9 +110,9 @@ public: */ virtual void makeRenderTargetCurrent(); - virtual bool filterImage(SkImageFilter*, const SkBitmap& src, - const SkMatrix& ctm, - SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; + virtual bool canHandleImageFilter(SkImageFilter*) SK_OVERRIDE; + virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&, + SkBitmap*, SkIPoint*) SK_OVERRIDE; class SkAutoCachedTexture; // used internally diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 1cd9d71..dde3570 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -277,20 +277,36 @@ private: class AutoDrawLooper { public: - AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) { + AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, + bool skipLayerForImageFilter = false) : fOrigPaint(paint) { fCanvas = canvas; fLooper = paint.getLooper(); fFilter = canvas->getDrawFilter(); fPaint = NULL; fSaveCount = canvas->getSaveCount(); + fDoClearImageFilter = false; fDone = false; + if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) { + SkPaint tmp; + tmp.setImageFilter(fOrigPaint.getImageFilter()); + // it would be nice if we had a guess at the bounds, instead of null + (void)canvas->internalSaveLayer(NULL, &tmp, + SkCanvas::kARGB_ClipLayer_SaveFlag, true); + // we'll clear the imageFilter for the actual draws in next(), so + // it will only be applied during the restore(). + fDoClearImageFilter = true; + } + if (fLooper) { fLooper->init(canvas); } } ~AutoDrawLooper() { + if (fDoClearImageFilter) { + fCanvas->internalRestore(); + } SkASSERT(fCanvas->getSaveCount() == fSaveCount); } @@ -309,6 +325,7 @@ private: SkDrawFilter* fFilter; const SkPaint* fPaint; int fSaveCount; + bool fDoClearImageFilter; bool fDone; }; @@ -318,8 +335,13 @@ bool AutoDrawLooper::next(SkDrawFilter::Type drawType) { return false; } - if (fLooper || fFilter) { + if (fLooper || fFilter || fDoClearImageFilter) { SkPaint* paint = fLazyPaint.set(fOrigPaint); + + if (fDoClearImageFilter) { + paint->setImageFilter(NULL); + } + if (fLooper && !fLooper->next(fCanvas, paint)) { fDone = true; return false; @@ -332,6 +354,11 @@ bool AutoDrawLooper::next(SkDrawFilter::Type drawType) { } } fPaint = paint; + + // if we only came in here for the imagefilter, mark us as done + if (!fLooper && !fFilter) { + fDone = true; + } } else { fDone = true; fPaint = &fOrigPaint; @@ -386,6 +413,13 @@ private: ////////// macros to place around the internal draw calls ////////////////// +#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \ +/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \ + AutoDrawLooper looper(this, paint, true); \ + while (looper.next(type)) { \ + SkAutoBounderCommit ac(fBounder); \ + SkDrawIter iter(this); + #define LOOPER_BEGIN(paint, type) \ /* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \ AutoDrawLooper looper(this, paint); \ @@ -749,6 +783,11 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) { + return this->internalSaveLayer(bounds, paint, flags, false); +} + +int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags flags, bool justForImageFilter) { // do this before we create the layer. We don't call the public save() since // that would invoke a possibly overridden virtual int count = this->internalSave(flags); @@ -764,6 +803,10 @@ int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SkLazyPaint lazyP; if (paint && paint->getImageFilter()) { if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) { + if (justForImageFilter) { + // early exit if the layer was just for the imageFilter + return count; + } SkPaint* p = lazyP.set(*paint); p->setImageFilter(NULL); paint = p; @@ -841,9 +884,9 @@ void SkCanvas::internalRestore() { if (NULL != layer) { if (layer->fNext) { const SkIPoint& origin = layer->fDevice->getOrigin(); - this->drawDevice(layer->fDevice, origin.x(), origin.y(), - layer->fPaint); - // reset this, since drawDevice will have set it to true + this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), + layer->fPaint); + // reset this, since internalDrawDevice will have set it to true fDeviceCMDirty = true; SkASSERT(fLayerCount > 0); @@ -905,43 +948,38 @@ class DeviceImageFilterProxy : public SkImageFilter::Proxy { public: DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {} - virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE; - virtual bool filterImage(SkImageFilter*, const SkBitmap& src, + virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE { + return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config, + w, h, false); + } + virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE { + return fDevice->canHandleImageFilter(filter); + } + virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src, const SkMatrix& ctm, - SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; - + SkBitmap* result, SkIPoint* offset) SK_OVERRIDE { + return fDevice->filterImage(filter, src, ctm, result, offset); + } + private: SkDevice* fDevice; }; -SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) { - return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config, - w, h, false); -} - -bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter, - const SkBitmap& src, - const SkMatrix& ctm, - SkBitmap* result, - SkIPoint* offset) { - return fDevice->filterImage(filter, src, ctm, result, offset); -} - -void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y, - const SkPaint* paint) { +void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y, + const SkPaint* paint) { SkPaint tmp; if (NULL == paint) { tmp.setDither(true); paint = &tmp; } - LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type) + LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) while (iter.next()) { SkDevice* dstDev = iter.fDevice; paint = &looper.paint(); SkImageFilter* filter = paint->getImageFilter(); SkIPoint pos = { x - iter.getX(), y - iter.getY() }; - if (filter) { + if (filter && !dstDev->canHandleImageFilter(filter)) { DeviceImageFilterProxy proxy(dstDev); SkBitmap dst; const SkBitmap& src = srcDev->accessBitmap(false); @@ -957,6 +995,40 @@ void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y, LOOPER_END } +void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, + const SkPaint* paint) { + SkDEBUGCODE(bitmap.validate();) + + if (reject_bitmap(bitmap)) { + return; + } + + SkPaint tmp; + if (NULL == paint) { + paint = &tmp; + } + + LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) + + while (iter.next()) { + paint = &looper.paint(); + SkImageFilter* filter = paint->getImageFilter(); + SkIPoint pos = { x - iter.getX(), y - iter.getY() }; + if (filter && !iter.fDevice->canHandleImageFilter(filter)) { + DeviceImageFilterProxy proxy(iter.fDevice); + SkBitmap dst; + if (filter->filterImage(&proxy, bitmap, *iter.fMatrix, &dst, &pos)) { + SkPaint tmp(*paint); + tmp.setImageFilter(NULL); + iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(), tmp); + } + } else { + iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint); + } + } + LOOPER_END +} + ///////////////////////////////////////////////////////////////////////////// bool SkCanvas::translate(SkScalar dx, SkScalar dy) { @@ -1575,28 +1647,6 @@ void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, this->internalDrawBitmapNine(bitmap, center, dst, paint); } -void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, - const SkPaint* paint) { - SkDEBUGCODE(bitmap.validate();) - - if (reject_bitmap(bitmap)) { - return; - } - - SkPaint tmp; - if (NULL == paint) { - paint = &tmp; - } - - LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type) - - while (iter.next()) { - iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(), - looper.paint()); - } - LOOPER_END -} - class SkDeviceFilteredPaint { public: SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) { diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index f1da2ef..b92f4e1 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -101,9 +101,13 @@ void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region, const SkClipStack& clipStack) { } -bool SkDevice::filterImage(SkImageFilter*, const SkBitmap& src, - const SkMatrix& ctm, - SkBitmap* result, SkIPoint* offset) { +bool SkDevice::canHandleImageFilter(SkImageFilter*) { + return false; +} + +bool SkDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, + const SkMatrix& ctm, SkBitmap* result, + SkIPoint* offset) { return false; } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 4917dcb..0ac4ed5 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1398,11 +1398,50 @@ void SkGpuDevice::internalDrawBitmap(const SkDraw& draw, fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m); } +static GrTexture* filter_texture(GrContext* context, GrTexture* texture, + SkImageFilter* filter, const GrRect& rect) { + GrAssert(filter); + + SkSize blurSize; + SkISize radius; + + const GrTextureDesc desc = { + kRenderTarget_GrTextureFlagBit, + rect.width(), + rect.height(), + kRGBA_8888_PM_GrPixelConfig, + {0} // samples + }; + + if (filter->asABlur(&blurSize)) { + GrAutoScratchTexture temp1, temp2; + texture = context->gaussianBlur(texture, &temp1, &temp2, rect, + blurSize.width(), + blurSize.height()); + texture->ref(); + } else if (filter->asADilate(&radius)) { + GrAutoScratchTexture temp1(context, desc), temp2(context, desc); + texture = context->applyMorphology(texture, rect, + temp1.texture(), temp2.texture(), + GrSamplerState::kDilate_Filter, + radius); + texture->ref(); + } else if (filter->asAnErode(&radius)) { + GrAutoScratchTexture temp1(context, desc), temp2(context, desc); + texture = context->applyMorphology(texture, rect, + temp1.texture(), temp2.texture(), + GrSamplerState::kErode_Filter, + radius); + texture->ref(); + } + return texture; +} + void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, int left, int top, const SkPaint& paint) { CHECK_SHOULD_DRAW(draw); - SkAutoLockPixels alp(bitmap); + SkAutoLockPixels alp(bitmap, !bitmap.getTexture()); if (!bitmap.getTexture() && !bitmap.readyToDraw()) { return; } @@ -1422,50 +1461,19 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, GrTexture* texture; sampler->reset(); SkAutoCachedTexture act(this, bitmap, sampler, &texture); - - SkImageFilter* imageFilter = paint.getImageFilter(); - SkSize blurSize; - SkISize radius; - if (NULL != imageFilter && imageFilter->asABlur(&blurSize)) { - GrAutoScratchTexture temp1, temp2; - GrTexture* blurTexture = fContext->gaussianBlur(texture, &temp1, &temp2, - GrRect::MakeWH(w, h), - blurSize.width(), - blurSize.height()); - texture = blurTexture; - grPaint.setTexture(kBitmapTextureIdx, texture); - } else if (NULL != imageFilter && imageFilter->asADilate(&radius)) { - const GrTextureDesc desc = { - kRenderTarget_GrTextureFlagBit, - w, - h, - kRGBA_8888_PM_GrPixelConfig, - {0} // samples - }; - GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc); - texture = fContext->applyMorphology(texture, GrRect::MakeWH(w, h), - temp1.texture(), temp2.texture(), - GrSamplerState::kDilate_Filter, - radius); - grPaint.setTexture(kBitmapTextureIdx, texture); - } else if (NULL != imageFilter && imageFilter->asAnErode(&radius)) { - const GrTextureDesc desc = { - kRenderTarget_GrTextureFlagBit, - w, - h, - kRGBA_8888_PM_GrPixelConfig, - {0} // samples - }; - GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc); - texture = fContext->applyMorphology(texture, GrRect::MakeWH(w, h), - temp1.texture(), temp2.texture(), - GrSamplerState::kErode_Filter, - radius); - grPaint.setTexture(kBitmapTextureIdx, texture); - } else { - grPaint.setTexture(kBitmapTextureIdx, texture); + grPaint.setTexture(kBitmapTextureIdx, texture); + + SkImageFilter* filter = paint.getImageFilter(); + if (NULL != filter) { + GrTexture* filteredTexture = filter_texture(fContext, texture, filter, + GrRect::MakeWH(w, h)); + if (filteredTexture) { + grPaint.setTexture(kBitmapTextureIdx, filteredTexture); + texture = filteredTexture; + filteredTexture->unref(); + } } - + fContext->drawRectToRect(grPaint, GrRect::MakeXYWH(GrIntToScalar(left), GrIntToScalar(top), @@ -1488,6 +1496,18 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev, GrTexture* devTex = grPaint.getTexture(0); SkASSERT(NULL != devTex); + SkImageFilter* filter = paint.getImageFilter(); + if (NULL != filter) { + GrRect rect = GrRect::MakeWH(devTex->width(), devTex->height()); + GrTexture* filteredTexture = filter_texture(fContext, devTex, filter, + rect); + if (filteredTexture) { + grPaint.setTexture(kBitmapTextureIdx, filteredTexture); + devTex = filteredTexture; + filteredTexture->unref(); + } + } + const SkBitmap& bm = dev->accessBitmap(false); int w = bm.width(); int h = bm.height(); @@ -1509,27 +1529,43 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev, fContext->drawRectToRect(grPaint, dstRect, srcRect); } -bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, - const SkMatrix& ctm, - SkBitmap* result, SkIPoint* offset) { +bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) { SkSize size; SkISize radius; if (!filter->asABlur(&size) && !filter->asADilate(&radius) && !filter->asAnErode(&radius)) { return false; } - SkDevice* dev = this->createCompatibleDevice(SkBitmap::kARGB_8888_Config, - src.width(), - src.height(), - false); - if (NULL == dev) { + return true; +} + +bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, + const SkMatrix& ctm, + SkBitmap* result, SkIPoint* offset) { + // want explicitly our impl, so guard against a subclass of us overriding it + if (!this->SkGpuDevice::canHandleImageFilter(filter)) { + return false; + } + + SkAutoLockPixels alp(src, !src.getTexture()); + if (!src.getTexture() && !src.readyToDraw()) { return false; } - SkAutoUnref aur(dev); - SkCanvas canvas(dev); - SkPaint paint; - paint.setImageFilter(filter); - canvas.drawSprite(src, 0, 0, &paint); - *result = dev->accessBitmap(false); + + GrPaint paint; + paint.reset(); + + GrSamplerState* sampler = paint.textureSampler(kBitmapTextureIdx); + + GrTexture* texture; + SkAutoCachedTexture act(this, src, sampler, &texture); + + result->setConfig(src.config(), src.width(), src.height()); + GrRect rect = GrRect::MakeWH(src.width(), src.height()); + GrTexture* resultTexture = filter_texture(fContext, texture, filter, rect); + if (resultTexture) { + result->setPixelRef(new SkGrTexturePixelRef(resultTexture))->unref(); + resultTexture->unref(); + } return true; } -- 2.7.4