aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar humper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-02-20 16:42:06 +0000
committerGravatar humper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-02-20 16:42:06 +0000
commita99a92cebaa46cf792cf86eaad1a4c3f9d6162f7 (patch)
treece0bff334549aab60c090e4d11f106172aa737ef
parentf33612b923c3d065aabda6967247771d9aa6172c (diff)
Complete the implementation of the faster blur; now supports all blur styles and matches the boxfilter approximation visually. Also change the interpretation of the blur radius to be sigma/2; need to add SK_IGNORE_BLUR_RADIUS_CORRECTNESS to chromium GYP to avoid immediate layout test failures over there.
Review URL: https://codereview.appspot.com/7307076 git-svn-id: http://skia.googlecode.com/svn/trunk@7793 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--bench/BlurRectBench.cpp115
-rw-r--r--gm/blurrect.cpp243
-rw-r--r--src/effects/SkBlurMask.cpp485
-rw-r--r--src/effects/SkBlurMask.h12
4 files changed, 660 insertions, 195 deletions
diff --git a/bench/BlurRectBench.cpp b/bench/BlurRectBench.cpp
index a3825be678..cbb624c112 100644
--- a/bench/BlurRectBench.cpp
+++ b/bench/BlurRectBench.cpp
@@ -74,13 +74,13 @@ private:
class BlurRectDirectBench: public BlurRectBench {
public:
- BlurRectDirectBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
+ BlurRectDirectBench(void *param, SkScalar rad) : INHERITED(param, rad) {
SkString name;
if (SkScalarFraction(rad) != 0) {
name.printf("blurrect_direct_%.2f", SkScalarToFloat(rad));
} else {
- name.printf("blurrect_direct_%d", SkScalarRound(rad));
+ name.printf("blurrect_direct_%d", SkScalarRoundToInt(rad));
}
setName(name);
@@ -88,23 +88,17 @@ class BlurRectDirectBench: public BlurRectBench {
protected:
virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
SkMask mask;
- SkBlurMask::BlurRect(&mask, r, radius(), SkBlurMask::kNormal_Style,
- SkBlurMask::kHigh_Quality);
+ SkBlurMask::BlurRect(&mask, r, this->radius(), SkBlurMask::kNormal_Style);
SkMask::FreeImage(mask.fImage);
}
+private:
+ typedef BlurRectBench INHERITED;
};
class BlurRectSeparableBench: public BlurRectBench {
- SkMask fSrcMask;
+
public:
- BlurRectSeparableBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
- SkString name;
- if (SkScalarFraction(rad) != 0) {
- name.printf("blurrect_separable_%.2f", SkScalarToFloat(rad));
- } else {
- name.printf("blurrect_separable_%d", SkScalarRound(rad));
- }
- setName(name);
+ BlurRectSeparableBench(void *param, SkScalar rad) : INHERITED(param, rad) {
fSrcMask.fImage = NULL;
}
@@ -123,21 +117,106 @@ protected:
memset(fSrcMask.fImage, 0xff, fSrcMask.computeTotalImageSize());
}
+
+ SkMask fSrcMask;
+private:
+ typedef BlurRectBench INHERITED;
+};
+
+class BlurRectBoxFilterBench: public BlurRectSeparableBench {
+public:
+ BlurRectBoxFilterBench(void *param, SkScalar rad) : INHERITED(param, rad) {
+ SkString name;
+ if (SkScalarFraction(rad) != 0) {
+ name.printf("blurrect_boxfilter_%.2f", SkScalarToFloat(rad));
+ } else {
+ name.printf("blurrect_boxfilter_%d", SkScalarRoundToInt(rad));
+ }
+ setName(name);
+ }
+
+protected:
virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
SkMask mask;
- SkBlurMask::BlurSeparable(&mask, fSrcMask, radius(),
+ mask.fImage = NULL;
+ SkBlurMask::BlurSeparable(&mask, fSrcMask, this->radius(),
SkBlurMask::kNormal_Style,
SkBlurMask::kHigh_Quality);
SkMask::FreeImage(mask.fImage);
}
+private:
+ typedef BlurRectSeparableBench INHERITED;
+};
+
+class BlurRectGaussianBench: public BlurRectSeparableBench {
+public:
+ BlurRectGaussianBench(void *param, SkScalar rad) : INHERITED(param, rad) {
+ SkString name;
+ if (SkScalarFraction(rad) != 0) {
+ name.printf("blurrect_gaussian_%.2f", SkScalarToFloat(rad));
+ } else {
+ name.printf("blurrect_gaussian_%d", SkScalarRoundToInt(rad));
+ }
+ setName(name);
+ }
+
+protected:
+
+ virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
+ SkMask mask;
+ mask.fImage = NULL;
+ SkBlurMask::BlurGroundTruth(&mask, fSrcMask, this->radius(),
+ SkBlurMask::kNormal_Style);
+ SkMask::FreeImage(mask.fImage);
+ }
+private:
+ typedef BlurRectSeparableBench INHERITED;
};
-DEF_BENCH(return new BlurRectSeparableBench(p, SMALL);)
-DEF_BENCH(return new BlurRectSeparableBench(p, BIG);)
-DEF_BENCH(return new BlurRectSeparableBench(p, REALBIG);)
-DEF_BENCH(return new BlurRectSeparableBench(p, REAL);)
+DEF_BENCH(return new BlurRectBoxFilterBench(p, SMALL);)
+DEF_BENCH(return new BlurRectBoxFilterBench(p, BIG);)
+DEF_BENCH(return new BlurRectBoxFilterBench(p, REALBIG);)
+DEF_BENCH(return new BlurRectBoxFilterBench(p, REAL);)
+DEF_BENCH(return new BlurRectGaussianBench(p, SMALL);)
+DEF_BENCH(return new BlurRectGaussianBench(p, BIG);)
+DEF_BENCH(return new BlurRectGaussianBench(p, REALBIG);)
+DEF_BENCH(return new BlurRectGaussianBench(p, REAL);)
DEF_BENCH(return new BlurRectDirectBench(p, SMALL);)
DEF_BENCH(return new BlurRectDirectBench(p, BIG);)
DEF_BENCH(return new BlurRectDirectBench(p, REALBIG);)
DEF_BENCH(return new BlurRectDirectBench(p, REAL);)
+
+DEF_BENCH(return new BlurRectDirectBench(p, SkIntToScalar(5));)
+DEF_BENCH(return new BlurRectDirectBench(p, SkIntToScalar(20));)
+
+DEF_BENCH(return new BlurRectBoxFilterBench(p, SkIntToScalar(5));)
+DEF_BENCH(return new BlurRectBoxFilterBench(p, SkIntToScalar(20));)
+
+#if 0
+// disable Gaussian benchmarks; the algorithm works well enough
+// and serves as a baseline for ground truth, but it's too slow
+// to use in production for non-trivial radii, so no real point
+// in having the bots benchmark it all the time.
+
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(1));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(2));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(3));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(4));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(5));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(6));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(7));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(8));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(9));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(10));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(11));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(12));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(13));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(14));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(15));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(16));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(17));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(18));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(19));)
+DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(20));)
+#endif
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
index a5e8cf0a8e..52f177db8a 100644
--- a/gm/blurrect.cpp
+++ b/gm/blurrect.cpp
@@ -1,9 +1,9 @@
/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
+* Copyright 2012 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
#include "gm.h"
#include "SkBlurMaskFilter.h"
@@ -65,19 +65,18 @@ static const char* gBlurStyle2Name[] = {
};
class BlurRectGM : public skiagm::GM {
- SkAutoTUnref<SkMaskFilter> fMaskFilter;
- SkString fName;
- PaintProc fPProc;
- SkAlpha fAlpha;
+ SkAutoTUnref<SkMaskFilter> fMaskFilter;
+ SkString fName;
+ PaintProc fPProc;
+ SkAlpha fAlpha;
public:
BlurRectGM(const char name[], PaintProc pproc, U8CPU alpha,
SkBlurMaskFilter::BlurStyle bs) :
fMaskFilter(SkBlurMaskFilter::Create(STROKE_WIDTH/2, bs,
- SkBlurMaskFilter::kHighQuality_BlurFlag))
- , fName(name)
- , fPProc(pproc)
- , fAlpha(SkToU8(alpha))
- {
+ SkBlurMaskFilter::kHighQuality_BlurFlag))
+ , fName(name)
+ , fPProc(pproc)
+ , fAlpha(SkToU8(alpha)) {
fName.appendf("_%s", gBlurStyle2Name[bs]);
}
@@ -131,7 +130,7 @@ private:
canvas->translate(0, r.height() * 4/3);
}
}
-
+private:
typedef GM INHERITED;
};
@@ -139,17 +138,28 @@ class BlurRectCompareGM : public skiagm::GM {
SkString fName;
unsigned int fRectWidth, fRectHeight;
SkScalar fRadius;
+ SkBlurMask::Style fStyle;
public:
- BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius)
+ BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius, SkBlurMask::Style style)
: fName(name)
, fRectWidth(rectWidth)
, fRectHeight(rectHeight)
, fRadius(radius)
- {}
+ , fStyle(style)
+ {}
- int width() const { return fRectWidth; }
- int height() const { return fRectHeight; }
- SkScalar radius() const { return fRadius; }
+ int width() const {
+ return fRectWidth;
+ }
+ int height() const {
+ return fRectHeight;
+ }
+ SkScalar radius() const {
+ return fRadius;
+ }
+ SkBlurMask::Style style() const {
+ return fStyle;
+ }
protected:
virtual SkString onShortName() {
@@ -160,21 +170,34 @@ protected:
return SkISize::Make(640, 480);
}
- virtual void makeMask(SkMask *m, const SkRect&) = 0;
+ virtual bool makeMask(SkMask *m, const SkRect&) = 0;
virtual void onDraw(SkCanvas* canvas) {
- SkRect r;
- r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
+ SkRect r;
+ r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
+
+ SkISize canvas_size = canvas->getDeviceSize();
+ int center_x = (canvas_size.fWidth - r.width())/2;
+ int center_y = (canvas_size.fHeight - r.height())/2;
+
+ SkMask mask;
+
+ if (!this->makeMask(&mask, r)) {
+ SkPaint paint;
+ r.offset( center_x, center_y );
+ canvas->drawRect(r,paint);
+ return;
+ }
+ SkAutoMaskFreeImage amfi(mask.fImage);
- SkMask mask;
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
+ bm.setPixels(mask.fImage);
- this->makeMask(&mask, r);
- SkAutoMaskFreeImage amfi(mask.fImage);
+ center_x = (canvas_size.fWidth - mask.fBounds.width())/2;
+ center_y = (canvas_size.fHeight - mask.fBounds.height())/2;
- SkBitmap bm;
- bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
- bm.setPixels(mask.fImage);
- canvas->drawBitmap(bm, 50, 50, NULL);
+ canvas->drawBitmap(bm, center_x, center_y, NULL);
}
virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
@@ -186,33 +209,94 @@ private:
class BlurRectFastGM: public BlurRectCompareGM {
public:
BlurRectFastGM(const char name[], unsigned int rect_width,
- unsigned int rect_height, float blur_radius) :
- BlurRectCompareGM(name, rect_width, rect_height, blur_radius) {}
+ unsigned int rect_height, float blur_radius,
+ SkBlurMask::Style style) :
+ INHERITED(name, rect_width, rect_height, blur_radius, style)
+ {
+
+ }
protected:
- virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
- SkBlurMask::BlurRect(m, r, radius(), SkBlurMask::kNormal_Style,
- SkBlurMask::kHigh_Quality );
+ virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+ return SkBlurMask::BlurRect(m, r, this->radius(), this->style());
}
+private:
+ typedef BlurRectCompareGM INHERITED;
};
class BlurRectSlowGM: public BlurRectCompareGM {
public:
- BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height, float blur_radius) :
- BlurRectCompareGM( name, rect_width, rect_height, blur_radius ) {}
+ BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height,
+ float blur_radius, SkBlurMask::Style style) :
+ INHERITED(name, rect_width, rect_height, blur_radius, style)
+ {
+
+ }
protected:
- virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+ virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
SkMask src;
r.roundOut(&src.fBounds);
src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin
src.fFormat = SkMask::kA8_Format;
src.fRowBytes = src.fBounds.width();
- src.fImage = SkMask::AllocImage( src.computeTotalImageSize() );
+ src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
SkAutoMaskFreeImage amfi(src.fImage);
memset(src.fImage, 0xff, src.computeTotalImageSize());
- SkBlurMask::BlurSeparable(m, src, radius()/2, SkBlurMask::kNormal_Style, SkBlurMask::kHigh_Quality);
+ return SkBlurMask::BlurSeparable(m, src, this->radius(), this->style(), this->getQuality());
}
+
+ virtual SkBlurMask::Quality getQuality() {
+ return SkBlurMask::kHigh_Quality;
+ }
+private:
+ typedef BlurRectCompareGM INHERITED;
+};
+
+class BlurRectSlowLowGM: public BlurRectSlowGM {
+public:
+ BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
+ float blurRadius, SkBlurMask::Style style) :
+ INHERITED(name, rectWidth, rectHeight, blurRadius, style)
+ {
+
+ }
+protected:
+ virtual SkBlurMask::Quality getQuality() SK_OVERRIDE {
+ return SkBlurMask::kLow_Quality;
+ }
+private:
+ typedef BlurRectSlowGM INHERITED;
+};
+
+class BlurRectGroundTruthGM: public BlurRectCompareGM {
+public:
+ BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
+ float blurRadius, SkBlurMask::Style style) :
+ INHERITED(name, rectWidth, rectHeight, blurRadius, style)
+ {
+
+ }
+protected:
+ virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+ SkMask src;
+ r.roundOut(&src.fBounds);
+ src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin
+ src.fFormat = SkMask::kA8_Format;
+ src.fRowBytes = src.fBounds.width();
+ src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
+ SkAutoMaskFreeImage amfi(src.fImage);
+
+ memset(src.fImage, 0xff, src.computeTotalImageSize());
+
+ return SkBlurMask::BlurGroundTruth(m, src, this->radius(), this->style());
+ }
+
+ virtual SkBlurMask::Quality getQuality() {
+ return SkBlurMask::kHigh_Quality;
+ }
+private:
+ typedef BlurRectCompareGM INHERITED;
};
@@ -223,12 +307,73 @@ DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kSolid_Bl
DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kOuter_BlurStyle);)
DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kInner_BlurStyle);)
-DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_10", 100, 100, 10);)
-DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_2", 100, 100, 2);)
-DEF_GM(return new BlurRectFastGM("blurrect_fast_10_10_100", 10, 10, 100);)
-DEF_GM(return new BlurRectFastGM("blurrect_fast_10_100_10", 10, 100, 10);)
-
-DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_10", 100, 100, 10);)
-DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_2", 100, 100, 2);)
-DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_10_100", 10, 10, 100);)
-DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_100_10", 10, 100, 10);)
+// regular size rects, blurs should be small enough not to completely overlap.
+
+DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_normal_fast", 25, 100, 2, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_normal_fast", 25, 100, 20, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_normal_slow", 25, 100, 2, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_normal_slow", 25, 100, 20, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_inner_fast", 25, 100, 2, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_inner_fast", 25, 100, 20, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_inner_slow", 25, 100, 2, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_inner_slow", 25, 100, 20, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_outer_fast", 25, 100, 2, SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_outer_fast", 25, 100, 20, SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_outer_slow", 25, 100, 2, SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_outer_slow", 25, 100, 20, SkBlurMask::kOuter_Style);)
+
+// skinny tall rects, blurs overlap in X but not y
+
+DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_normal_fast", 5, 100, 2 , SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_normal_fast", 5, 100, 20, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_normal_slow", 5, 100, 2 , SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_normal_slow", 5, 100, 20, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_inner_fast", 5, 100, 2 , SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_inner_fast", 5, 100, 20, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_inner_slow", 5, 100, 2 , SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_inner_slow", 5, 100, 20, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_outer_fast", 5, 100, 2 , SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_outer_fast", 5, 100, 20, SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_outer_slow", 5, 100, 2 , SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_outer_slow", 5, 100, 20, SkBlurMask::kOuter_Style);)
+
+// tiny rects, blurs overlap in X and Y
+
+DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_normal_fast", 5, 5, 2 , SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_normal_fast", 5, 5, 20, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_normal_slow", 5, 5, 2 , SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_normal_slow", 5, 5, 20, SkBlurMask::kNormal_Style);)
+DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_inner_fast", 5, 5, 2 , SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_inner_fast", 5, 5, 20, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_inner_slow", 5, 5, 2 , SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_inner_slow", 5, 5, 20, SkBlurMask::kInner_Style);)
+DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_outer_fast", 5, 5, 2 , SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_outer_fast", 5, 5, 20, SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_outer_slow", 5, 5, 2 , SkBlurMask::kOuter_Style);)
+DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_outer_slow", 5, 5, 20, SkBlurMask::kOuter_Style);)
+
+
+#if 0
+// dont' need to GM the gaussian convolution; it's slow and intended
+// as a ground truth comparison only. Leaving these here in case we
+// ever want to turn these back on for debugging reasons.
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_1_simple", 25, 100, 1);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_2_simple", 25, 100, 2);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_3_simple", 25, 100, 3);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_4_simple", 25, 100, 4);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_5_simple", 25, 100, 5);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_6_simple", 25, 100, 6);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_7_simple", 25, 100, 7);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_8_simple", 25, 100, 8);)
+DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_9_simple", 25, 100, 9);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_10_simple", 25, 100, 10);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_11_simple", 25, 100, 11);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_12_simple", 25, 100, 12);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_13_simple", 25, 100, 13);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_14_simple", 25, 100, 14);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_15_simple", 25, 100, 15);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_16_simple", 25, 100, 16);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_17_simple", 25, 100, 17);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_18_simple", 25, 100, 18);)
+DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_19_simple", 25, 100, 19);)
+#endif
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index 70efa0bcba..e2dfd2b112 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -210,7 +210,7 @@ static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst,
RIGHT_BORDER_ITER
}
#undef RIGHT_BORDER_ITER
- for (int x = 0; x < leftRadius - rightRadius; x++) {
+ for (int x = 0; x < leftRadius - rightRadius; ++x) {
*dptr = 0;
dptr += dst_x_stride;
}
@@ -326,7 +326,7 @@ static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
}
#endif
- for (;x < border; x++) {
+ for (;x < border; ++x) {
LEFT_BORDER_ITER
}
#undef LEFT_BORDER_ITER
@@ -395,7 +395,7 @@ static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
RIGHT_BORDER_ITER
}
#endif
- for (; x < border; x++) {
+ for (; x < border; ++x) {
RIGHT_BORDER_ITER
}
#undef RIGHT_BORDER_ITER
@@ -519,17 +519,19 @@ static void kernel_clamped(uint8_t dst[], int rx, int ry, const uint32_t sum[],
int prev_y = -2*ry;
int next_y = 1;
- for (int y = 0; y < dh; y++) {
+ for (int y = 0; y < dh; ++y) {
int py = SkClampPos(prev_y) * sumStride;
int ny = SkFastMin32(next_y, sh) * sumStride;
int prev_x = -2*rx;
int next_x = 1;
- for (int x = 0; x < dw; x++) {
+ for (int x = 0; x < dw; ++x) {
int px = SkClampPos(prev_x);
int nx = SkFastMin32(next_x, sw);
+ // TODO: should we be adding 1/2 (1 << 23) to round to the
+ // nearest integer here?
uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
*dst++ = SkToU8(tmp * scale >> 24);
@@ -549,7 +551,7 @@ static void kernel_clamped(uint8_t dst[], int rx, int ry, const uint32_t sum[],
*
* The inner loop is conceptually simple; we break it into several sections
* to improve performance. Here's the original version:
- for (int x = 0; x < dw; x++) {
+ for (int x = 0; x < dw; ++x) {
int px = SkClampPos(prev_x);
int nx = SkFastMin32(next_x, sw);
@@ -585,7 +587,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
SkASSERT(2*rx <= dw - 2*rx);
- for (int y = 0; y < dh; y++) {
+ for (int y = 0; y < dh; ++y) {
int py = SkClampPos(prev_y) * sumStride;
int ny = SkFastMin32(next_y, sh) * sumStride;
@@ -593,7 +595,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
int next_x = 1;
int x = 0;
- for (; x < 2*rx; x++) {
+ for (; x < 2*rx; ++x) {
SkASSERT(prev_x <= 0);
SkASSERT(next_x <= sw);
@@ -631,7 +633,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
}
#endif
- for (; x < dw - 2*rx; x++) {
+ for (; x < dw - 2*rx; ++x) {
SkASSERT(prev_x >= 0);
SkASSERT(next_x <= sw);
@@ -642,7 +644,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
next_x += 1;
}
- for (; x < dw; x++) {
+ for (; x < dw; ++x) {
SkASSERT(prev_x >= 0);
SkASSERT(next_x > sw);
@@ -666,17 +668,17 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
* is wider than the source image.
*/
static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
- const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
+ const uint32_t sum[], int sw, int sh, U8CPU outerWeight) {
SkASSERT(2*rx > sw);
- int inner_weight = 255 - outer_weight;
+ int innerWeight = 255 - outerWeight;
// round these guys up if they're bigger than 127
- outer_weight += outer_weight >> 7;
- inner_weight += inner_weight >> 7;
+ outerWeight += outerWeight >> 7;
+ innerWeight += innerWeight >> 7;
- uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
- uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
+ uint32_t outerScale = (outerWeight << 16) / ((2*rx + 1)*(2*ry + 1));
+ uint32_t innerScale = (innerWeight << 16) / ((2*rx - 1)*(2*ry - 1));
int sumStride = sw + 1;
@@ -686,7 +688,7 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
int prev_y = -2*ry;
int next_y = 1;
- for (int y = 0; y < dh; y++) {
+ for (int y = 0; y < dh; ++y) {
int py = SkClampPos(prev_y) * sumStride;
int ny = SkFastMin32(next_y, sh) * sumStride;
@@ -696,19 +698,19 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
int prev_x = -2*rx;
int next_x = 1;
- for (int x = 0; x < dw; x++) {
+ for (int x = 0; x < dw; ++x) {
int px = SkClampPos(prev_x);
int nx = SkFastMin32(next_x, sw);
int ipx = SkClampPos(prev_x + 1);
int inx = SkClampMax(next_x - 1, sw);
- uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+ uint32_t outerSum = sum[px+py] + sum[nx+ny]
- sum[nx+py] - sum[px+ny];
- uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+ uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
- sum[inx+ipy] - sum[ipx+iny];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
prev_x += 1;
next_x += 1;
@@ -726,19 +728,19 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
*
* The inner loop is conceptually simple; we break it into several variants
* to improve performance. Here's the original version:
- for (int x = 0; x < dw; x++) {
+ for (int x = 0; x < dw; ++x) {
int px = SkClampPos(prev_x);
int nx = SkFastMin32(next_x, sw);
int ipx = SkClampPos(prev_x + 1);
int inx = SkClampMax(next_x - 1, sw);
- uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+ uint32_t outerSum = sum[px+py] + sum[nx+ny]
- sum[nx+py] - sum[px+ny];
- uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+ uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
- sum[inx+ipy] - sum[ipx+iny];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
prev_x += 1;
next_x += 1;
@@ -751,23 +753,23 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
* speedup.
*/
static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
- const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
+ const uint32_t sum[], int sw, int sh, U8CPU outerWeight) {
SkASSERT(rx > 0 && ry > 0);
- SkASSERT(outer_weight <= 255);
+ SkASSERT(outerWeight <= 255);
if (2*rx > sw) {
- kernel_interp_clamped(dst, rx, ry, sum, sw, sh, outer_weight);
+ kernel_interp_clamped(dst, rx, ry, sum, sw, sh, outerWeight);
return;
}
- int inner_weight = 255 - outer_weight;
+ int innerWeight = 255 - outerWeight;
// round these guys up if they're bigger than 127
- outer_weight += outer_weight >> 7;
- inner_weight += inner_weight >> 7;
+ outerWeight += outerWeight >> 7;
+ innerWeight += innerWeight >> 7;
- uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
- uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
+ uint32_t outerScale = (outerWeight << 16) / ((2*rx + 1)*(2*ry + 1));
+ uint32_t innerScale = (innerWeight << 16) / ((2*rx - 1)*(2*ry - 1));
int sumStride = sw + 1;
@@ -779,7 +781,7 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
SkASSERT(2*rx <= dw - 2*rx);
- for (int y = 0; y < dh; y++) {
+ for (int y = 0; y < dh; ++y) {
int py = SkClampPos(prev_y) * sumStride;
int ny = SkFastMin32(next_y, sh) * sumStride;
@@ -790,7 +792,7 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
int next_x = 1;
int x = 0;
- for (; x < 2*rx; x++) {
+ for (; x < 2*rx; ++x) {
SkASSERT(prev_x < 0);
SkASSERT(next_x <= sw);
@@ -800,12 +802,12 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
int ipx = 0;
int inx = next_x - 1;
- uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+ uint32_t outerSum = sum[px+py] + sum[nx+ny]
- sum[nx+py] - sum[px+ny];
- uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+ uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
- sum[inx+ipy] - sum[ipx+iny];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
prev_x += 1;
next_x += 1;
@@ -825,42 +827,42 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
SkASSERT(prev_x >= 0);
SkASSERT(next_x <= sw);
- uint32_t outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
- uint32_t inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
- outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
- inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
- outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
- inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
- outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
- inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
+ uint32_t outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+ uint32_t innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
+ outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+ innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
+ outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+ innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
+ outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+ innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
prev_x += 4;
next_x += 4;
}
#endif
- for (; x < dw - 2*rx; x++) {
+ for (; x < dw - 2*rx; ++x) {
SkASSERT(prev_x >= 0);
SkASSERT(next_x <= sw);
- uint32_t outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
- uint32_t inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
+ uint32_t outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+ uint32_t innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
prev_x += 1;
next_x += 1;
}
- for (; x < dw; x++) {
+ for (; x < dw; ++x) {
SkASSERT(prev_x >= 0);
SkASSERT(next_x > sw);
@@ -870,12 +872,12 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
int ipx = prev_x + 1;
int inx = sw;
- uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+ uint32_t outerSum = sum[px+py] + sum[nx+ny]
- sum[nx+py] - sum[px+ny];
- uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+ uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
- sum[inx+ipy] - sum[ipx+iny];
- *dst++ = SkToU8((outer_sum * outer_scale
- + inner_sum * inner_scale) >> 24);
+ *dst++ = SkToU8((outerSum * outerScale
+ + innerSum * innerScale) >> 24);
prev_x += 1;
next_x += 1;
@@ -955,6 +957,7 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
SkScalar radius, Style style, Quality quality,
SkIPoint* margin, bool separable)
{
+
if (src.fFormat != SkMask::kA8_Format) {
return false;
}
@@ -963,16 +966,27 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
if (radius < SkIntToScalar(3)) {
quality = kLow_Quality;
}
-
- // highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur
+
+ // highQuality: use three box blur passes as a cheap way
+ // to approximate a Gaussian blur
int passCount = (kHigh_Quality == quality) ? 3 : 1;
- SkScalar passRadius = (kHigh_Quality == quality) ? SkScalarMul( radius, kBlurRadiusFudgeFactor): radius;
+ SkScalar passRadius = (kHigh_Quality == quality) ?
+ SkScalarMul( radius, kBlurRadiusFudgeFactor):
+ radius;
+
+#ifndef SK_IGNORE_BLUR_RADIUS_CORRECTNESS
+ // multiply the given radius by sqrt(2)/2 to convert
+ // from (2x) standard deviation to needed box width
+ const SkScalar radiusMultiplier = SkFloatToScalar(0.707f);
+ SkScalar boxWidth = SkScalarMul(passRadius, radiusMultiplier);
+ passRadius = SkScalarMul(boxWidth,SK_ScalarHalf) - SK_ScalarHalf;
+#endif
int rx = SkScalarCeil(passRadius);
- int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
+ int outerWeight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
SkASSERT(rx >= 0);
- SkASSERT((unsigned)outer_weight <= 255);
+ SkASSERT((unsigned)outerWeight <= 255);
if (rx <= 0) {
return false;
}
@@ -981,11 +995,13 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
int padx = passCount * rx;
int pady = passCount * ry;
+
if (margin) {
margin->set(padx, pady);
}
dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
+
dst->fRowBytes = dst->fBounds.width();
dst->fFormat = SkMask::kA8_Format;
dst->fImage = NULL;
@@ -1000,7 +1016,6 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
int sh = src.fBounds.height();
const uint8_t* sp = src.fImage;
uint8_t* dp = SkMask::AllocImage(dstSize);
-
SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
// build the blurry destination
@@ -1008,8 +1023,8 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
uint8_t* tp = tmpBuffer.get();
int w = sw, h = sh;
-
- if (outer_weight == 255) {
+
+ if (outerWeight == 255) {
int loRadius, hiRadius;
get_adjusted_radii(passRadius, &loRadius, &hiRadius);
if (kHigh_Quality == quality) {
@@ -1028,16 +1043,16 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
} else {
if (kHigh_Quality == quality) {
// Do three X blurs, with a transpose on the final one.
- w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outer_weight);
- w = boxBlurInterp(tp, w, dp, rx, w, h, false, outer_weight);
- w = boxBlurInterp(dp, w, tp, rx, w, h, true, outer_weight);
+ w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outerWeight);
+ w = boxBlurInterp(tp, w, dp, rx, w, h, false, outerWeight);
+ w = boxBlurInterp(dp, w, tp, rx, w, h, true, outerWeight);
// Do three Y blurs, with a transpose on the final one.
- h = boxBlurInterp(tp, h, dp, ry, h, w, false, outer_weight);
- h = boxBlurInterp(dp, h, tp, ry, h, w, false, outer_weight);
- h = boxBlurInterp(tp, h, dp, ry, h, w, true, outer_weight);
+ h = boxBlurInterp(tp, h, dp, ry, h, w, false, outerWeight);
+ h = boxBlurInterp(dp, h, tp, ry, h, w, false, outerWeight);
+ h = boxBlurInterp(tp, h, dp, ry, h, w, true, outerWeight);
} else {
- w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outer_weight);
- h = boxBlurInterp(tp, h, dp, ry, h, w, true, outer_weight);
+ w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outerWeight);
+ h = boxBlurInterp(tp, h, dp, ry, h, w, true, outerWeight);
}
}
} else {
@@ -1048,10 +1063,10 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
//pass1: sp is source, dp is destination
build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
- if (outer_weight == 255) {
+ if (outerWeight == 255) {
apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
} else {
- apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
+ apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outerWeight);
}
if (kHigh_Quality == quality) {
@@ -1060,21 +1075,21 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
int tmp_sh = sh + 2 * ry;
SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, dp, tmp_sw);
- if (outer_weight == 255)
+ if (outerWeight == 255)
apply_kernel(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh);
else
apply_kernel_interp(tmpBuffer.get(), rx, ry, sumBuffer,
- tmp_sw, tmp_sh, outer_weight);
+ tmp_sw, tmp_sh, outerWeight);
//pass3: tmpBuffer is source, dp is destination
tmp_sw += 2 * rx;
tmp_sh += 2 * ry;
build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, tmpBuffer.get(), tmp_sw);
- if (outer_weight == 255)
+ if (outerWeight == 255)
apply_kernel(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh);
else
apply_kernel_interp(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh,
- outer_weight);
+ outerWeight);
}
}
@@ -1126,26 +1141,43 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
quadratic function:
0 x <= -1.5
- 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= 1.5
+ 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5
3/4 - x^2 -.5 < x <= .5
9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5
0 1.5 < x
+
+ Mathematica:
+
+ g[x_] := Piecewise [ {
+ {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5},
+ {3/4 - x^2 , -.5 < x <= .5},
+ {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5}
+ }, 0]
To get the profile curve of the blurred step function at the rectangle
edge, we evaluate the indefinite integral, which is piecewise cubic:
0 x <= -1.5
- 5/8 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
+ 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5
- 3/8 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
+ 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
1 1.5 < x
+
+ in Mathematica code:
+
+ gi[x_] := Piecewise[ {
+ { 0 , x <= -1.5 },
+ { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
+ { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5},
+ { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5}
+ },1]
*/
-static float gaussian_integral( float x ) {
- if ( x > 1.5f ) {
+static float gaussianIntegral(float x) {
+ if (x > 1.5f) {
return 0.0f;
}
- if ( x < -1.5f ) {
+ if (x < -1.5f) {
return 1.0f;
}
@@ -1153,7 +1185,7 @@ static float gaussian_integral( float x ) {
float x3 = x2*x;
if ( x > 0.5f ) {
- return 0.5625f - ( x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
+ return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
}
if ( x > -0.5f ) {
return 0.5f - (0.75f * x - x3 / 3.0f);
@@ -1174,19 +1206,19 @@ static float gaussian_integral( float x ) {
memory returned in profile_out.
*/
-static int compute_profile( SkScalar radius, unsigned int **profile_out ) {
- int size = SkScalarFloorToInt(radius * 3 + 1);
+static int compute_profile(SkScalar radius, unsigned int **profile_out) {
+ int size = SkScalarRoundToInt(radius * 3);
int center = size >> 1;
unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
- float invr = 1.0f/radius;
+ float invr = 1.f/radius;
profile[0] = 255;
- for (int x = 1 ; x < size ; x++) {
- float scaled_x = ( center - x ) * invr;
- float gi = gaussian_integral( scaled_x );
- profile[x] = 255 - (uint8_t) ( 255.f * gi );
+ for (int x = 1 ; x < size ; ++x) {
+ float scaled_x = (center - x - .5) * invr;
+ float gi = gaussianIntegral(scaled_x);
+ profile[x] = 255 - (uint8_t) (255.f * gi);
}
*profile_out = profile;
@@ -1200,29 +1232,49 @@ static int compute_profile( SkScalar radius, unsigned int **profile_out ) {
// Implementation adapted from Michael Herf's approach:
// http://stereopsis.com/shadowrect/
+static inline unsigned int profile_lookup( unsigned int *profile, int loc, int blurred_width, int sharp_width ) {
+ int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
+ int ox = dx >> 1;
+ if (ox < 0) {
+ ox = 0;
+ }
+
+ return profile[ox];
+}
+
bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
- SkScalar provided_radius, Style style, Quality quality,
+ SkScalar provided_radius, Style style,
SkIPoint *margin) {
int profile_size;
unsigned int *profile;
-
float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFudgeFactor ) );
+
+#ifndef SK_IGNORE_BLUR_RADIUS_CORRECTNESS
+ float stddev = SkScalarToFloat( radius ) /2.0f;
+ radius = stddev * 1.414f;
+#endif
profile_size = compute_profile( radius, &profile );
+
SkAutoTDeleteArray<unsigned int> ada(profile);
- int pad = (int) (radius * 1.5f + 1);
+ int pad = profile_size/2;
if (margin) {
margin->set( pad, pad );
}
- dst->fBounds = SkIRect::MakeWH(SkScalarFloorToInt(src.width()), SkScalarFloorToInt(src.height()));
- dst->fBounds.outset(pad, pad);
+
+ int shadow_left = -pad;
+ int shadow_top = -pad;
+ int shadow_right = src.width() + pad;
+ int shadow_bottom = src.height() + pad;
+
+ dst->fBounds.set(shadow_left, shadow_top, shadow_right, shadow_bottom);
dst->fRowBytes = dst->fBounds.width();
dst->fFormat = SkMask::kA8_Format;
dst->fImage = NULL;
-
+
size_t dstSize = dst->computeImageSize();
if (0 == dstSize) {
return false; // too big to allocate, abort
@@ -1235,8 +1287,8 @@ bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
dst->fImage = dp;
- int dst_height = dst->fBounds.height();
- int dst_width = dst->fBounds.width();
+ int dstHeight = dst->fBounds.height();
+ int dstWidth = dst->fBounds.width();
// nearest odd number less than the profile size represents the center
// of the (2x scaled) profile
@@ -1246,28 +1298,209 @@ bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
int h = sh - center;
uint8_t *outptr = dp;
+
+ SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
- for (int y = 0 ; y < dst_height ; y++)
- {
- // time to fill in a scanline of the blurry rectangle.
- // to avoid floating point math, everything is multiplied by
- // 2 where needed. This keeps things nice and integer-oriented.
+ for (int x = 0 ; x < dstWidth ; ++x) {
+ if (profile_size <= sw) {
+ horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w);
+ } else {
+ float span = float(sw)/radius;
+ float giX = 1.5 - (x+.5)/radius;
+ horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
+ }
+ }
+
+ for (int y = 0 ; y < dstHeight ; ++y) {
+ unsigned int profile_y;
+ if (profile_size <= sh) {
+ profile_y = profile_lookup(profile, y, dstHeight, h);
+ } else {
+ float span = float(sh)/radius;
+ float giY = 1.5 - (y+.5)/radius;
+ profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span)));
+ }
- int dy = abs((y << 1) - dst_height) - h; // how far are we from the original edge?
- int oy = dy >> 1;
- if (oy < 0) oy = 0;
+ for (int x = 0 ; x < dstWidth ; x++) {
+ unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profile_y);
+ *(outptr++) = maskval;
+ }
+ }
+
+ if (style == kInner_Style) {
+ // now we allocate the "real" dst, mirror the size of src
+ size_t srcSize = src.width() * src.height();
+ if (0 == srcSize) {
+ return false; // too big to allocate, abort
+ }
+ dst->fImage = SkMask::AllocImage(srcSize);
+ for (int y = 0 ; y < sh ; y++) {
+ uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
+ uint8_t *inner_scanline = dst->fImage + y*sw;
+ memcpy(inner_scanline, blur_scanline, sw);
+ }
+ SkMask::FreeImage(dp);
+
+ dst->fBounds.set(0, 0, sw, sh); // restore trimmed bounds
+ dst->fRowBytes = sw;
+
+ } else if (style == kOuter_Style) {
+ for (int y = pad ; y < dstHeight-pad ; y++) {
+ uint8_t *dst_scanline = dp + y*dstWidth + pad;
+ memset(dst_scanline, 0, sw);
+ }
+ }
+ // normal and solid styles are the same for analytic rect blurs, so don't
+ // need to handle solid specially.
- unsigned int profile_y = profile[oy];
+ return true;
+}
- for (int x = 0 ; x < (dst_width << 1) ; x += 2) {
- int dx = abs( x - dst_width ) - w;
- int ox = dx >> 1;
- if (ox < 0) ox = 0;
+// The "simple" blur is a direct implementation of separable convolution with a discrete
+// gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very
+// useful for correctness comparisons.
- unsigned int maskval = SkMulDiv255Round(profile[ox], profile_y);
+bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provided_radius,
+ Style style, SkIPoint* margin) {
+
+ if (src.fFormat != SkMask::kA8_Format) {
+ return false;
+ }
- *(outptr++) = maskval;
+ float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor));
+ float stddev = SkScalarToFloat(radius) /2.0f;
+ float variance = stddev * stddev;
+
+ int windowSize = SkScalarCeil(stddev*4);
+ // round window size up to nearest odd number
+ windowSize |= 1;
+
+ SkAutoTMalloc<float> gaussWindow(windowSize);
+
+ int halfWindow = windowSize >> 1;
+
+ gaussWindow[halfWindow] = 1;
+
+ float windowSum = 1;
+ for (int x = 1 ; x <= halfWindow ; ++x) {
+ float gaussian = expf(-x*x / variance);
+ gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
+ windowSum += 2*gaussian;
+ }
+
+ // leave the filter un-normalized for now; we will divide by the normalization
+ // sum later;
+
+ int pad = halfWindow;
+ if (margin) {
+ margin->set( pad, pad );
+ }
+
+ dst->fBounds = src.fBounds;
+ dst->fBounds.outset(pad, pad);
+
+ dst->fRowBytes = dst->fBounds.width();
+ dst->fFormat = SkMask::kA8_Format;
+ dst->fImage = NULL;
+
+ if (src.fImage) {
+
+ size_t dstSize = dst->computeImageSize();
+ if (0 == dstSize) {
+ return false; // too big to allocate, abort
+ }
+
+ int srcWidth = src.fBounds.width();
+ int srcHeight = src.fBounds.height();
+ int dstWidth = dst->fBounds.width();
+
+ const uint8_t* srcPixels = src.fImage;
+ uint8_t* dstPixels = SkMask::AllocImage(dstSize);
+ SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dstPixels);
+
+ // do the actual blur. First, make a padded copy of the source.
+ // use double pad so we never have to check if we're outside anything
+
+ int padWidth = srcWidth + 4*pad;
+ int padHeight = srcHeight;
+ int padSize = padWidth * padHeight;
+
+ SkAutoTMalloc<uint8_t> padPixels(padSize);
+ memset(padPixels, 0, padSize);
+
+ for (int y = 0 ; y < srcHeight; ++y) {
+ uint8_t* padptr = padPixels + y * padWidth + 2*pad;
+ const uint8_t* srcptr = srcPixels + y * srcWidth;
+ memcpy(padptr, srcptr, srcWidth);
+ }
+
+ // blur in X, transposing the result into a temporary floating point buffer.
+ // also double-pad the intermediate result so that the second blur doesn't
+ // have to do extra conditionals.
+
+ int tmpWidth = padHeight + 4*pad;
+ int tmpHeight = padWidth - 2*pad;
+ int tmpSize = tmpWidth * tmpHeight;
+
+ SkAutoTMalloc<float> tmpImage(tmpSize);
+ memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
+
+ for (int y = 0 ; y < padHeight ; ++y) {
+ uint8_t *srcScanline = padPixels + y*padWidth;
+ for (int x = pad ; x < padWidth - pad ; ++x) {
+ float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
+ uint8_t *windowCenter = srcScanline + x;
+ for (int i = -pad ; i <= pad ; ++i) {
+ *outPixel += gaussWindow[pad+i]*windowCenter[i];
+ }
+ *outPixel /= windowSum;
+ }
+ }
+
+ // blur in Y; now filling in the actual desired destination. We have to do
+ // the transpose again; these transposes guarantee that we read memory in
+ // linear order.
+
+ for (int y = 0 ; y < tmpHeight ; ++y) {
+ float *srcScanline = tmpImage + y*tmpWidth;
+ for (int x = pad ; x < tmpWidth - pad ; ++x) {
+ float *windowCenter = srcScanline + x;
+ float finalValue = 0;
+ for (int i = -pad ; i <= pad ; ++i) {
+ finalValue += gaussWindow[pad+i]*windowCenter[i];
+ }
+ finalValue /= windowSum;
+ uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
+ int integerPixel = int(finalValue + 0.5f);
+ *outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
+ }
+ }
+
+ dst->fImage = dstPixels;
+ // if need be, alloc the "real" dst (same size as src) and copy/merge
+ // the blur into it (applying the src)
+ if (style == kInner_Style) {
+ // now we allocate the "real" dst, mirror the size of src
+ size_t srcSize = src.computeImageSize();
+ if (0 == srcSize) {
+ return false; // too big to allocate, abort
+ }
+ dst->fImage = SkMask::AllocImage(srcSize);
+ merge_src_with_blur(dst->fImage, src.fRowBytes,
+ srcPixels, src.fRowBytes,
+ dstPixels + pad*dst->fRowBytes + pad,
+ dst->fRowBytes, srcWidth, srcHeight);
+ SkMask::FreeImage(dstPixels);
+ } else if (style != kNormal_Style) {
+ clamp_with_orig(dstPixels + pad*dst->fRowBytes + pad,
+ dst->fRowBytes, srcPixels, src.fRowBytes, srcWidth, srcHeight, style);
}
+ (void)autoCall.detach();
+ }
+
+ if (style == kInner_Style) {
+ dst->fBounds = src.fBounds; // restore trimmed bounds
+ dst->fRowBytes = src.fRowBytes;
}
return true;
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
index 853ba52855..51ec8f858f 100644
--- a/src/effects/SkBlurMask.h
+++ b/src/effects/SkBlurMask.h
@@ -29,15 +29,23 @@ public:
};
static bool BlurRect(SkMask *dst, const SkRect &src,
- SkScalar radius, Style style, Quality quality,
+ SkScalar radius, Style style,
SkIPoint *margin = NULL);
-
static bool Blur(SkMask* dst, const SkMask& src,
SkScalar radius, Style style, Quality quality,
SkIPoint* margin = NULL);
static bool BlurSeparable(SkMask* dst, const SkMask& src,
SkScalar radius, Style style, Quality quality,
SkIPoint* margin = NULL);
+
+
+ // the "ground truth" blur does a gaussian convolution; it's slow
+ // but useful for comparison purposes.
+
+ static bool BlurGroundTruth(SkMask* dst, const SkMask& src,
+ SkScalar provided_radius, Style style,
+ SkIPoint* margin = NULL);
+
private:
static bool Blur(SkMask* dst, const SkMask& src,
SkScalar radius, Style style, Quality quality,