Cache blur mask for rects which can not break into nine-patch

With this CL performance improves:
blurrectsnonninepatch   42.4us -> 20.5us        0.48x

BUG=431021,skia:3118

Review URL: https://codereview.chromium.org/729463002
diff --git a/src/core/SkMaskCache.cpp b/src/core/SkMaskCache.cpp
index b4b8b4f..207ea0e 100644
--- a/src/core/SkMaskCache.cpp
+++ b/src/core/SkMaskCache.cpp
@@ -18,6 +18,28 @@
 namespace {
 static unsigned gRRectBlurKeyNamespaceLabel;
 
+static bool copy_cacheddata_to_mask(SkCachedData* data, SkMask* mask) {
+    const size_t size = data->size();
+    SkASSERT(mask->computeTotalImageSize() <= size);
+
+    mask->fImage = SkMask::AllocImage(size);
+    if (mask->fImage) {
+        memcpy(mask->fImage, data->data(), size);
+        return true;
+    }
+    return false;
+}
+
+static SkCachedData* copy_mask_to_cacheddata(const SkMask& mask) {
+    const size_t size = mask.computeTotalImageSize();
+    SkCachedData* data = SkResourceCache::NewCachedData(size);
+    if (data) {
+        memcpy(data->writable_data(), mask.fImage, size);
+        return data;
+    }
+    return NULL;
+}
+
 struct RRectBlurKey : public SkResourceCache::Key {
 public:
     RRectBlurKey(SkScalar sigma, const SkRRect& rrect, SkBlurStyle style, SkBlurQuality quality)
@@ -90,6 +112,20 @@
     return CHECK_LOCAL(localCache, add, Add, SkNEW_ARGS(RRectBlurRec, (key, mask, data)));
 }
 
+bool SkMaskCache::FindAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                              const SkRRect& rrect, SkMask* mask) {
+    SkAutoTUnref<SkCachedData> data(SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask));
+    return data.get() && copy_cacheddata_to_mask(data, mask);
+}
+
+void SkMaskCache::AddAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                             const SkRRect& rrect, const SkMask& mask) {
+    SkAutoTUnref<SkCachedData> data(copy_mask_to_cacheddata(mask));
+    if (data.get()) {
+        SkMaskCache::Add(sigma, style, quality, rrect, mask, data);
+    }
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 
 namespace {
@@ -181,3 +217,17 @@
     RectsBlurKey key(sigma, style, quality, rects, count);
     return CHECK_LOCAL(localCache, add, Add, SkNEW_ARGS(RectsBlurRec, (key, mask, data)));
 }
+
+bool SkMaskCache::FindAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                              const SkRect rects[], int count, SkMask* mask) {
+    SkAutoTUnref<SkCachedData> data(SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask));
+    return data.get() && copy_cacheddata_to_mask(data, mask);
+}
+
+void SkMaskCache::AddAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                             const SkRect rects[], int count, const SkMask& mask) {
+    SkAutoTUnref<SkCachedData> data(copy_mask_to_cacheddata(mask));
+    if (data.get()) {
+        SkMaskCache::Add(sigma, style, quality, rects, count, mask, data);
+    }
+}
diff --git a/src/core/SkMaskCache.h b/src/core/SkMaskCache.h
index f98387b..2cfb971 100644
--- a/src/core/SkMaskCache.h
+++ b/src/core/SkMaskCache.h
@@ -39,6 +39,25 @@
     static void Add(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
                     const SkRect rects[], int count, const SkMask& mask, SkCachedData* data,
                     SkResourceCache* localCache = NULL);
+
+    /**
+     * On success, set mask with cached value, allocate memory for mask->fImage,
+     * copy pixels from SkCachedData in the cache to mask->fImage, then return true.
+     *
+     * On failure, return false, no memory allocated for mask->fImage.
+     */
+    static bool FindAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                            const SkRRect& rrect, SkMask* mask);
+    static bool FindAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                            const SkRect rects[], int count, SkMask* mask);
+
+    /**
+     * Create a new SkCachedData, copy pixels from mask.fImage to it, then add it into cache.
+     */
+    static void AddAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                           const SkRRect& rrect, const SkMask& mask);
+    static void AddAndCopy(SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                           const SkRect rects[], int count, const SkMask& mask);
 };
 
 #endif
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index c9783e7..3a4e3a4 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -10,6 +10,7 @@
 #include "SkMaskFilter.h"
 #include "SkBlitter.h"
 #include "SkDraw.h"
+#include "SkMaskCache.h"
 #include "SkRasterClip.h"
 #include "SkRRect.h"
 #include "SkTypes.h"
@@ -263,9 +264,41 @@
     }
     SkAutoMaskFreeImage autoSrc(srcM.fImage);
 
