add asAShadowBlur for android to drawlooper

BUG=skia:
R=djsollen@google.com, scroggo@google.com

Review URL: https://codereview.chromium.org/253633003

git-svn-id: http://skia.googlecode.com/svn/trunk@14431 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
index cc42952..b92bacc 100644
--- a/include/core/SkDrawLooper.h
+++ b/include/core/SkDrawLooper.h
@@ -10,7 +10,10 @@
 #ifndef SkDrawLooper_DEFINED
 #define SkDrawLooper_DEFINED
 
+#include "SkBlurTypes.h"
 #include "SkFlattenable.h"
+#include "SkPoint.h"
+#include "SkColor.h"
 
 class SkCanvas;
 class SkPaint;
@@ -88,6 +91,24 @@
     virtual void computeFastBounds(const SkPaint& paint,
                                    const SkRect& src, SkRect* dst) const;
 
+    struct BlurShadowRec {
+        SkScalar        fSigma;
+        SkVector        fOffset;
+        SkColor         fColor;
+        SkBlurStyle     fStyle;
+        SkBlurQuality   fQuality;
+    };
+    /**
+     *  If this looper can be interpreted as having two layers, such that
+     *      1. The first layer (bottom most) just has a blur and translate
+     *      2. The second layer has no modifications to either paint or canvas
+     *      3. No other layers.
+     *  then return true, and if not null, fill out the BlurShadowRec).
+     *
+     *  If any of the above are not met, return false and ignore the BlurShadowRec parameter.
+     */
+    virtual bool asABlurShadow(BlurShadowRec*) const;
+
     SK_TO_STRING_PUREVIRT()
     SK_DEFINE_FLATTENABLE_TYPE(SkDrawLooper)
 
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
index 3113c93..8051e7d 100644
--- a/include/core/SkMaskFilter.h
+++ b/include/core/SkMaskFilter.h
@@ -10,6 +10,7 @@
 #ifndef SkMaskFilter_DEFINED
 #define SkMaskFilter_DEFINED
 
+#include "SkBlurTypes.h"
 #include "SkFlattenable.h"
 #include "SkMask.h"
 #include "SkPaint.h"
@@ -138,6 +139,18 @@
      */
     virtual void computeFastBounds(const SkRect& src, SkRect* dest) const;
 
+    struct BlurRec {
+        SkScalar        fSigma;
+        SkBlurStyle     fStyle;
+        SkBlurQuality   fQuality;
+    };
+    /**
+     *  If this filter can be represented by a BlurRec, return true and (if not null) fill in the
+     *  provided BlurRec parameter. If this effect cannot be represented as a BlurRec, return false
+     *  and ignore the BlurRec parameter.
+     */
+    virtual bool asABlur(BlurRec*) const;
+
     SK_TO_STRING_PUREVIRT()
     SK_DEFINE_FLATTENABLE_TYPE(SkMaskFilter)
 
diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h
index fe945c3..75ed80e 100644
--- a/include/effects/SkBlurDrawLooper.h
+++ b/include/effects/SkBlurDrawLooper.h
@@ -61,10 +61,12 @@
     SkBlurDrawLooper(SkReadBuffer&);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
+    virtual bool asABlurShadow(BlurShadowRec*) const SK_OVERRIDE;
+
 private:
     SkMaskFilter*   fBlur;
     SkColorFilter*  fColorFilter;
-    SkScalar        fDx, fDy;
+    SkScalar        fDx, fDy, fSigma;
     SkColor         fBlurColor;
     uint32_t        fBlurFlags;
 
@@ -86,6 +88,7 @@
     };
 
     void init(SkScalar sigma, SkScalar dx, SkScalar dy, SkColor color, uint32_t flags);
+    void initEffects();
 
     typedef SkDrawLooper INHERITED;
 };
diff --git a/include/effects/SkEmbossMaskFilter.h b/include/effects/SkEmbossMaskFilter.h
index eb8f811..65dbbe7 100644
--- a/include/effects/SkEmbossMaskFilter.h
+++ b/include/effects/SkEmbossMaskFilter.h
@@ -23,9 +23,7 @@
         uint8_t     fSpecular;      // exponent, 4.4 right now
     };
 
