diff options
-rw-r--r-- | gm/imagefilterscropped.cpp | 176 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/core/SkImageFilter.h | 24 | ||||
-rw-r--r-- | include/core/SkRect.h | 11 | ||||
-rw-r--r-- | include/effects/SkBicubicImageFilter.h | 3 | ||||
-rw-r--r-- | include/effects/SkBlurImageFilter.h | 5 | ||||
-rwxr-xr-x | include/effects/SkColorFilterImageFilter.h | 8 | ||||
-rw-r--r-- | src/core/SkImageFilter.cpp | 23 | ||||
-rw-r--r-- | src/effects/SkBlurImageFilter.cpp | 71 | ||||
-rwxr-xr-x | src/effects/SkColorFilterImageFilter.cpp | 27 | ||||
-rw-r--r-- | src/effects/SkGpuBlurUtils.cpp | 47 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 14 | ||||
-rw-r--r-- | src/gpu/effects/GrConvolutionEffect.cpp | 35 | ||||
-rw-r--r-- | src/gpu/effects/GrConvolutionEffect.h | 23 |
14 files changed, 395 insertions, 73 deletions
diff --git a/gm/imagefilterscropped.cpp b/gm/imagefilterscropped.cpp new file mode 100644 index 0000000000..f1ede2fefd --- /dev/null +++ b/gm/imagefilterscropped.cpp @@ -0,0 +1,176 @@ +/* + * 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 "SkColorFilterImageFilter.h" +#include "SkTestImageFilters.h" + +/////////////////////////////////////////////////////////////////////////////// + +static void draw_paint(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + paint.setColor(SK_ColorBLACK); + canvas->save(); + canvas->clipRect(r); + canvas->drawPaint(paint); + canvas->restore(); +} + +static void draw_path(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setColor(SK_ColorMAGENTA); + paint.setImageFilter(imf); + paint.setAntiAlias(true); + canvas->save(); + canvas->clipRect(r); + canvas->drawCircle(r.centerX(), r.centerY(), r.width()*2/5, paint); + canvas->restore(); +} + +static void draw_text(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + paint.setColor(SK_ColorGREEN); + paint.setAntiAlias(true); + paint.setTextSize(r.height()/2); + paint.setTextAlign(SkPaint::kCenter_Align); + canvas->save(); + canvas->clipRect(r); + canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint); + canvas->restore(); +} + +static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()); + bm.allocPixels(); + bm.eraseColor(SK_ColorTRANSPARENT); + SkCanvas c(bm); + draw_path(&c, r, NULL); + + paint.setImageFilter(imf); + canvas->save(); + canvas->clipRect(r); + canvas->drawBitmap(bm, 0, 0, &paint); + canvas->restore(); +} + +static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()); + bm.allocPixels(); + bm.eraseColor(SK_ColorRED); + SkCanvas c(bm); + + SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44); + paint.setColor(SK_ColorGREEN); + c.drawRect(SkRect::Make(cropRect), paint); + + paint.setImageFilter(imf); + SkPoint loc = { r.fLeft, r.fTop }; + canvas->getTotalMatrix().mapPoints(&loc, 1); + canvas->drawSprite(bm, + SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY), + &paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +class ImageFiltersCroppedGM : public skiagm::GM { +public: + ImageFiltersCroppedGM () {} + +protected: + + virtual SkString onShortName() { + return SkString("imagefilterscropped"); + } + + 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 uint32_t onGetFlags() const { + // Because of the use of drawSprite, this test is excluded + // from scaled replay tests because drawSprite ignores the + // reciprocal scale that is applied at record time, which is + // the intended behavior of drawSprite. + return kSkipScaledReplay_Flag; + } + + virtual void onDraw(SkCanvas* canvas) { + void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = { + draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text + }; + + SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorRED, + SkXfermode::kSrcIn_Mode); + SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44); + SkIRect bogusRect = SkIRect::MakeXYWH(-100, -100, 10, 10); + + SkImageFilter* filters[] = { + NULL, + SkColorFilterImageFilter::Create(cf, NULL, &cropRect), + new SkBlurImageFilter(8.0f, 0.0f, NULL, &cropRect), + new SkBlurImageFilter(0.0f, 8.0f, NULL, &cropRect), + new SkBlurImageFilter(8.0f, 8.0f, NULL, &cropRect), + new SkBlurImageFilter(8.0f, 8.0f, NULL, &bogusRect), + SkColorFilterImageFilter::Create(cf, NULL, &bogusRect), + }; + 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(filters); ++i) { + canvas->save(); + for (size_t j = 0; j < SK_ARRAY_COUNT(drawProc); ++j) { + drawProc[j](canvas, r, filters[i]); + canvas->translate(0, DY); + } + canvas->restore(); + canvas->translate(DX, 0); + } + + for(size_t j = 0; j < SK_ARRAY_COUNT(filters); ++j) { + SkSafeUnref(filters[j]); + } + } + +private: + typedef GM INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static skiagm::GM* MyFactory(void*) { return new ImageFiltersCroppedGM; } +static skiagm::GMRegistry reg(MyFactory); diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 9d335496eb..46b1bd5bfc 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -63,6 +63,7 @@ '../gm/lighting.cpp', '../gm/image.cpp', '../gm/imagefiltersbase.cpp', + '../gm/imagefilterscropped.cpp', '../gm/imagefiltersgraph.cpp', '../gm/internal_links.cpp', '../gm/lcdtext.cpp', diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index fe383ae9e3..e467761a1f 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -9,13 +9,13 @@ #define SkImageFilter_DEFINED #include "SkFlattenable.h" +#include "SkRect.h" class SkBitmap; class SkColorFilter; class SkDevice; class SkMatrix; struct SkIPoint; -struct SkIRect; class SkShader; class GrEffectRef; class GrTexture; @@ -139,14 +139,25 @@ public: return fInputs[i]; } + /** + * Returns the crop rectangle of this filter. This is set at construction + * time, and determines which pixels from the input image will + * be processed. The size of this rectangle should be used as the size + * of the destination image. The origin of this rect should be used to + * offset access to the input images, and should also be added to the + * "offset" parameter in onFilterImage and filterImageGPU(). (The latter + * ensures that the resulting buffer is drawn in the correct location.) + */ + const SkIRect& cropRect() const { return fCropRect; } + protected: - SkImageFilter(int inputCount, SkImageFilter** inputs); + SkImageFilter(int inputCount, SkImageFilter** inputs, const SkIRect* cropRect = NULL); // Convenience constructor for 1-input filters. - explicit SkImageFilter(SkImageFilter* input); + explicit SkImageFilter(SkImageFilter* input, const SkIRect* cropRect = NULL); // Convenience constructor for 2-input filters. - SkImageFilter(SkImageFilter* input1, SkImageFilter* input2); + SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const SkIRect* cropRect = NULL); virtual ~SkImageFilter(); @@ -160,10 +171,15 @@ protected: // Default impl copies src into dst and returns true virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*); + // Sets rect to the intersection of rect and the crop rect. If there + // is no overlap, returns false and leaves rect unchanged. + bool applyCropRect(SkIRect* rect) const; + private: typedef SkFlattenable INHERITED; int fInputCount; SkImageFilter** fInputs; + SkIRect fCropRect; }; #endif diff --git a/include/core/SkRect.h b/include/core/SkRect.h index b2f515129d..9f3b59a38f 100644 --- a/include/core/SkRect.h +++ b/include/core/SkRect.h @@ -26,6 +26,12 @@ struct SK_API SkIRect { return r; } + static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() { + SkIRect r; + r.setLargest(); + return r; + } + static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) { SkIRect r; r.set(0, 0, w, h); @@ -94,6 +100,11 @@ struct SK_API SkIRect { */ bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + bool isLargest() const { return SK_MinS32 == fLeft && + SK_MinS32 == fTop && + SK_MaxS32 == fRight && + SK_MaxS32 == fBottom; } + friend bool operator==(const SkIRect& a, const SkIRect& b) { return !memcmp(&a, &b, sizeof(a)); } diff --git a/include/effects/SkBicubicImageFilter.h b/include/effects/SkBicubicImageFilter.h index 75cd27df74..6696365433 100644 --- a/include/effects/SkBicubicImageFilter.h +++ b/include/effects/SkBicubicImageFilter.h @@ -27,7 +27,8 @@ public: passed to filterImage() is used instead. */ - SkBicubicImageFilter(const SkSize& scale, const SkScalar coefficients[16], + SkBicubicImageFilter(const SkSize& scale, + const SkScalar coefficients[16], SkImageFilter* input = NULL); static SkBicubicImageFilter* CreateMitchell(const SkSize& scale, SkImageFilter* input = NULL); virtual ~SkBicubicImageFilter(); diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h index 56b1f3543e..f2677353e0 100644 --- a/include/effects/SkBlurImageFilter.h +++ b/include/effects/SkBlurImageFilter.h @@ -13,7 +13,10 @@ class SK_API SkBlurImageFilter : public SkImageFilter { public: - SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = NULL); + SkBlurImageFilter(SkScalar sigmaX, + SkScalar sigmaY, + SkImageFilter* input = NULL, + const SkIRect* cropRect = NULL); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilter) diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h index 2e5e59c3c0..314ab070b6 100755 --- a/include/effects/SkColorFilterImageFilter.h +++ b/include/effects/SkColorFilterImageFilter.h @@ -14,7 +14,9 @@ class SkColorFilter; class SK_API SkColorFilterImageFilter : public SkImageFilter { public: - static SkColorFilterImageFilter* Create(SkColorFilter* cf, SkImageFilter* input = NULL); + static SkColorFilterImageFilter* Create(SkColorFilter* cf, + SkImageFilter* input = NULL, + const SkIRect* cropRect = NULL); virtual ~SkColorFilterImageFilter(); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter) @@ -29,7 +31,9 @@ protected: virtual bool asColorFilter(SkColorFilter**) const SK_OVERRIDE; private: - SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input); + SkColorFilterImageFilter(SkColorFilter* cf, + SkImageFilter* input, + const SkIRect* cropRect = NULL); SkColorFilter* fColorFilter; typedef SkImageFilter INHERITED; diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp index ddd6449490..ff060a1a52 100644 --- a/src/core/SkImageFilter.cpp +++ b/src/core/SkImageFilter.cpp @@ -18,22 +18,27 @@ SK_DEFINE_INST_COUNT(SkImageFilter) -SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs) - : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) { +SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const SkIRect* cropRect) + : fInputCount(inputCount), + fInputs(new SkImageFilter*[inputCount]), + fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) { for (int i = 0; i < inputCount; ++i) { fInputs[i] = inputs[i]; SkSafeRef(fInputs[i]); } } -SkImageFilter::SkImageFilter(SkImageFilter* input) - : fInputCount(1), fInputs(new SkImageFilter*[1]) { +SkImageFilter::SkImageFilter(SkImageFilter* input, const SkIRect* cropRect) + : fInputCount(1), + fInputs(new SkImageFilter*[1]), + fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) { fInputs[0] = input; SkSafeRef(fInputs[0]); } -SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2) - : fInputCount(2), fInputs(new SkImageFilter*[2]) { +SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const SkIRect* cropRect) + : fInputCount(2), fInputs(new SkImageFilter*[2]), + fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) { fInputs[0] = input1; fInputs[1] = input2; SkSafeRef(fInputs[0]); @@ -56,6 +61,7 @@ SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer) fInputs[i] = NULL; } } + buffer.readIRect(&fCropRect); } void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { @@ -67,6 +73,7 @@ void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { buffer.writeFlattenable(input); } } + buffer.writeIRect(fCropRect); } bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, @@ -137,6 +144,10 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* #endif } +bool SkImageFilter::applyCropRect(SkIRect* rect) const { + return rect->intersect(fCropRect); +} + bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) { *dst = src; diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp index 157a0c3335..0f0c809e55 100644 --- a/src/effects/SkBlurImageFilter.cpp +++ b/src/effects/SkBlurImageFilter.cpp @@ -21,8 +21,11 @@ SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer) fSigma.fHeight = buffer.readScalar(); } -SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input) - : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) { +SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, + SkScalar sigmaY, + SkImageFilter* input, + const SkIRect* cropRect) + : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) { SkASSERT(sigmaX >= 0 && sigmaY >= 0); } @@ -33,13 +36,13 @@ void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { } static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize, - int leftOffset, int rightOffset) + int leftOffset, int rightOffset, const SkIRect& bounds) { - int width = src.width(), height = src.height(); + int width = bounds.width(), height = bounds.height(); int rightBorder = SkMin32(rightOffset + 1, width); for (int y = 0; y < height; ++y) { int sumA = 0, sumR = 0, sumG = 0, sumB = 0; - SkPMColor* p = src.getAddr32(0, y); + SkPMColor* p = src.getAddr32(bounds.fLeft, y + bounds.fTop); for (int i = 0; i < rightBorder; ++i) { sumA += SkGetPackedA32(*p); sumR += SkGetPackedR32(*p); @@ -48,7 +51,7 @@ static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize, p++; } - const SkColor* sptr = src.getAddr32(0, y); + const SkColor* sptr = src.getAddr32(bounds.fLeft, bounds.fTop + y); SkColor* dptr = dst->getAddr32(0, y); for (int x = 0; x < width; ++x) { *dptr = SkPackARGB32(sumA / kernelSize, @@ -76,15 +79,15 @@ static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize, } static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize, - int topOffset, int bottomOffset) + int topOffset, int bottomOffset, const SkIRect& bounds) { - int width = src.width(), height = src.height(); + int width = bounds.width(), height = bounds.height(); int bottomBorder = SkMin32(bottomOffset + 1, height); int srcStride = src.rowBytesAsPixels(); int dstStride = dst->rowBytesAsPixels(); for (int x = 0; x < width; ++x) { int sumA = 0, sumR = 0, sumG = 0, sumB = 0; - SkColor* p = src.getAddr32(x, 0); + SkColor* p = src.getAddr32(bounds.fLeft + x, bounds.fTop); for (int i = 0; i < bottomBorder; ++i) { sumA += SkGetPackedA32(*p); sumR += SkGetPackedR32(*p); @@ -93,7 +96,7 @@ static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize, p += srcStride; } - const SkColor* sptr = src.getAddr32(x, 0); + const SkColor* sptr = src.getAddr32(bounds.fLeft + x, bounds.fTop); SkColor* dptr = dst->getAddr32(x, 0); for (int y = 0; y < height; ++y) { *dptr = SkPackARGB32(sumA / kernelSize, @@ -153,7 +156,14 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy, return false; } - dst->setConfig(src.config(), src.width(), src.height()); + SkIRect srcBounds, dstBounds; + src.getBounds(&srcBounds); + if (!this->applyCropRect(&srcBounds)) { + return false; + } + + dst->setConfig(src.config(), srcBounds.width(), srcBounds.height()); + dst->getBounds(&dstBounds); dst->allocPixels(); int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; @@ -176,21 +186,23 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy, } if (kernelSizeX > 0 && kernelSizeY > 0) { - boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX); - boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY); - boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); - boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY); - boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX); - boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); + boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX, srcBounds); + boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY, dstBounds); + boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds); + boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY, dstBounds); + boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX, dstBounds); + boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds); } else if (kernelSizeX > 0) { - boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX); - boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); - boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX); + boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX, srcBounds); + boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds); + boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX, dstBounds); } else if (kernelSizeY > 0) { - boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY); - boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY); - boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); + boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY, srcBounds); + boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY, dstBounds); + boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds); } + offset->fX += srcBounds.fLeft; + offset->fY += srcBounds.fTop; return true; } @@ -202,12 +214,17 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitm return false; } GrTexture* source = input.getTexture(); - SkRect rect; + SkIRect rect; src.getBounds(&rect); - SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), - source, false, rect, + if (!this->applyCropRect(&rect)) { + return false; + } + SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), + source, false, SkRect::Make(rect), fSigma.width(), fSigma.height())); - return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result); + offset->fX += rect.fLeft; + offset->fY += rect.fTop; + return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result); #else SkDEBUGFAIL("Should not call in GPU-less build"); return false; diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp index 1d3cfee1c3..a24934e0e2 100755 --- a/src/effects/SkColorFilterImageFilter.cpp +++ b/src/effects/SkColorFilterImageFilter.cpp @@ -57,7 +57,7 @@ bool matrix_needs_clamping(SkScalar matrix[20]) { }; SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf, - SkImageFilter* input) { + SkImageFilter* input, const SkIRect* cropRect) { SkASSERT(cf); SkScalar colorMatrix[20], inputMatrix[20]; SkColorFilter* inputColorFilter; @@ -69,13 +69,15 @@ SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf, SkScalar combinedMatrix[20]; mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix); SkAutoTUnref<SkColorFilter> newCF(SkNEW_ARGS(SkColorMatrixFilter, (combinedMatrix))); - return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0))); + return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect)); } } - return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input)); + return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect)); } -SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input) : INHERITED(input), fColorFilter(cf) { +SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf, + SkImageFilter* input, const SkIRect* cropRect) + : INHERITED(input, cropRect), fColorFilter(cf) { SkASSERT(cf); SkSafeRef(cf); } @@ -103,22 +105,31 @@ bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& sourc return false; } - SkAutoTUnref<SkDevice> device(proxy->createDevice(src.width(), src.height())); + SkIRect bounds; + src.getBounds(&bounds); + if (!this->applyCropRect(&bounds)) { + return false; + } + + SkAutoTUnref<SkDevice> device(proxy->createDevice(bounds.width(), bounds.height())); SkCanvas canvas(device.get()); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setColorFilter(fColorFilter); - canvas.drawSprite(src, 0, 0, &paint); + canvas.drawSprite(src, -bounds.fLeft, -bounds.fTop, &paint); *result = device.get()->accessBitmap(false); + loc->fX += bounds.fLeft; + loc->fY += bounds.fTop; return true; } bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const { - if (filter) { + if (filter && cropRect().isLargest()) { *filter = fColorFilter; fColorFilter->ref(); + return true; } - return true; + return false; } diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp index 479648850c..5b52ad3cea 100644 --- a/src/effects/SkGpuBlurUtils.cpp +++ b/src/effects/SkGpuBlurUtils.cpp @@ -11,6 +11,7 @@ #if SK_SUPPORT_GPU #include "effects/GrConvolutionEffect.h" +#include "effects/GrTextureDomainEffect.h" #include "GrContext.h" #endif @@ -40,18 +41,29 @@ static float adjust_sigma(float sigma, int *scaleFactor, int *radius) { static void convolve_gaussian(GrContext* context, GrTexture* texture, - const SkRect& rect, + const SkRect& srcRect, + const SkRect& dstRect, float sigma, int radius, Gr1DKernelEffect::Direction direction) { GrPaint paint; + paint.reset(); + float cropRect[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; + if (direction == Gr1DKernelEffect::kX_Direction) { + cropRect[0] = SkScalarToFloat(srcRect.left()) / texture->width(); + cropRect[1] = SkScalarToFloat(srcRect.right()) / texture->width(); + } else { + cropRect[2] = SkScalarToFloat(srcRect.top()) / texture->height(); + cropRect[3] = SkScalarToFloat(srcRect.bottom()) / texture->height(); + } SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture, direction, radius, - sigma)); + sigma, + cropRect)); paint.addColorEffect(conv); - context->drawRect(paint, rect); + context->drawRectToRect(paint, dstRect, srcRect); } GrTexture* GaussianBlur(GrContext* context, @@ -79,7 +91,7 @@ GrTexture* GaussianBlur(GrContext* context, scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); - GrContext::AutoClip acs(context, srcRect); + GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height())); GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || @@ -104,10 +116,25 @@ GrTexture* GaussianBlur(GrContext* context, matrix.setIDiv(srcTexture->width(), srcTexture->height()); context->setRenderTarget(dstTexture->asRenderTarget()); SkRect dstRect(srcRect); + if (i == 1) { + dstRect.offset(-dstRect.fLeft, -dstRect.fTop); + SkRect domain; + matrix.mapRect(&domain, rect); + domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, + i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); + SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create( + srcTexture, + matrix, + domain, + GrTextureDomainEffect::kDecal_WrapMode, + true)); + paint.addColorEffect(effect); + } else { + GrTextureParams params(SkShader::kClamp_TileMode, true); + paint.addColorTextureEffect(srcTexture, matrix, params); + } scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); - GrTextureParams params(SkShader::kClamp_TileMode, true); - paint.addColorTextureEffect(srcTexture, matrix, params); context->drawRectToRect(paint, dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; @@ -126,9 +153,11 @@ GrTexture* GaussianBlur(GrContext* context, context->clear(&clearRect, 0x0); } context->setRenderTarget(dstTexture->asRenderTarget()); - convolve_gaussian(context, srcTexture, srcRect, sigmaX, radiusX, + SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); + convolve_gaussian(context, srcTexture, srcRect, dstRect, sigmaX, radiusX, Gr1DKernelEffect::kX_Direction); srcTexture = dstTexture; + srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } @@ -142,9 +171,11 @@ GrTexture* GaussianBlur(GrContext* context, } context->setRenderTarget(dstTexture->asRenderTarget()); - convolve_gaussian(context, srcTexture, srcRect, sigmaY, radiusY, + SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); + convolve_gaussian(context, srcTexture, srcRect, dstRect, sigmaY, radiusY, Gr1DKernelEffect::kY_Direction); srcTexture = dstTexture; + srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index eaac5ab12c..4456f513c4 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1387,7 +1387,7 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, SkAutoCachedTexture act(this, bitmap, NULL, &texture); SkImageFilter* filter = paint.getImageFilter(); - SkIPoint offset = SkIPoint::Make(0, 0); + SkIPoint offset = SkIPoint::Make(left, top); // This bitmap will own the filtered result as a texture. SkBitmap filteredBitmap; @@ -1396,6 +1396,8 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, texture = (GrTexture*) filteredBitmap.getTexture(); w = filteredBitmap.width(); h = filteredBitmap.height(); + } else { + return; } } @@ -1407,12 +1409,12 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, } fContext->drawRectToRect(grPaint, - SkRect::MakeXYWH(SkIntToScalar(left), - SkIntToScalar(top), - SkIntToScalar(w), - SkIntToScalar(h)), SkRect::MakeXYWH(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY), + SkIntToScalar(w), + SkIntToScalar(h)), + SkRect::MakeXYWH(0, + 0, SK_Scalar1 * w / texture->width(), SK_Scalar1 * h / texture->height())); } @@ -1481,6 +1483,8 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device, h = filteredBitmap.height(); x += offset.fX; y += offset.fY; + } else { + return; } } diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp index 380581fae3..5682b9c65c 100644 --- a/src/gpu/effects/GrConvolutionEffect.cpp +++ b/src/gpu/effects/GrConvolutionEffect.cpp @@ -37,6 +37,7 @@ private: int fRadius; UniformHandle fKernelUni; UniformHandle fImageIncrementUni; + UniformHandle fCropRectUni; GrGLEffectMatrix fEffectMatrix; typedef GrGLEffect INHERITED; @@ -47,6 +48,7 @@ GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& facto : INHERITED(factory) , fKernelUni(kInvalidUniformHandle) , fImageIncrementUni(kInvalidUniformHandle) + , fCropRectUni(kInvalidUniformHandle) , fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) { const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>(); fRadius = c.radius(); @@ -62,6 +64,8 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder, fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords); fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, kVec2f_GrSLType, "ImageIncrement"); + fCropRectUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, + kVec4f_GrSLType, "CropRect"); fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType, kFloat_GrSLType, "Kernel", this->width()); @@ -70,6 +74,7 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder, int width = this ->width(); const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni); const char* imgInc = builder->getUniformCStr(fImageIncrementUni); + const char* cropRect = builder->getUniformCStr(fCropRectUni); builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc); @@ -81,9 +86,11 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder, kernel.appendArrayAccess(index.c_str(), &kernelIndex); builder->fsCodeAppendf("\t\t%s += ", outputColor); builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord"); - builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str()); + builder->fsCodeAppendf(" * float(coord.x >= %s.x && coord.x <= %s.y && coord.y >= %s.z && coord.y <= %s.w) * %s;\n", + cropRect, cropRect, cropRect, cropRect, kernelIndex.c_str()); builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc); } + SkString modulate; GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor); builder->fsCodeAppend(modulate.c_str()); @@ -96,17 +103,26 @@ void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman, // the code we generated was for a specific kernel radius GrAssert(conv.radius() == fRadius); float imageIncrement[2] = { 0 }; + float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f; switch (conv.direction()) { case Gr1DKernelEffect::kX_Direction: imageIncrement[0] = 1.0f / texture.width(); break; case Gr1DKernelEffect::kY_Direction: - imageIncrement[1] = 1.0f / texture.height(); + imageIncrement[1] = ySign / texture.height(); break; default: GrCrash("Unknown filter direction."); } uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement); + float c[4]; + memcpy(c, conv.cropRect(), sizeof(c)); + if (texture.origin() != kTopLeft_GrSurfaceOrigin) { + float tmp = 1.0f - c[2]; + c[2] = 1.0f - c[3]; + c[3] = tmp; + } + uman.set4fv(fCropRectUni, 0, 1, c); uman.set1fv(fKernelUni, 0, this->width(), conv.kernel()); fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0)); } @@ -128,7 +144,8 @@ GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffe GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, Direction direction, int radius, - const float* kernel) + const float* kernel, + float cropRect[4]) : Gr1DKernelEffect(texture, direction, radius) { GrAssert(radius <= kMaxKernelRadius); GrAssert(NULL != kernel); @@ -136,12 +153,14 @@ GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, for (int i = 0; i < width; i++) { fKernel[i] = kernel[i]; } + memcpy(fCropRect, cropRect, sizeof(fCropRect)); } GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, Direction direction, int radius, - float gaussianSigma) + float gaussianSigma, + float cropRect[4]) : Gr1DKernelEffect(texture, direction, radius) { GrAssert(radius <= kMaxKernelRadius); int width = this->width(); @@ -160,6 +179,7 @@ GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, for (int i = 0; i < width; ++i) { fKernel[i] *= scale; } + memcpy(fCropRect, cropRect, sizeof(fCropRect)); } GrConvolutionEffect::~GrConvolutionEffect() { @@ -174,6 +194,7 @@ bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const { return (this->texture(0) == s.texture(0) && this->radius() == s.radius() && this->direction() == s.direction() && + 0 == memcmp(fCropRect, s.fCropRect, sizeof(fCropRect)) && 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float))); } @@ -190,9 +211,13 @@ GrEffectRef* GrConvolutionEffect::TestCreate(SkMWCRandom* random, Direction dir = random->nextBool() ? kX_Direction : kY_Direction; int radius = random->nextRangeU(1, kMaxKernelRadius); float kernel[kMaxKernelRadius]; + float cropRect[4]; for (int i = 0; i < kMaxKernelRadius; ++i) { kernel[i] = random->nextSScalar1(); } + for (int i = 0; i < 4; ++i) { + cropRect[i] = random->nextF(); + } - return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel); + return GrConvolutionEffect::Create(textures[texIdx], dir, radius, kernel, cropRect); } diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h index e4faa945b8..f75cfe9155 100644 --- a/src/gpu/effects/GrConvolutionEffect.h +++ b/src/gpu/effects/GrConvolutionEffect.h @@ -22,11 +22,16 @@ class GrConvolutionEffect : public Gr1DKernelEffect { public: /// Convolve with an arbitrary user-specified kernel - static GrEffectRef* Create(GrTexture* tex, Direction dir, int halfWidth, const float* kernel) { + static GrEffectRef* Create(GrTexture* tex, + Direction dir, + int halfWidth, + const float* kernel, + float cropRect[4]) { AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex, dir, halfWidth, - kernel))); + kernel, + cropRect))); return CreateEffectRef(effect); } @@ -34,11 +39,13 @@ public: static GrEffectRef* CreateGaussian(GrTexture* tex, Direction dir, int halfWidth, - float gaussianSigma) { + float gaussianSigma, + float cropRect[4]) { AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex, dir, halfWidth, - gaussianSigma))); + gaussianSigma, + cropRect))); return CreateEffectRef(effect); } @@ -46,6 +53,8 @@ public: const float* kernel() const { return fKernel; } + const float* cropRect() const { return fCropRect; } + static const char* Name() { return "Convolution"; } typedef GrGLConvolutionEffect GLEffect; @@ -72,15 +81,17 @@ public: protected: float fKernel[kMaxKernelWidth]; + float fCropRect[4]; private: GrConvolutionEffect(GrTexture*, Direction, - int halfWidth, const float* kernel); + int halfWidth, const float* kernel, float cropRect[4]); /// Convolve with a Gaussian kernel GrConvolutionEffect(GrTexture*, Direction, int halfWidth, - float gaussianSigma); + float gaussianSigma, + float cropRect[4]); virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; |