Fix quickReject computation for blurs

https://codereview.chromium.org/17035007/



git-svn-id: http://skia.googlecode.com/svn/trunk@10428 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/blurquickreject.cpp b/gm/blurquickreject.cpp
new file mode 100644
index 0000000..b1406f3
--- /dev/null
+++ b/gm/blurquickreject.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2013 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 "SkCanvas.h"
+#include "SkBlurMaskFilter.h"
+
+// This GM tests out the quick reject bounds of the blur mask filter. It draws
+// four blurred rects around a central clip. The blurred rect geometry outset
+// by the blur radius does not overlap the clip rect so, if the blur clipping
+// just uses the radius, they will be clipped out (and the result will differ
+// from the result if quick reject were disabled. If the blur clipping uses
+// the correct 3 sigma bound then the images with and without quick rejecting
+// will be the same.
+class BlurQuickRejectGM : public skiagm::GM {
+public:
+    BlurQuickRejectGM() {}
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("blurquickreject");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        static const SkScalar kBlurRadius = SkIntToScalar(20);
+        static const SkScalar kBoxSize = SkIntToScalar(100);
+
+        SkRect clipRect = SkRect::MakeXYWH(0, 0, kBoxSize, kBoxSize);
+        SkRect blurRects[] = {
+            { -kBoxSize - (kBlurRadius+1), 0, -(kBlurRadius+1), kBoxSize },
+            { 0, -kBoxSize - (kBlurRadius+1), kBoxSize, -(kBlurRadius+1) },
+            { kBoxSize+kBlurRadius+1, 0, 2*kBoxSize+kBlurRadius+1, kBoxSize },
+            { 0, kBoxSize+kBlurRadius+1, kBoxSize, 2*kBoxSize+kBlurRadius+1 }
+        };
+        SkColor colors[] = {
+            SK_ColorRED,
+            SK_ColorGREEN,
+            SK_ColorBLUE,
+            SK_ColorYELLOW,
+        };
+        SkASSERT(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(blurRects));
+
+        SkPaint hairlinePaint;
+        hairlinePaint.setStyle(SkPaint::kStroke_Style);
+        hairlinePaint.setColor(SK_ColorWHITE);
+        hairlinePaint.setStrokeWidth(0);
+
+        SkPaint blurPaint;
+        blurPaint.setFilterBitmap(true);
+        SkMaskFilter* mf = SkBlurMaskFilter::Create(kBlurRadius,
+                                                    SkBlurMaskFilter::kNormal_BlurStyle);
+        blurPaint.setMaskFilter(mf)->unref();
+
+        canvas->clear(SK_ColorBLACK);
+        canvas->save();
+        canvas->translate(kBoxSize, kBoxSize);
+        canvas->drawRect(clipRect, hairlinePaint);
+        canvas->clipRect(clipRect);
+        for (int i = 0; i < SK_ARRAY_COUNT(blurRects); ++i) {
+            blurPaint.setColor(colors[i]);
+            canvas->drawRect(blurRects[i], blurPaint);
+            canvas->drawRect(blurRects[i], hairlinePaint);
+        }
+        canvas->restore();
+    }
+
+private:
+    static const int kWidth = 300;
+    static const int kHeight = 300;
+
+    typedef GM INHERITED;
+};
+
+DEF_GM( return new BlurQuickRejectGM(); )
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 275568a..3af7d73 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -18,6 +18,7 @@
     '../gm/bleed.cpp',
     '../gm/blend.cpp',
     '../gm/blurs.cpp',
+    '../gm/blurquickreject.cpp',
     '../gm/blurrect.cpp',
     '../gm/circles.cpp',
     '../gm/colorfilterimagefilter.cpp',
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index 0089bab..c946c5e 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -12,13 +12,7 @@
 #include "SkTemplates.h"
 #include "SkEndian.h"
 