-    static SkEmbossMaskFilter* Create(SkScalar blurSigma, const Light& light) {
-        return SkNEW_ARGS(SkEmbossMaskFilter, (blurSigma, light));
-    }
+    static SkEmbossMaskFilter* Create(SkScalar blurSigma, const Light& light);
 
     // overrides from SkMaskFilter
     //  This method is not exported to java.
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
index fe660a8..ac56e28 100644
--- a/include/effects/SkLayerDrawLooper.h
+++ b/include/effects/SkLayerDrawLooper.h
@@ -77,6 +77,8 @@
 
     virtual size_t contextSize() const SK_OVERRIDE { return sizeof(LayerDrawLooperContext); }
 
+    virtual bool asABlurShadow(BlurShadowRec* rec) const SK_OVERRIDE;
+
     SK_TO_STRING_OVERRIDE()
 
     /// Implements Flattenable.
diff --git a/src/core/SkDrawLooper.cpp b/src/core/SkDrawLooper.cpp
index c620cd0..d18d127 100644
--- a/src/core/SkDrawLooper.cpp
+++ b/src/core/SkDrawLooper.cpp
@@ -59,3 +59,7 @@
         }
     }
 }
+
+bool SkDrawLooper::asABlurShadow(BlurShadowRec*) const {
+    return false;
+}
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 9b023d0..8b9792c 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -26,6 +26,10 @@
     return false;
 }
 
+bool SkMaskFilter::asABlur(BlurRec*) const {
+    return false;
+}
+
 static void extractMaskSubset(const SkMask& src, SkMask* dst) {
     SkASSERT(src.fBounds.contains(dst->fBounds));
 
diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
index 03e635b..5af02db 100644
--- a/src/effects/SkBlurDrawLooper.cpp
+++ b/src/effects/SkBlurDrawLooper.cpp
@@ -1,10 +1,10 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkBlurDrawLooper.h"
 #include "SkBlurMask.h"     // just for SkBlurMask::ConvertRadiusToSigma
 #include "SkBlurMaskFilter.h"
@@ -29,32 +29,27 @@
     this->init(sigma, dx, dy, color, flags);
 }
 
-void SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy,
-                            SkColor color, uint32_t flags) {
-    fDx = dx;
-    fDy = dy;
-    fBlurColor = color;
-    fBlurFlags = flags;
-
-    SkASSERT(flags <= kAll_BlurFlag);
-    if (sigma > 0) {
-        uint32_t blurFlags = flags & kIgnoreTransform_BlurFlag ?
-            SkBlurMaskFilter::kIgnoreTransform_BlurFlag :
-            SkBlurMaskFilter::kNone_BlurFlag;
-
-        blurFlags |= flags & kHighQuality_BlurFlag ?
-            SkBlurMaskFilter::kHighQuality_BlurFlag :
-            SkBlurMaskFilter::kNone_BlurFlag;
-
-        fBlur = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, sigma, blurFlags);
+// only call from constructor
+void SkBlurDrawLooper::initEffects() {
+    SkASSERT(fBlurFlags <= kAll_BlurFlag);
+    if (fSigma > 0) {
+        uint32_t flags = fBlurFlags & kIgnoreTransform_BlurFlag ?
+                            SkBlurMaskFilter::kIgnoreTransform_BlurFlag :
+                            SkBlurMaskFilter::kNone_BlurFlag;
+        
+        flags |= fBlurFlags & kHighQuality_BlurFlag ?
+                    SkBlurMaskFilter::kHighQuality_BlurFlag :
+                    SkBlurMaskFilter::kNone_BlurFlag;
+        
+        fBlur = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, fSigma, flags);
     } else {
         fBlur = NULL;
     }
-
-    if (flags & kOverrideColor_BlurFlag) {
+    
+    if (fBlurFlags & kOverrideColor_BlurFlag) {
         // Set alpha to 1 for the override since transparency will already
         // be baked into the blurred mask.
-        SkColor opaqueColor = SkColorSetA(color, 255);
+        SkColor opaqueColor = SkColorSetA(fBlurColor, 255);
         //The SrcIn xfer mode will multiply 'color' by the incoming alpha
         fColorFilter = SkColorFilter::CreateModeFilter(opaqueColor,
                                                        SkXfermode::kSrcIn_Mode);
@@ -63,15 +58,35 @@
     }
 }
 
-SkBlurDrawLooper::SkBlurDrawLooper(SkReadBuffer& buffer)
-: INHERITED(buffer) {
+void SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy,
+                            SkColor color, uint32_t flags) {
+    fSigma = sigma;
+    fDx = dx;
+    fDy = dy;
+    fBlurColor = color;
+    fBlurFlags = flags;
 
+    this->initEffects();
+}
+
+SkBlurDrawLooper::SkBlurDrawLooper(SkReadBuffer& buffer) : INHERITED(buffer) {
+
+    fSigma = buffer.readScalar();
     fDx = buffer.readScalar();
     fDy = buffer.readScalar();
     fBlurColor = buffer.readColor();
-    fBlur = buffer.readMaskFilter();
-    fColorFilter = buffer.readColorFilter();
     fBlurFlags = buffer.readUInt() & kAll_BlurFlag;
+
+    this->initEffects();
+}
+
+void SkBlurDrawLooper::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fSigma);
+    buffer.writeScalar(fDx);
+    buffer.writeScalar(fDy);
+    buffer.writeColor(fBlurColor);
+    buffer.write32(fBlurFlags);
 }
 
 SkBlurDrawLooper::~SkBlurDrawLooper() {
@@ -79,16 +94,24 @@
     SkSafeUnref(fColorFilter);
 }
 
