aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-25 14:32:42 +0000
committerGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-25 14:32:42 +0000
commit8640d5024d57da5508bdf7585849e3b1f1cb365b (patch)
tree7fbf975b21bfd199135354d0a34b561e7150f029
parent63e0ffde9faf7e5e233128f4bc5459eee23a2a17 (diff)
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
-rw-r--r--bench/MatrixConvolutionBench.cpp12
-rw-r--r--gm/matrixconvolution.cpp14
-rw-r--r--include/effects/SkMatrixConvolutionImageFilter.h8
-rw-r--r--src/effects/SkMatrixConvolutionImageFilter.cpp62
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<SkImageFilter> filter(SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias, target, tileMode)));
+ SkAutoTUnref<SkImageFilter> 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 <class PixelFetcher, bool convolveAlpha>
+ void filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect);
template <class PixelFetcher>
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<class PixelFetcher>
+template<class PixelFetcher, bool convolveAlpha>
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<class PixelFetcher>
+void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
+ if (fConvolveAlpha) {
+ filterPixels<PixelFetcher, true>(src, result, rect);
+ } else {
+ filterPixels<PixelFetcher, false>(src, result, rect);
+ }
+}
+
void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
filterPixels<UncheckedPixelFetcher>(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;