Just pass color glyph masks to filters.

Allow the filters to try to apply themselves to the color mask. Most
mask filters will just return false, but allow them the opprotunity.
This removes the behavior of trying to create a mask from the color
mask.

This updates the blur mask filter to handle kARGB32_Format directly by
using just the alpha (which mirrors current behavior).

Change-Id: I15eb736060ecf9b24aca874758c167b74d9ebc22
Reviewed-on: https://skia-review.googlesource.com/124185
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/src/core/SkBlurMask.cpp b/src/core/SkBlurMask.cpp
index ccdea57..f189cd6 100644
--- a/src/core/SkBlurMask.cpp
+++ b/src/core/SkBlurMask.cpp
@@ -97,8 +97,7 @@
 
 bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style,
                          SkIPoint* margin) {
-
-    if (src.fFormat != SkMask::kA8_Format) {
+    if (src.fFormat != SkMask::kA8_Format && src.fFormat != SkMask::kARGB32_Format) {
         return false;
     }
 
diff --git a/src/core/SkMaskBlurFilter.cpp b/src/core/SkMaskBlurFilter.cpp
index b8e245f..e6c62d2 100644
--- a/src/core/SkMaskBlurFilter.cpp
+++ b/src/core/SkMaskBlurFilter.cpp
@@ -17,43 +17,7 @@
 
 static const double kPi = 3.14159265358979323846264338327950288;
 
-class BlurScanInterface {
-public:
-    virtual ~BlurScanInterface() = default;
-    virtual void blur(const uint8_t* src, int srcStride, const uint8_t* srcEnd,
-                            uint8_t* dst, int dstStride,       uint8_t* dstEnd) const = 0;
-    virtual bool canBlur4() { return false; }
-    virtual void blur4Transpose(
-        const uint8_t* src, int srcStride, const uint8_t* srcEnd,
-              uint8_t* dst, int dstStride,       uint8_t* dstEnd) const {
-        SK_ABORT("This should not be called.");
-    }
-};
-
-class PlanningInterface {
-public:
-    virtual ~PlanningInterface() = default;
-    virtual size_t bufferSize() const = 0;
-    virtual int    border() const = 0;
-    virtual bool   needsBlur() const = 0;
-    virtual BlurScanInterface* makeBlurScan(
-        SkArenaAlloc* alloc, int width, uint32_t* buffer) const = 0;
-};
-
-class None final : public PlanningInterface {
-public:
-    None() = default;
-    size_t bufferSize() const override { return 0; }
-    int    border()     const override { return 0; }
-    bool   needsBlur()  const override { return false; }
-    BlurScanInterface* makeBlurScan(
-        SkArenaAlloc* alloc, int width, uint32_t* buffer) const override {
-        SK_ABORT("Should never be called.");
-        return nullptr;
-    }
-};
-
-class PlanGauss final : public PlanningInterface {
+class PlanGauss final {
 public:
     explicit PlanGauss(double sigma) {
         auto possibleWindow = static_cast<int>(floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5));
@@ -117,36 +81,17 @@
         fWeight = static_cast<uint64_t>(round(1.0 / divisor * (1ull << 32)));
     }
 
-    size_t bufferSize() const override { return fPass0Size + fPass1Size + fPass2Size; }
+    size_t bufferSize() const { return fPass0Size + fPass1Size + fPass2Size; }
 
-    int    border()     const override { return fBorder; }
-
-    bool needsBlur()    const override { return true; }
-
-    BlurScanInterface* makeBlurScan(
-        SkArenaAlloc* alloc, int width, uint32_t* buffer) const override
-    {
-        uint32_t* buffer0, *buffer0End, *buffer1, *buffer1End, *buffer2, *buffer2End;
-        buffer0 = buffer;
-        buffer0End = buffer1 = buffer0 + fPass0Size;
-        buffer1End = buffer2 = buffer1 + fPass1Size;
-        buffer2End = buffer2 + fPass2Size;
-        int noChangeCount = fSlidingWindow > width ? fSlidingWindow - width : 0;
-
-        return alloc->make<Gauss>(
-            fWeight, noChangeCount,
-            buffer0, buffer0End,
-            buffer1, buffer1End,
-            buffer2, buffer2End);
-    }
+    int    border()     const { return fBorder; }
 
 public:
-    class Gauss final : public BlurScanInterface {
+    class Scan {
     public:
-        Gauss(uint64_t weight, int noChangeCount,
-              uint32_t* buffer0, uint32_t* buffer0End,
-              uint32_t* buffer1, uint32_t* buffer1End,
-              uint32_t* buffer2, uint32_t* buffer2End)
+        Scan(uint64_t weight, int noChangeCount,
+             uint32_t* buffer0, uint32_t* buffer0End,
+             uint32_t* buffer1, uint32_t* buffer1End,
+             uint32_t* buffer2, uint32_t* buffer2End)
             : fWeight{weight}
             , fNoChangeCount{noChangeCount}
             , fBuffer0{buffer0}
@@ -158,7 +103,7 @@
         { }
 
         void blur(const uint8_t* src, int srcStride, const uint8_t* srcEnd,
-                        uint8_t* dst, int dstStride, uint8_t* dstEnd) const override {
+                        uint8_t* dst, int dstStride, uint8_t* dstEnd) const {
             auto buffer0Cursor = fBuffer0;
             auto buffer1Cursor = fBuffer1;
             auto buffer2Cursor = fBuffer2;
@@ -264,6 +209,21 @@
         uint32_t* fBuffer2End;
     };
 
+    Scan makeBlurScan(int width, uint32_t* buffer) const {
+        uint32_t* buffer0, *buffer0End, *buffer1, *buffer1End, *buffer2, *buffer2End;
+        buffer0 = buffer;
+        buffer0End = buffer1 = buffer0 + fPass0Size;
+        buffer1End = buffer2 = buffer1 + fPass1Size;
+        buffer2End = buffer2 + fPass2Size;
+        int noChangeCount = fSlidingWindow > width ? fSlidingWindow - width : 0;
+
+        return Scan(
+            fWeight, noChangeCount,
+            buffer0, buffer0End,
+            buffer1, buffer1End,
+            buffer2, buffer2End);
+    }
+
     uint64_t fWeight;
     int      fBorder;
     int      fSlidingWindow;
@@ -325,12 +285,13 @@
 static constexpr uint16_t _____ = 0u;
 static constexpr uint16_t kHalf = 0x80u;
 
-static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width) {
+static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width, int stride) {
+    SkASSERT(0 < width && width <= 8);
     uint8_t buffer[8];
-    if (width < 8) {
+    if (width < 8 || stride != 1) {
         sk_bzero(buffer, sizeof(buffer));
         for (int i = 0; i < width; i++) {
-            buffer[i] = from[i];
+            buffer[i] = from[i * stride];
         }
         from = buffer;
     }
@@ -580,7 +541,7 @@
     // Go by multiples of 8 in src.
     int x = 0;
     for (; x <= srcW - 8; x += 8) {
-        blur(load(src, 8), g0, g1, g2, g3, g4, &d0, &d8);
+        blur(load(src, 8, 1), g0, g1, g2, g3, g4, &d0, &d8);
 
         store(dst, d0, 8);
 
@@ -595,7 +556,7 @@
     int srcTail = srcW - x;
     if (srcTail > 0) {
 
-        blur(load(src, srcTail), g0, g1, g2, g3, g4, &d0, &d8);
+        blur(load(src, srcTail, 1), g0, g1, g2, g3, g4, &d0, &d8);
 
         int dstTail = std::min(8, dstW - x);
         store(dst, d0, dstTail);
@@ -790,26 +751,26 @@
 static SK_ALWAYS_INLINE void blur_column(
         BlurY blur, int radius, int width,
         const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h& g3, const Sk8h& g4,
-        const uint8_t* src, size_t srcStride, int srcH,
-        uint8_t* dst, size_t dstStride) {
+        const uint8_t* src, size_t srcRB, int srcStride, int srcH,
+        uint8_t* dst, size_t dstRB) {
     Sk8h d01{kHalf}, d12{kHalf}, d23{kHalf}, d34{kHalf},
          d45{kHalf}, d56{kHalf}, d67{kHalf}, d78{kHalf};
 
     auto flush = [&](uint8_t* to, const Sk8h& v0, const Sk8h& v1) {
         store(to, v0, width);
-        to += dstStride;
+        to += dstRB;
         store(to, v1, width);
-        return to + dstStride;
+        return to + dstRB;
     };
 
     for (int y = 0; y < srcH; y += 1) {
-        auto s = load(src, width);
+        auto s = load(src, width, srcStride);
         auto b = blur(s,
                       g0, g1, g2, g3, g4,
                       &d01, &d12, &d23, &d34, &d45, &d56, &d67, &d78);
         store(dst, b, width);
-        src += srcStride;
-        dst += dstStride;
+        src += srcRB;
+        dst += dstRB;
     }
 
     if (radius >= 1) {
@@ -829,8 +790,8 @@
 // BlurY will be one of blur_y_radius_(1|2|3|4).
 static SK_ALWAYS_INLINE void blur_y_rect(
         BlurY blur, int radius, uint16_t *gauss,
-        const uint8_t *src, size_t srcStride, int srcW, int srcH,
-        uint8_t *dst, size_t dstStride) {
+        const uint8_t *src, size_t srcRB, int srcStride, int srcW, int srcH,
+        uint8_t *dst, size_t dstRB) {
 
     Sk8h g0{gauss[0]},
          g1{gauss[1]},
@@ -842,9 +803,9 @@
     for (; x <= srcW - 8; x += 8) {
         blur_column(blur, radius, 8,
                     g0, g1, g2, g3, g4,
-                    src, srcStride, srcH,
-                    dst, dstStride);
-        src += 8;
+                    src, srcRB, srcStride, srcH,
+                    dst, dstRB);
+        src += 8 * srcStride;
         dst += 8;
     }
 
@@ -852,39 +813,39 @@
     if (xTail > 0) {
         blur_column(blur, radius, xTail,
                     g0, g1, g2, g3, g4,
-                    src, srcStride, srcH,
-                    dst, dstStride);
+                    src, srcRB, srcStride, srcH,
+                    dst, dstRB);
     }
 }
 
 SK_ATTRIBUTE(noinline) static void direct_blur_y(
         int radius, uint16_t* gauss,
-        const uint8_t* src, size_t srcStride, int srcW, int srcH,
-              uint8_t* dst, size_t dstStride) {
+        const uint8_t* src, size_t srcRB, int srcStride, int srcW, int srcH,
+              uint8_t* dst, size_t dstRB) {
 
     switch (radius) {
         case 1:
             blur_y_rect(blur_y_radius_1, 1, gauss,
-                        src, srcStride, srcW, srcH,
-                        dst, dstStride);
+                        src, srcRB, srcStride, srcW, srcH,
+                        dst, dstRB);
             break;
 
         case 2:
             blur_y_rect(blur_y_radius_2, 2, gauss,
-                        src, srcStride, srcW, srcH,
-                        dst, dstStride);
+                        src, srcRB, srcStride, srcW, srcH,
+                        dst, dstRB);
             break;
 
         case 3:
             blur_y_rect(blur_y_radius_3, 3, gauss,
-                        src, srcStride, srcW, srcH,
-                        dst, dstStride);
+                        src, srcRB, srcStride, srcW, srcH,
+                        dst, dstRB);
             break;
 
         case 4:
             blur_y_rect(blur_y_radius_4, 4, gauss,
-                        src, srcStride, srcW, srcH,
-                        dst, dstStride);
+                        src, srcRB, srcStride, srcW, srcH,
+                        dst, dstRB);
             break;
 
         default:
@@ -932,20 +893,27 @@
     int dstW = dst->fBounds.width(),
         dstH = dst->fBounds.height();
 
-    size_t srcStride = src.fRowBytes,
-           dstStride = dst->fRowBytes;
+    size_t srcRB = src.fRowBytes,
+           dstRB = dst->fRowBytes;
 
     //TODO: handle bluring in only one direction.
 
+    int srcStride = 1;
+    int srcAlphaOffset = 0;
+    if (src.fFormat == SkMask::kARGB32_Format) {
+        srcStride = 4;
+        srcAlphaOffset = SK_A32_SHIFT / 8;
+    }
+
     // Blur vertically and copy to destination.
     direct_blur_y(radiusY, gaussFactorsY,
-                  src.fImage,  srcStride, srcW, srcH,
-                  dst->fImage + radiusX, dstStride);
+                  src.fImage + srcAlphaOffset, srcRB, srcStride, srcW, srcH,
+                  dst->fImage + radiusX, dstRB);
 
     // Blur horizontally in place.
     direct_blur_x(radiusX, gaussFactorsX,
-                  dst->fImage + radiusX,  dstStride, srcW,
-                  dst->fImage,            dstStride, dstW, dstH);
+                  dst->fImage + radiusX,  dstRB, srcW,
+                  dst->fImage,            dstRB, dstW, dstH);
 
     return {radiusX, radiusY};
 }
@@ -961,11 +929,11 @@
     // 1024 is a place holder guess until more analysis can be done.
     SkSTArenaAlloc<1024> alloc;
 
-    PlanningInterface* planW = alloc.make<PlanGauss>(fSigmaW);
-    PlanningInterface* planH = alloc.make<PlanGauss>(fSigmaH);
+    PlanGauss planW(fSigmaW);
+    PlanGauss planH(fSigmaH);
 
-    int borderW = planW->border(),
-        borderH = planH->border();
+    int borderW = planW.border(),
+        borderH = planH.border();
     SkASSERT(borderH >= 0 && borderW >= 0);
 
     *dst = prepare_destination(borderW, borderH, src);
@@ -983,41 +951,40 @@
         dstH = dst->fBounds.height();
     SkASSERT(srcW >= 0 && srcH >= 0 && dstW >= 0 && dstH >= 0);
 
-    auto bufferSize = std::max(planW->bufferSize(), planH->bufferSize());
+    auto bufferSize = std::max(planW.bufferSize(), planH.bufferSize());
     auto buffer = alloc.makeArrayDefault<uint32_t>(bufferSize);
 
-    if (planW->needsBlur() && planH->needsBlur()) {
-        // Blur both directions.
-        int tmpW = srcH,
-            tmpH = dstW;
+    // Blur both directions.
+    int tmpW = srcH,
+        tmpH = dstW;
 
-        auto tmp = alloc.makeArrayDefault<uint8_t>(tmpW * tmpH);
+    auto tmp = alloc.makeArrayDefault<uint8_t>(tmpW * tmpH);
 
-        // Blur horizontally, and transpose.
-        auto scanW = planW->makeBlurScan(&alloc, srcW, buffer);
-        for (int y = 0; y < srcH; y++) {
-            auto srcStart = &src.fImage[y * src.fRowBytes];
-            auto tmpStart = &tmp[y];
-            scanW->blur(srcStart,    1, srcStart + srcW,
-                        tmpStart, tmpW, tmpStart + tmpW * tmpH);
-        }
+    int srcStride = 1;
+    int srcAlphaOffset = 0;
+    if (src.fFormat == SkMask::kARGB32_Format) {
+        srcStride = 4;
+        srcAlphaOffset = SK_A32_SHIFT / 8;
+    }
 
-        // Blur vertically (scan in memory order because of the transposition),
-        // and transpose back to the original orientation.
-        auto scanH = planH->makeBlurScan(&alloc, tmpW, buffer);
-        for (int y = 0; y < tmpH; y++) {
-            auto tmpStart = &tmp[y * tmpW];
-            auto dstStart = &dst->fImage[y];
+    // Blur horizontally, and transpose.
+    const PlanGauss::Scan& scanW = planW.makeBlurScan(srcW, buffer);
+    for (int y = 0; y < srcH; y++) {
+        auto srcStart = &src.fImage[y * src.fRowBytes] + srcAlphaOffset;
+        auto tmpStart = &tmp[y];
+        scanW.blur(srcStart, srcStride, srcStart + srcW * srcStride,
+                   tmpStart, tmpW, tmpStart + tmpW * tmpH);
+    }
 
-            scanH->blur(tmpStart, 1, tmpStart + tmpW,
-                        dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
-        }
-    } else {
-        // Copy to dst. No Blur.
-        SkASSERT(false);    // should not get here
-        for (int y = 0; y < srcH; y++) {
-            std::memcpy(&dst->fImage[y * dst->fRowBytes], &src.fImage[y * src.fRowBytes], dstW);
-        }
+    // Blur vertically (scan in memory order because of the transposition),
+    // and transpose back to the original orientation.
+    const PlanGauss::Scan& scanH = planH.makeBlurScan(tmpW, buffer);
+    for (int y = 0; y < tmpH; y++) {
+        auto tmpStart = &tmp[y * tmpW];
+        auto dstStart = &dst->fImage[y];
+
+        scanH.blur(tmpStart, 1, tmpStart + tmpW,
+                   dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
     }
 
     return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 7c90634..b17d942 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -34,11 +34,6 @@
     }
 }
 
-bool SkMaskFilterBase::filterMask(SkMask*, const SkMask&, const SkMatrix&,
-                              SkIPoint*) const {
-    return false;
-}
-
 bool SkMaskFilterBase::asABlur(BlurRec*) const {
     return false;
 }
diff --git a/src/core/SkMaskFilterBase.h b/src/core/SkMaskFilterBase.h
index b61ec98..16f9645 100644
--- a/src/core/SkMaskFilterBase.h
+++ b/src/core/SkMaskFilterBase.h
@@ -54,7 +54,7 @@
         @return true if the dst mask was correctly created.
     */
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                            SkIPoint* margin) const;
+                            SkIPoint* margin) const = 0;
 
 #if SK_SUPPORT_GPU
     /**
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index 68eb568..b8cd107 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -130,7 +130,7 @@
     }
 
     // If we are going to create the mask, then we cannot keep the color
-    if ((generatingImageFromPath || fMaskFilter) && SkMask::kARGB32_Format == glyph->fMaskFormat) {
+    if (generatingImageFromPath && SkMask::kARGB32_Format == glyph->fMaskFormat) {
         glyph->fMaskFormat = SkMask::kA8_Format;
     }
 
@@ -382,26 +382,6 @@
     }
 }
 
-static void extract_alpha(const SkMask& dst,
-                          const SkPMColor* srcRow, size_t srcRB) {
-    int width = dst.fBounds.width();
-    int height = dst.fBounds.height();
-    int dstRB = dst.fRowBytes;
-    uint8_t* dstRow = dst.fImage;
-
-    for (int y = 0; y < height; ++y) {
-        for (int x = 0; x < width; ++x) {
-            dstRow[x] = SkGetPackedA32(srcRow[x]);
-        }
-        // zero any padding on each row
-        for (int x = width; x < dstRB; ++x) {
-            dstRow[x] = 0;
-        }
-        dstRow += dstRB;
-        srcRow = (const SkPMColor*)((const char*)srcRow + srcRB);
-    }
-}
-
 void SkScalerContext::getImage(const SkGlyph& origGlyph) {
     const SkGlyph*  glyph = &origGlyph;
     SkGlyph         tmpGlyph;
@@ -456,19 +436,7 @@
         // the src glyph image shouldn't be 3D
         SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
 
-        SkAutoSMalloc<32*32> a8storage;
         glyph->toMask(&srcM);
-        if (SkMask::kARGB32_Format == srcM.fFormat) {
-            // now we need to extract the alpha-channel from the glyph's image
-            // and copy it into a temp buffer, and then point srcM at that temp.
-            srcM.fFormat = SkMask::kA8_Format;
-            srcM.fRowBytes = SkAlign4(srcM.fBounds.width());
-            size_t size = srcM.computeImageSize();
-            a8storage.reset(size);
-            srcM.fImage = (uint8_t*)a8storage.get();
-            extract_alpha(srcM,
-                          (const SkPMColor*)glyph->fImage, glyph->rowBytes());
-        }
 
         fRec.getMatrixFrom2x2(&matrix);
 
@@ -496,7 +464,7 @@
             }
             SkMask::FreeImage(dstM.fImage);
 
-            if (fPreBlendForFilter.isApplicable()) {
+            if (SkMask::kA8_Format == dstM.fFormat && fPreBlendForFilter.isApplicable()) {
                 applyLUTToA8Mask(srcM, fPreBlendForFilter.fG);
             }
         }
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
index e991d94..4a4c8d0 100644
--- a/src/effects/SkEmbossMaskFilter.cpp
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -72,6 +72,10 @@
 
 bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
                                     const SkMatrix& matrix, SkIPoint* margin) const {
+    if (src.fFormat != SkMask::kA8_Format) {
+        return false;
+    }
+
     SkScalar sigma = matrix.mapRadius(fBlurSigma);
 
     if (!SkBlurMask::BoxBlur(dst, src, sigma, kInner_SkBlurStyle)) {
diff --git a/src/effects/SkShaderMaskFilter.cpp b/src/effects/SkShaderMaskFilter.cpp
index 8315994..cfa15c5 100644
--- a/src/effects/SkShaderMaskFilter.cpp
+++ b/src/effects/SkShaderMaskFilter.cpp
@@ -70,7 +70,9 @@
 
 bool SkShaderMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm,
                             SkIPoint* margin) const {
-    SkASSERT(src.fFormat == SkMask::kA8_Format);
+    if (src.fFormat != SkMask::kA8_Format) {
+        return false;
+    }
 
     if (margin) {
         margin->set(0, 0);