-void SkBlurDrawLooper::flatten(SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writeScalar(fDx);
-    buffer.writeScalar(fDy);
-    buffer.writeColor(fBlurColor);
-    buffer.writeFlattenable(fBlur);
-    buffer.writeFlattenable(fColorFilter);
-    buffer.writeUInt(fBlurFlags);
+bool SkBlurDrawLooper::asABlurShadow(BlurShadowRec* rec) const {
+    if (fSigma <= 0 || (fBlurFlags & fBlurFlags & kIgnoreTransform_BlurFlag)) {
+        return false;
+    }
+
+    if (rec) {
+        rec->fSigma = fSigma;
+        rec->fColor = fBlurColor;
+        rec->fOffset.set(fDx, fDy);
+        rec->fStyle = kNormal_SkBlurStyle;
+        rec->fQuality = (fBlurFlags & kHighQuality_BlurFlag) ?
+                        kHigh_SkBlurQuality : kLow_SkBlurQuality;
+    }
+    return true;
 }
 
+////////////////////////////////////////////////////////////////////////////////////////
+
 SkDrawLooper::Context* SkBlurDrawLooper::createContext(SkCanvas*, void* storage) const {
     return SkNEW_PLACEMENT_ARGS(storage, BlurDrawLooperContext, (this));
 }
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index c04a6f8..bf50845 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -13,17 +13,21 @@
 #include "SkEndian.h"
 
 
-SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar 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
-    // according to the CSS and canvas specs, simply because Safari does the same.
-    // Firefox used to do the same too, until 4.0 where they fixed it.  So at some
-    // point we should probably get rid of these scaling constants and rebaseline
-    // all the blur tests.
-    static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
+// 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
+// according to the CSS and canvas specs, simply because Safari does the same.
+// Firefox used to do the same too, until 4.0 where they fixed it.  So at some
+// point we should probably get rid of these scaling constants and rebaseline
+// all the blur tests.
+static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
 
-    return radius ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
+SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
+    return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
+}
+
+SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) {
+    return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f;
 }
 
 #define UNROLL_SEPARABLE_LOOPS
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
index 39adb96..71f60d9 100644
--- a/src/effects/SkBlurMask.h
+++ b/src/effects/SkBlurMask.h
@@ -39,7 +39,10 @@
     static bool BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src, SkBlurStyle,
                                 SkIPoint* margin = NULL);
 
