From 8640d5024d57da5508bdf7585849e3b1f1cb365b Mon Sep 17 00:00:00 2001 From: "senorblanco@chromium.org" Date: Tue, 25 Sep 2012 14:32:42 +0000 Subject: This patch adds support for optional processing of the alpha channel in the matrix convolution filter. Test cases are added to the GM and the bench. NOTE: This will require rebaselining the matrixconvolution GM, so it will likely turn the bots red until that is done. https://codereview.appspot.com/6547049/ git-svn-id: http://skia.googlecode.com/svn/trunk@5661 2bbb7eff-a529-9590-31e7-b0007b416f81 --- bench/MatrixConvolutionBench.cpp | 12 +++-- gm/matrixconvolution.cpp | 14 +++--- include/effects/SkMatrixConvolutionImageFilter.h | 8 ++- src/effects/SkMatrixConvolutionImageFilter.cpp | 62 +++++++++++++++++++++--- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/bench/MatrixConvolutionBench.cpp b/bench/MatrixConvolutionBench.cpp index 1f6a004270..dfa05dbd35 100644 --- a/bench/MatrixConvolutionBench.cpp +++ b/bench/MatrixConvolutionBench.cpp @@ -15,7 +15,7 @@ class MatrixConvolutionBench : public SkBenchmark { SkMatrixConvolutionImageFilter::TileMode fTileMode; public: - MatrixConvolutionBench(void* param, SkMatrixConvolutionImageFilter::TileMode tileMode) + MatrixConvolutionBench(void* param, SkMatrixConvolutionImageFilter::TileMode tileMode, bool convolveAlpha) : INHERITED(param), fName("matrixconvolution") { SkISize kernelSize = SkISize::Make(3, 3); SkScalar kernel[9] = { @@ -25,7 +25,7 @@ public: }; SkScalar gain = SkFloatToScalar(0.3f), bias = SkIntToScalar(100); SkIPoint target = SkIPoint::Make(1, 1); - fFilter = new SkMatrixConvolutionImageFilter(kernelSize, kernel, gain, bias, target, tileMode); + fFilter = new SkMatrixConvolutionImageFilter(kernelSize, kernel, gain, bias, target, tileMode, convolveAlpha); } ~MatrixConvolutionBench() { @@ -56,10 +56,12 @@ private: SkString fName; }; -static SkBenchmark* Fact00(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClamp_TileMode); } -static SkBenchmark* Fact01(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kRepeat_TileMode); } -static SkBenchmark* Fact02(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode); } +static SkBenchmark* Fact00(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClamp_TileMode, true); } +static SkBenchmark* Fact01(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kRepeat_TileMode, true); } +static SkBenchmark* Fact02(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, true); } +static SkBenchmark* Fact03(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, false); } static BenchRegistry gReg00(Fact00); static BenchRegistry gReg01(Fact01); static BenchRegistry gReg02(Fact02); +static BenchRegistry gReg03(Fact03); diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp index 6c69fac485..0a0afa492d 100644 --- a/gm/matrixconvolution.cpp +++ b/gm/matrixconvolution.cpp @@ -36,10 +36,10 @@ protected: } virtual SkISize onISize() { - return make_isize(300, 300); + return make_isize(400, 300); } - void draw(SkCanvas* canvas, int x, int y, const SkIPoint& target, SkMatrixConvolutionImageFilter::TileMode tileMode) { + void draw(SkCanvas* canvas, int x, int y, const SkIPoint& target, SkMatrixConvolutionImageFilter::TileMode tileMode, bool convolveAlpha) { SkScalar kernel[9] = { SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1), @@ -48,7 +48,7 @@ protected: SkISize kernelSize = SkISize::Make(3, 3); SkScalar gain = SkFloatToScalar(0.3f), bias = SkIntToScalar(100); SkPaint paint; - SkAutoTUnref filter(SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias, target, tileMode))); + SkAutoTUnref filter(SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias, target, tileMode, convolveAlpha))); paint.setImageFilter(filter); canvas->drawSprite(fBitmap, x, y, &paint); } @@ -62,10 +62,12 @@ protected: SkIPoint target = SkIPoint::Make(1, 1); for (target.fY = 0; target.fY < 3; ++target.fY) { int y = target.fY * 100 + 10; - draw(canvas, 10, y, target, SkMatrixConvolutionImageFilter::kClamp_TileMode); - draw(canvas, 110, y, target, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode); - draw(canvas, 210, y, target, SkMatrixConvolutionImageFilter::kRepeat_TileMode); + draw(canvas, 10, y, target, SkMatrixConvolutionImageFilter::kClamp_TileMode, true); + draw(canvas, 110, y, target, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, true); + draw(canvas, 210, y, target, SkMatrixConvolutionImageFilter::kRepeat_TileMode, true); } + target.fY = 1; + draw(canvas, 310, 10, target, SkMatrixConvolutionImageFilter::kClamp_TileMode, false); } private: diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h index 035b863a55..a938fd0b26 100644 --- a/include/effects/SkMatrixConvolutionImageFilter.h +++ b/include/effects/SkMatrixConvolutionImageFilter.h @@ -42,11 +42,14 @@ public: target of {1, 1}). @param tileMode How accesses outside the image are treated. (@see TileMode). + @param convolveAlpha If true, all channels are convolved. If false, + only the RGB channels are convolved, and + alpha is copied from the source image. @param input The input image filter. If NULL, the src bitmap passed to filterImage() is used instead. */ - SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, SkImageFilter* input = NULL); + SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, bool convolveAlpha, SkImageFilter* input = NULL); virtual ~SkMatrixConvolutionImageFilter(); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMatrixConvolutionImageFilter) @@ -65,8 +68,11 @@ private: SkScalar fBias; SkIPoint fTarget; TileMode fTileMode; + bool fConvolveAlpha; typedef SkSingleInputImageFilter INHERITED; + template + void filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect); template void filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect); void filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect); diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp index 852412bb91..1c6f980b41 100644 --- a/src/effects/SkMatrixConvolutionImageFilter.cpp +++ b/src/effects/SkMatrixConvolutionImageFilter.cpp @@ -10,14 +10,16 @@ #include "SkColorPriv.h" #include "SkFlattenableBuffers.h" #include "SkRect.h" +#include "SkUnPreMultiply.h" -SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, SkImageFilter* input) +SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, bool convolveAlpha, SkImageFilter* input) : INHERITED(input), fKernelSize(kernelSize), fGain(gain), fBias(bias), fTarget(target), - fTileMode(tileMode) { + fTileMode(tileMode), + fConvolveAlpha(convolveAlpha) { uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight; fKernel = SkNEW_ARRAY(SkScalar, size); memcpy(fKernel, kernel, size * sizeof(SkScalar)); @@ -37,6 +39,7 @@ SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableRead fTarget.fX = buffer.readScalar(); fTarget.fY = buffer.readScalar(); fTileMode = (TileMode) buffer.readInt(); + fConvolveAlpha = buffer.readBool(); } void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { @@ -49,6 +52,7 @@ void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) c buffer.writeScalar(fTarget.fX); buffer.writeScalar(fTarget.fY); buffer.writeInt((int) fTileMode); + buffer.writeBool(fConvolveAlpha); } SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() { @@ -97,7 +101,7 @@ public: } }; -template +template void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { for (int y = rect.fTop; y < rect.fBottom; ++y) { SkPMColor* dptr = result->getAddr32(rect.fLeft, y); @@ -107,21 +111,38 @@ void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* for (int cx = 0; cx < fKernelSize.fWidth; cx++) { SkPMColor s = PixelFetcher::fetch(src, x + cx - fTarget.fX, y + cy - fTarget.fY); SkScalar k = fKernel[cy * fKernelSize.fWidth + cx]; - sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k); + if (convolveAlpha) { + sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k); + } sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k); sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k); sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k); } } - int a = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255); + int a = convolveAlpha + ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255) + : SkGetPackedA32(PixelFetcher::fetch(src, x, y)); int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a); int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a); int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a); - *dptr++ = SkPackARGB32(a, r, g, b); + if (!convolveAlpha) { + *dptr++ = SkPreMultiplyARGB(a, r, g, b); + } else { + *dptr++ = SkPackARGB32(a, r, g, b); + } } } } +template +void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { + if (fConvolveAlpha) { + filterPixels(src, result, rect); + } else { + filterPixels(src, result, rect); + } +} + void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { filterPixels(src, result, rect); } @@ -140,6 +161,31 @@ void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, SkB } } +// FIXME: This should be refactored to SkSingleInputImageFilter for +// use by other filters. For now, we assume the input is always +// premultiplied and unpremultiply it +static SkBitmap unpremultiplyBitmap(const SkBitmap& src) +{ + SkAutoLockPixels alp(src); + if (!src.getPixels()) { + return SkBitmap(); + } + SkBitmap result; + result.setConfig(src.config(), src.width(), src.height()); + result.allocPixels(); + if (!result.getPixels()) { + return SkBitmap(); + } + for (int y = 0; y < src.height(); ++y) { + const uint32_t* srcRow = src.getAddr32(0, y); + uint32_t* dstRow = result.getAddr32(0, y); + for (int x = 0; x < src.width(); ++x) { + dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]); + } + } + return result; +} + bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const SkMatrix& matrix, @@ -150,6 +196,10 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, return false; } + if (!fConvolveAlpha && !src.isOpaque()) { + src = unpremultiplyBitmap(src); + } + SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; -- cgit v1.2.3