aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-12-20 20:02:19 +0000
committerGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-12-20 20:02:19 +0000
commitae814c74763e3f7c13aeb03f6f8b894bd11ff35e (patch)
treecd8339409655599e1a532442c838e837940bd593 /src
parent6c31d9d9b4ea183ec7be29f97e108c8cfb33533b (diff)
A software implementation of the Gaussian blur filter, using 3 box blurs. Also
re-enable the imageblur GM test, since the SkPicture path now works. Review URL: http://codereview.appspot.com/5413048/ git-svn-id: http://skia.googlecode.com/svn/trunk@2905 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/effects/SkBlurImageFilter.cpp150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index effc734784..5542cb7ca0 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -6,6 +6,7 @@
*/
#include "SkBlurImageFilter.h"
+#include "SkColorPriv.h"
SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
: INHERITED(buffer) {
@@ -29,4 +30,153 @@ void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
buffer.writeScalar(fSigma.fHeight);
}
+static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
+ int leftOffset, int rightOffset)
+{
+ int width = src.width(), height = src.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);
+ for (int i = 0; i < rightBorder; ++i) {
+ sumA += SkGetPackedA32(*p);
+ sumR += SkGetPackedR32(*p);
+ sumG += SkGetPackedG32(*p);
+ sumB += SkGetPackedB32(*p);
+ p++;
+ }
+
+ const SkColor* sptr = src.getAddr32(0, y);
+ SkColor* dptr = dst->getAddr32(0, y);
+ for (int x = 0; x < width; ++x) {
+ *dptr = SkPackARGB32(sumA / kernelSize,
+ sumR / kernelSize,
+ sumG / kernelSize,
+ sumB / kernelSize);
+ if (x >= leftOffset) {
+ SkColor l = *(sptr - leftOffset);
+ sumA -= SkGetPackedA32(l);
+ sumR -= SkGetPackedR32(l);
+ sumG -= SkGetPackedG32(l);
+ sumB -= SkGetPackedB32(l);
+ }
+ if (x + rightOffset + 1 < width) {
+ SkColor r = *(sptr + rightOffset + 1);
+ sumA += SkGetPackedA32(r);
+ sumR += SkGetPackedR32(r);
+ sumG += SkGetPackedG32(r);
+ sumB += SkGetPackedB32(r);
+ }
+ sptr++;
+ dptr++;
+ }
+ }
+}
+
+static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
+ int topOffset, int bottomOffset)
+{
+ int width = src.width(), height = src.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);
+ for (int i = 0; i < bottomBorder; ++i) {
+ sumA += SkGetPackedA32(*p);
+ sumR += SkGetPackedR32(*p);
+ sumG += SkGetPackedG32(*p);
+ sumB += SkGetPackedB32(*p);
+ p += srcStride;
+ }
+
+ const SkColor* sptr = src.getAddr32(x, 0);
+ SkColor* dptr = dst->getAddr32(x, 0);
+ for (int y = 0; y < height; ++y) {
+ *dptr = SkPackARGB32(sumA / kernelSize,
+ sumR / kernelSize,
+ sumG / kernelSize,
+ sumB / kernelSize);
+ if (y >= topOffset) {
+ SkColor l = *(sptr - topOffset * srcStride);
+ sumA -= SkGetPackedA32(l);
+ sumR -= SkGetPackedR32(l);
+ sumG -= SkGetPackedG32(l);
+ sumB -= SkGetPackedB32(l);
+ }
+ if (y + bottomOffset + 1 < height) {
+ SkColor r = *(sptr + (bottomOffset + 1) * srcStride);
+ sumA += SkGetPackedA32(r);
+ sumR += SkGetPackedR32(r);
+ sumG += SkGetPackedG32(r);
+ sumB += SkGetPackedB32(r);
+ }
+ sptr += srcStride;
+ dptr += dstStride;
+ }
+ }
+}
+
+static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, int *highOffset)
+{
+ int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * M_PI) / 4.0f + 0.5f));
+ *kernelSize = d;
+ if (d % 2 == 1) {
+ *lowOffset = *highOffset = (d - 1) / 2;
+ *kernelSize3 = d;
+ } else {
+ *highOffset = d / 2;
+ *lowOffset = *highOffset - 1;
+ *kernelSize3 = d + 1;
+ }
+}
+
+bool SkBlurImageFilter::onFilterImage(const SkBitmap& src, const SkMatrix&,
+ SkBitmap* dst, SkIPoint*) {
+ if (src.config() != SkBitmap::kARGB_8888_Config) {
+ return false;
+ }
+
+ dst->setConfig(src.config(), src.width(), src.height());
+ dst->allocPixels();
+ int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
+ int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
+ getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
+ getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
+
+ if (kernelSizeX < 0 || kernelSizeY < 0) {
+ return false;
+ }
+
+ if (kernelSizeX == 0 && kernelSizeY == 0) {
+ src.copyTo(dst, dst->config());
+ return true;
+ }
+
+ SkBitmap temp;
+ temp.setConfig(dst->config(), dst->width(), dst->height());
+ if (!temp.allocPixels()) {
+ return false;
+ }
+
+ 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);
+ } else if (kernelSizeX > 0) {
+ boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX);
+ boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX);
+ boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX);
+ } else if (kernelSizeY > 0) {
+ boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY);
+ boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY);
+ boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY);
+ }
+ return true;
+}
+
SK_DEFINE_FLATTENABLE_REGISTRAR(SkBlurImageFilter)