-// scale factor for the blur radius to match the behavior of the all existing blur
-// code (both on the CPU and the GPU).  This magic constant is  1/sqrt(3).
-
-// TODO: get rid of this fudge factor and move any required fudging up into
-// the calling library
-
-#define kBlurRadiusFudgeFactor SkFloatToScalar( .57735f )
+const SkScalar SkBlurMask::kBlurRadiusFudgeFactor = SkFloatToScalar(.57735f);
 
 #define UNROLL_SEPARABLE_LOOPS
 
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
index 9c625ad..36d7800 100644
--- a/src/effects/SkBlurMask.h
+++ b/src/effects/SkBlurMask.h
@@ -43,6 +43,13 @@
     static bool BlurGroundTruth(SkMask* dst, const SkMask& src,
                            SkScalar provided_radius, Style style,
                            SkIPoint* margin = NULL);
+
+    // scale factor for the blur radius to match the behavior of the all existing blur
+    // code (both on the CPU and the GPU).  This magic constant is  1/sqrt(3).
+    // TODO: get rid of this fudge factor and move any required fudging up into
+    // the calling library
+    static const SkScalar kBlurRadiusFudgeFactor;
+
 };
 
 #endif
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index f1fee06..bb7aa4e 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -60,7 +60,7 @@
     // To avoid unseemly allocation requests (esp. for finite platforms like
     // handset) we limit the radius so something manageable. (as opposed to
     // a request like 10,000)
-    static const SkScalar kMAX_RADIUS;
+    static const SkScalar kMAX_BLUR_RADIUS;
     // This constant approximates the scaling done in the software path's
     // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
     // IMHO, it actually should be 1:  we blur "less" than we should do
@@ -82,15 +82,15 @@
 
         SkScalar xformedRadius = ignoreTransform ? fRadius
                                                  : ctm.mapRadius(fRadius);
-        return SkMinScalar(xformedRadius, kMAX_RADIUS);
+        return SkMinScalar(xformedRadius, kMAX_BLUR_RADIUS);
     }
 #endif
 
     typedef SkMaskFilter INHERITED;
 };
 
-const SkScalar SkBlurMaskFilterImpl::kMAX_RADIUS = SkIntToScalar(128);
-const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = 0.6f;
+const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_RADIUS = SkIntToScalar(128);
+const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = SkFloatToScalar(0.6f);
 
 SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
                                        SkBlurMaskFilter::BlurStyle style,
@@ -139,7 +139,7 @@
         radius = matrix.mapRadius(fRadius);
     }
 
-    radius = SkMinScalar(radius, kMAX_RADIUS);
+    radius = SkMinScalar(radius, kMAX_BLUR_RADIUS);
     SkBlurMask::Quality blurQuality =
         (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
             SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
@@ -158,7 +158,7 @@
         radius = matrix.mapRadius(fRadius);
     }
 
-    radius = SkMinScalar(radius, kMAX_RADIUS);
+    radius = SkMinScalar(radius, kMAX_BLUR_RADIUS);
 
     return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle,
                                 margin, createMode);
@@ -334,8 +334,27 @@
 
 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
                                              SkRect* dst) const {
-    dst->set(src.fLeft - fRadius, src.fTop - fRadius,
-             src.fRight + fRadius, src.fBottom + fRadius);
+    SkScalar gpuPad, rasterPad;
+
+    {
+        // GPU path
+        SkScalar sigma = SkScalarMul(fRadius, kBLUR_SIGMA_SCALE);
+        gpuPad = sigma * 3.0f;
+    }
+
+    {
+        // raster path
+        SkScalar radius = SkScalarMul(fRadius, SkBlurMask::kBlurRadiusFudgeFactor);
+ 
+        radius = (radius + .5f) * 2.f;
+
+        rasterPad = SkIntToScalar(SkScalarRoundToInt(radius * 3)/2);
+    }
+
+    SkScalar pad = SkMaxScalar(gpuPad, rasterPad);
+
+    dst->set(src.fLeft  - pad, src.fTop    - pad,
+             src.fRight + pad, src.fBottom + pad);
 }
 
 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)