Complete the implementation of the faster blur; now supports all blur styles and...
authorhumper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 20 Feb 2013 16:42:06 +0000 (16:42 +0000)
committerhumper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 20 Feb 2013 16:42:06 +0000 (16:42 +0000)
Review URL: https://codereview.appspot.com/7307076

git-svn-id: http://skia.googlecode.com/svn/trunk@7793 2bbb7eff-a529-9590-31e7-b0007b416f81

bench/BlurRectBench.cpp
gm/blurrect.cpp
src/effects/SkBlurMask.cpp
src/effects/SkBlurMask.h

index a3825be..cbb624c 100644 (file)
@@ -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
index a5e8cf0..52f177d 100644 (file)
@@ -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
index 70efa0b..e2dfd2b 100644 (file)
@@ -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;
index 853ba52..51ec8f8 100644 (file)
@@ -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,