+    // If radius > 0, return the corresponding sigma, else return 0
     static SkScalar ConvertRadiusToSigma(SkScalar radius);
+    // If sigma > 0.5, return the corresponding radius, else return 0
+    static SkScalar ConvertSigmaToRadius(SkScalar sigma);
 
     /* Helper functions for analytic rectangle blurs */
 
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 2bed2fe..5dffd6f 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -59,6 +59,7 @@
 #endif
 
     virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
+    virtual bool asABlur(BlurRec*) const SK_OVERRIDE;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
@@ -87,6 +88,11 @@
     SkBlurStyle fBlurStyle;
     uint32_t    fBlurFlags;
 
+    SkBlurQuality getQuality() const {
+        return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
+                kHigh_SkBlurQuality : kLow_SkBlurQuality;
+    }
+
     SkBlurMaskFilterImpl(SkReadBuffer&);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
@@ -145,16 +151,24 @@
     return SkMask::kA8_Format;
 }
 
+bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
+    if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
+        return false;
+    }
+
+    if (rec) {
+        rec->fSigma = fSigma;
+        rec->fStyle = fBlurStyle;
+        rec->fQuality = this->getQuality();
+    }
+    return true;
+}
+
 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
                                       const SkMatrix& matrix,
                                       SkIPoint* margin) const{
     SkScalar sigma = this->computeXformedSigma(matrix);
-
-    SkBlurQuality blurQuality =
-        (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
-            kHigh_SkBlurQuality : kLow_SkBlurQuality;
-
-    return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, blurQuality, margin);
+    return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
 }
 
 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
index 9bf5025..cdd55fc 100644
--- a/src/effects/SkEmbossMaskFilter.cpp
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkEmbossMaskFilter.h"
 #include "SkBlurMaskFilter.h"
 #include "SkBlurMask.h"
@@ -15,6 +13,10 @@
 #include "SkWriteBuffer.h"
 #include "SkString.h"
 