-    if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
-        return false;
+    BlurRec rec;
+    if (this->asABlur(&rec) && rectCount) {
+        SkScalar scaledSigma = matrix.mapRadius(rec.fSigma);
+        if (!SkMaskCache::FindAndCopy(scaledSigma, rec.fStyle, rec.fQuality,
+                                      rects, rectCount, &dstM)) {
+            if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
+                return false;
+            }
+            SkMaskCache::AddAndCopy(scaledSigma, rec.fStyle, rec.fQuality, rects, rectCount, dstM);
+        } else {
+            // Compute the correct bounds of dst mask if dst mask is got from cache.
+            SkMask tmpSrc, tmpDst;
+            tmpSrc = srcM;
+            tmpSrc.fImage = NULL;
+            if (!this->filterMask(&tmpDst, tmpSrc, matrix, NULL)) {
+                return false;
+            }
+
+            // Fallback to original calculation if size of bounds is different with
+            // size of the cached mask.
+            if (dstM.fBounds.width() != tmpDst.fBounds.width() ||
+                dstM.fBounds.height() != tmpDst.fBounds.height()) {
+                if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
+                    return false;
+                }
+            } else {
+                dstM.fBounds = tmpDst.fBounds;
+            }
+        }
+    } else {
+        if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
+            return false;
+        }
     }
+
     SkAutoMaskFreeImage autoDst(dstM.fImage);
 
     // if we get here, we need to (possibly) resolve the clip and blitter
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 97ae436..38b2acc 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -11,6 +11,7 @@
 #include "SkGpuBlurUtils.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
+#include "SkMaskCache.h"
 #include "SkMaskFilter.h"
 #include "SkRRect.h"
 #include "SkRTConf.h"
@@ -260,58 +261,6 @@
            r.width() > v || r.height() > v;
 }
 
-#include "SkMaskCache.h"
-
-static bool copy_cacheddata_to_mask(SkCachedData* data, SkMask* mask) {
-    const size_t size = data->size();
-    SkASSERT(mask->computeTotalImageSize() <= size);
-
-    mask->fImage = SkMask::AllocImage(size);
-    if (mask->fImage) {
-        memcpy(mask->fImage, data->data(), size);
-        return true;
-    }
-    return false;
-}
-
-static SkCachedData* copy_mask_to_cacheddata(const SkMask& mask) {
-    const size_t size = mask.computeTotalImageSize();
-    SkCachedData* data = SkResourceCache::NewCachedData(size);
-    if (data) {
-        memcpy(data->writable_data(), mask.fImage, size);
-        return data;
-    }
-    return NULL;
-}
-
-static bool find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
-                              SkBlurQuality quality, const SkRRect& rrect) {
-    SkAutoTUnref<SkCachedData> data(SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask));
-    return data.get() && copy_cacheddata_to_mask(data, mask);
-}
-
-static void add_cached_rrect(const SkMask& mask, SkScalar sigma, SkBlurStyle style,
-                             SkBlurQuality quality, const SkRRect& rrect) {
-    SkAutoTUnref<SkCachedData> data(copy_mask_to_cacheddata(mask));
-    if (data.get()) {
-        SkMaskCache::Add(sigma, style, quality, rrect, mask, data);
-    }
-}
-
-static bool find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
-                              SkBlurQuality quality, const SkRect rects[], int count) {
-    SkAutoTUnref<SkCachedData> data(SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask));
-    return data.get() && copy_cacheddata_to_mask(data, mask);
-}
-
-static void add_cached_rects(const SkMask& mask, SkScalar sigma, SkBlurStyle style,
-                             SkBlurQuality quality, const SkRect rects[], int count) {
-    SkAutoTUnref<SkCachedData> data(copy_mask_to_cacheddata(mask));
-    if (data.get()) {
-        SkMaskCache::Add(sigma, style, quality, rects, count, mask, data);
-    }
-}
-
 #ifdef SK_IGNORE_FAST_RRECT_BLUR
 SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticblurrrect", false, "Use the faster analytic blur approach for ninepatch rects" );
 #else
@@ -420,7 +369,7 @@
     smallRR.setRectRadii(smallR, radii);
 
     const SkScalar sigma = this->computeXformedSigma(matrix);
-    if (!find_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR)) {
+    if (!SkMaskCache::FindAndCopy(sigma, fBlurStyle, this->getQuality(), smallRR, &patch->fMask)) {
         bool analyticBlurWorked = false;
         if (c_analyticBlurRRect) {
             analyticBlurWorked =
@@ -439,7 +388,7 @@
                 return kFalse_FilterReturn;
             }
         }
-        add_cached_rrect(patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
+        SkMaskCache::AddAndCopy(sigma, fBlurStyle, this->getQuality(), smallRR, patch->fMask);
     }
 
     patch->fMask.fBounds.offsetTo(0, 0);
@@ -550,7 +499,8 @@
     }
 
     const SkScalar sigma = this->computeXformedSigma(matrix);
-    if (!find_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count)) {
+    if (!SkMaskCache::FindAndCopy(sigma, fBlurStyle, this->getQuality(),
+                                  smallR, count, &patch->fMask)) {
         if (count > 1 || !c_analyticBlurNinepatch) {
             if (!draw_rects_into_mask(smallR, count, &srcM)) {
                 return kFalse_FilterReturn;
@@ -567,7 +517,7 @@
                 return kFalse_FilterReturn;
             }
         }
-        add_cached_rects(patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
+        SkMaskCache::AddAndCopy(sigma, fBlurStyle, this->getQuality(), smallR, count, patch->fMask);
     }
     patch->fMask.fBounds.offsetTo(0, 0);
     patch->fOuterRect = dstM.fBounds;