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
This commit is contained in:
senorblanco@chromium.org 2011-12-20 20:02:19 +00:00
parent 6c31d9d9b4
commit ae814c7476
3 changed files with 154 additions and 3 deletions

View File

@ -19,9 +19,7 @@
'../gm/gradients.cpp',
'../gm/gradtext.cpp',
'../gm/hairmodes.cpp',
# commented out until SkPicture support for SkImageFilter is complete
# See http://code.google.com/p/skia/issues/detail?id=391
# '../gm/imageblur.cpp',
'../gm/imageblur.cpp',
'../gm/lcdtext.cpp',
'../gm/linepaths.cpp',
'../gm/movepaths.cpp',

View File

@ -19,6 +19,9 @@ public:
return SkNEW_ARGS(SkBlurImageFilter, (buffer));
}
virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
SkBitmap* result, SkIPoint* offset);
SK_DECLARE_FLATTENABLE_REGISTRAR()
protected:

View File

@ -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)