+SkEmbossMaskFilter* SkEmbossMaskFilter::Create(SkScalar blurSigma, const Light& light) {
+    return SkNEW_ARGS(SkEmbossMaskFilter, (blurSigma, light));
+}
+
 static inline int pin2byte(int n) {
     if (n < 0) {
         n = 0;
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index fa590d2..aed2c9b 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -153,6 +153,50 @@
     return true;
 }
 
+bool SkLayerDrawLooper::asABlurShadow(BlurShadowRec* bsRec) const {
+    if (fCount != 2) {
+        return false;
+    }
+    const Rec* rec = fRecs;
+
+    // bottom layer needs to be just blur(maskfilter)
+    if ((rec->fInfo.fPaintBits & ~kMaskFilter_Bit)) {
+        return false;
+    }
+    if (SkXfermode::kSrc_Mode != rec->fInfo.fColorMode) {
+        return false;
+    }
+    const SkMaskFilter* mf = rec->fPaint.getMaskFilter();
+    if (NULL == mf) {
+        return false;
+    }
+    SkMaskFilter::BlurRec maskBlur;
+    if (!mf->asABlur(&maskBlur)) {
+        return false;
+    }
+
+    rec = rec->fNext;
+    // top layer needs to be "plain"
+    if (rec->fInfo.fPaintBits) {
+        return false;
+    }
+    if (SkXfermode::kDst_Mode != rec->fInfo.fColorMode) {
+        return false;
+    }
+    if (!rec->fInfo.fOffset.equals(0, 0)) {
+        return false;
+    }
+
+    if (bsRec) {
+        bsRec->fSigma = maskBlur.fSigma;
+        bsRec->fOffset = fRecs->fInfo.fOffset;
+        bsRec->fColor = fRecs->fPaint.getColor();
+        bsRec->fStyle = maskBlur.fStyle;
+        bsRec->fQuality = maskBlur.fQuality;
+    }
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkLayerDrawLooper::flatten(SkWriteBuffer& buffer) const {
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
index 028b509..c09a4ee 100644
--- a/tests/BlurTest.cpp
+++ b/tests/BlurTest.cpp
@@ -1,12 +1,15 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkBlurMask.h"
 #include "SkBlurMaskFilter.h"
+#include "SkBlurDrawLooper.h"
+#include "SkLayerDrawLooper.h"
+#include "SkEmbossMaskFilter.h"
 #include "SkCanvas.h"
 #include "SkMath.h"
 #include "SkPaint.h"
@@ -380,7 +383,187 @@
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////
+
+static SkBlurQuality blurMaskFilterFlags_as_quality(uint32_t blurMaskFilterFlags) {
+    return (blurMaskFilterFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
+            kHigh_SkBlurQuality : kLow_SkBlurQuality;
+}
+
+static uint32_t blurMaskFilterFlags_to_blurDrawLooperFlags(uint32_t bmf) {
+    const struct {
+        uint32_t fBlurMaskFilterFlag;
+        uint32_t fBlurDrawLooperFlag;
+    } pairs[] = {
+        { SkBlurMaskFilter::kIgnoreTransform_BlurFlag, SkBlurDrawLooper::kIgnoreTransform_BlurFlag },
+        { SkBlurMaskFilter::kHighQuality_BlurFlag,     SkBlurDrawLooper::kHighQuality_BlurFlag },
+    };
+
+    uint32_t bdl = 0;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pairs); ++i) {
+        if (bmf & pairs[i].fBlurMaskFilterFlag) {
+            bdl |= pairs[i].fBlurDrawLooperFlag;
+        }
+    }
+    return bdl;
+}
+
+static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma,
+                                SkBlurStyle style, uint32_t blurMaskFilterFlags) {
+    if (kNormal_SkBlurStyle != style) {
+        return; // blurdrawlooper only supports normal
+    }
+
+    const SkColor color = 0xFF335577;
+    const SkScalar dx = 10;
+    const SkScalar dy = -5;
+    const SkBlurQuality quality = blurMaskFilterFlags_as_quality(blurMaskFilterFlags);
+    uint32_t flags = blurMaskFilterFlags_to_blurDrawLooperFlags(blurMaskFilterFlags);
+
+    SkAutoTUnref<SkDrawLooper> lp(SkBlurDrawLooper::Create(color, sigma, dx, dy, flags));
+
+    const bool expectSuccess = sigma > 0 &&
+                               0 == (flags & SkBlurDrawLooper::kIgnoreTransform_BlurFlag);
+
+    if (NULL == lp.get()) {
+        REPORTER_ASSERT(reporter, sigma <= 0);
+    } else {
+        SkDrawLooper::BlurShadowRec rec;
+        bool success = lp->asABlurShadow(&rec);
+        REPORTER_ASSERT(reporter, success == expectSuccess);
+        if (success) {
+            REPORTER_ASSERT(reporter, rec.fSigma == sigma);
+            REPORTER_ASSERT(reporter, rec.fOffset.x() == dx);
+            REPORTER_ASSERT(reporter, rec.fOffset.y() == dy);
+            REPORTER_ASSERT(reporter, rec.fColor == color);
+            REPORTER_ASSERT(reporter, rec.fStyle == style);
+            REPORTER_ASSERT(reporter, rec.fQuality == quality);
+        }
+    }
+}
+
+static void test_delete_looper(skiatest::Reporter* reporter, SkDrawLooper* lp, SkScalar sigma,
+                               SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) {
+    SkDrawLooper::BlurShadowRec rec;
+    bool success = lp->asABlurShadow(&rec);
+    REPORTER_ASSERT(reporter, success == expectSuccess);
+    if (success != expectSuccess) {
+        lp->asABlurShadow(&rec);
+    }
+    if (success) {
+        REPORTER_ASSERT(reporter, rec.fSigma == sigma);
+        REPORTER_ASSERT(reporter, rec.fStyle == style);
+        REPORTER_ASSERT(reporter, rec.fQuality == quality);
+    }
+    lp->unref();
+}
+
+static void make_noop_layer(SkLayerDrawLooper::Builder* builder) {
+    SkLayerDrawLooper::LayerInfo info;
+
+    info.fPaintBits = 0;
+    info.fColorMode = SkXfermode::kDst_Mode;
+    builder->addLayer(info);
+}
+
+static void make_blur_layer(SkLayerDrawLooper::Builder* builder, SkMaskFilter* mf) {
+    SkLayerDrawLooper::LayerInfo info;
+
+    info.fPaintBits = SkLayerDrawLooper::kMaskFilter_Bit;
+    info.fColorMode = SkXfermode::kSrc_Mode;
+    SkPaint* paint = builder->addLayer(info);
+    paint->setMaskFilter(mf);
+}
+
+static void test_layerDrawLooper(skiatest::Reporter* reporter, SkMaskFilter* mf, SkScalar sigma,
+                                 SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) {
+
+    SkLayerDrawLooper::LayerInfo info;
+    SkLayerDrawLooper::Builder builder;
+
+    // 1 layer is too few
+    make_noop_layer(&builder);
+    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false);
+
+    // 2 layers is good, but need blur
+    make_noop_layer(&builder);
+    make_noop_layer(&builder);
+    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false);
+
+    // 2 layers is just right
+    make_noop_layer(&builder);
+    make_blur_layer(&builder, mf);
+    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, expectSuccess);
+
+    // 3 layers is too many
+    make_noop_layer(&builder);
+    make_blur_layer(&builder, mf);
+    make_noop_layer(&builder);
+    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false);
+}
+
+static void test_asABlur(skiatest::Reporter* reporter) {
+    const SkBlurStyle styles[] = {
+        kNormal_SkBlurStyle, kSolid_SkBlurStyle, kOuter_SkBlurStyle, kInner_SkBlurStyle
+    };
+    const SkScalar sigmas[] = {
+        // values <= 0 should not success for a blur
+        -1, 0, 0.5f, 2
+    };
+
+    // Test asABlur for SkBlurMaskFilter
+    //
+    for (size_t i = 0; i < SK_ARRAY_COUNT(styles); ++i) {
+        const SkBlurStyle style = (SkBlurStyle)styles[i];
+        for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) {
+            const SkScalar sigma = sigmas[j];
+            for (int flags = 0; flags <= SkBlurMaskFilter::kAll_BlurFlag; ++flags) {
+                const SkBlurQuality quality = blurMaskFilterFlags_as_quality(flags);
+
+                SkAutoTUnref<SkMaskFilter> mf(SkBlurMaskFilter::Create(style, sigma, flags));
+                if (NULL == mf.get()) {
+                    REPORTER_ASSERT(reporter, sigma <= 0);
+                } else {
+                    REPORTER_ASSERT(reporter, sigma > 0);
+                    SkMaskFilter::BlurRec rec;
+                    bool success = mf->asABlur(&rec);
+                    if (flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
+                        REPORTER_ASSERT(reporter, !success);
+                    } else {
+                        REPORTER_ASSERT(reporter, success);
+                        REPORTER_ASSERT(reporter, rec.fSigma == sigma);
+                        REPORTER_ASSERT(reporter, rec.fStyle == style);
+                        REPORTER_ASSERT(reporter, rec.fQuality == quality);
+                    }
+                    test_layerDrawLooper(reporter, mf, sigma, style, quality, success);
+                }
+                test_blurDrawLooper(reporter, sigma, style, flags);
+            }
+        }
+    }
+
+    // Test asABlur for SkEmbossMaskFilter -- should never succeed
+    //
+    {
+        SkEmbossMaskFilter::Light light = {
+            { 1, 1, 1 }, 0, 127, 127
+        };
+        for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) {
+            const SkScalar sigma = sigmas[j];
+            SkAutoTUnref<SkMaskFilter> mf(SkEmbossMaskFilter::Create(sigma, light));
+            if (mf.get()) {
+                SkMaskFilter::BlurRec rec;
+                bool success = mf->asABlur(&rec);
+                REPORTER_ASSERT(reporter, !success);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
 DEF_GPUTEST(Blur, reporter, factory) {
     test_blur_drawing(reporter);
     test_sigma_range(reporter, factory);
+    test_asABlur(reporter);
 }