diff options
author | senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-12-20 20:02:19 +0000 |
---|---|---|
committer | senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-12-20 20:02:19 +0000 |
commit | ae814c74763e3f7c13aeb03f6f8b894bd11ff35e (patch) | |
tree | cd8339409655599e1a532442c838e837940bd593 /src | |
parent | 6c31d9d9b4ea183ec7be29f97e108c8cfb33533b (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.cpp | 150 |
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) |