This patch implements generalized DAG connectivity for SkImageFilter.  SkImageFilter maintains a list of inputs, which can be constructed either from a SkImageFilter** or zero or more SkImageFilter* arguments (varargs).

Existing filters which maintained their own filter connectivity were refactored to use the new constructors and flattening/unflattening code.  Modifying the remaining filters which are not yet DAG-friendly is left for future work; they are considered to have zero inputs for now.

Review URL: https://codereview.appspot.com/6443119

git-svn-id: http://skia.googlecode.com/svn/trunk@5891 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
index 73ce806..bcceb58 100644
--- a/gm/imagefiltersbase.cpp
+++ b/gm/imagefiltersbase.cpp
@@ -17,7 +17,7 @@
 
 class FailImageFilter : public SkImageFilter {
 public:
-    FailImageFilter() {}
+    FailImageFilter() : INHERITED(0) {}
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
 protected:
@@ -38,7 +38,7 @@
 
 class IdentityImageFilter : public SkImageFilter {
 public:
-    IdentityImageFilter() {}
+    IdentityImageFilter() : INHERITED(0) {}
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(IdentityImageFilter)
 protected:
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 92a5a58..f035bf9 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -109,8 +109,18 @@
     virtual GrTexture* onFilterImageGPU(Proxy*, GrTexture* texture, const SkRect& rect);
 
 protected:
-    SkImageFilter() {}
-    explicit SkImageFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+    SkImageFilter(int numInputs, SkImageFilter** inputs);
+
+    // The ... represents numInputs SkImageFilter pointers, upon which this
+    // constructor will call SkSafeRef().  This is the same behaviour as
+    // the SkImageFilter(int, SkImageFilter**) constructor above.
+    explicit SkImageFilter(int numInputs, ...);
+
+    virtual ~SkImageFilter();
+
+    explicit SkImageFilter(SkFlattenableReadBuffer& rb);
+
+    virtual void flatten(SkFlattenableWriteBuffer& wb) const SK_OVERRIDE;
 
     // Default impl returns false
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
@@ -118,8 +128,17 @@
     // Default impl copies src into dst and returns true
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
 
+    int numInputs() const { return fNumInputs; }
+    SkImageFilter* getInput(int i) const { SkASSERT(i < fNumInputs); return fInputs[i]; }
+    // Return the result of processing the given input, or the source bitmap
+    // if we have no connected input at that index.
+    SkBitmap getInputResult(int index, Proxy*, const SkBitmap& src, const SkMatrix&,
+                            SkIPoint*);
+
 private:
     typedef SkFlattenable INHERITED;
+    int fNumInputs;
+    SkImageFilter** fInputs;
 };
 
 #endif
diff --git a/include/effects/SkBlendImageFilter.h b/include/effects/SkBlendImageFilter.h
index be1be68..ff528ad 100644
--- a/include/effects/SkBlendImageFilter.h
+++ b/include/effects/SkBlendImageFilter.h
@@ -43,9 +43,9 @@
 
 private:
     Mode fMode;
-    SkImageFilter* fBackground;
-    SkImageFilter* fForeground;
     typedef SkImageFilter INHERITED;
+    SkImageFilter* getBackgroundInput() { return getInput(0); }
+    SkImageFilter* getForegroundInput() { return getInput(1); }
 };
 
 #endif
diff --git a/include/effects/SkSingleInputImageFilter.h b/include/effects/SkSingleInputImageFilter.h
index b29eca0..e7819c4 100644
--- a/include/effects/SkSingleInputImageFilter.h
+++ b/include/effects/SkSingleInputImageFilter.h
@@ -35,10 +35,9 @@
     GrTexture* getInputResultAsTexture(Proxy* proxy, GrTexture* src, const SkRect& rect);
 #endif
 
-    SkImageFilter* input() const { return fInput; }
+    SkImageFilter* input() const { return getInput(0); }
 private:
     typedef SkImageFilter INHERITED;
-    SkImageFilter* fInput;
 };
 
 #endif
diff --git a/include/effects/SkTestImageFilters.h b/include/effects/SkTestImageFilters.h
index 4c96776..c31a439 100755
--- a/include/effects/SkTestImageFilters.h
+++ b/include/effects/SkTestImageFilters.h
@@ -7,7 +7,7 @@
 
 class SkOffsetImageFilter : public SkImageFilter {
 public:
-    SkOffsetImageFilter(SkScalar dx, SkScalar dy) {
+    SkOffsetImageFilter(SkScalar dx, SkScalar dy) : INHERITED(0) {
         fOffset.set(dx, dy);
     }
 
@@ -29,28 +29,19 @@
 
 class SkComposeImageFilter : public SkImageFilter {
 public:
-    SkComposeImageFilter(SkImageFilter* outer, SkImageFilter* inner) {
-        fOuter = outer;
-        fInner = inner;
-        SkSafeRef(outer);
-        SkSafeRef(inner);
-    }
+    SkComposeImageFilter(SkImageFilter* outer, SkImageFilter* inner) : INHERITED(2, outer, inner) {}
     virtual ~SkComposeImageFilter();
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeImageFilter)
 
 protected:
     SkComposeImageFilter(SkFlattenableReadBuffer& buffer);
-    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
 
 private:
-    SkImageFilter*  fOuter;
-    SkImageFilter*  fInner;
-
     typedef SkImageFilter INHERITED;
 };
 
@@ -75,7 +66,6 @@
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
 
 private:
-    SkImageFilter**     fFilters;
     uint8_t*            fModes; // SkXfermode::Mode
     int                 fCount;
 
@@ -83,8 +73,8 @@
     // of the filters and modes (unless fCount is so large we can't fit).
     intptr_t    fStorage[16];
 
-    void initAlloc(int count, bool hasModes);
-    void init(SkImageFilter* const [], int count, const SkXfermode::Mode []);
+    void initAllocModes();
+    void initModes(const SkXfermode::Mode []);
 
     typedef SkImageFilter INHERITED;
 };
@@ -94,7 +84,7 @@
 // Fun mode that scales down (only) and then scales back up to look pixelated
 class SkDownSampleImageFilter : public SkImageFilter {
 public:
-    SkDownSampleImageFilter(SkScalar scale) : fScale(scale) {}
+    SkDownSampleImageFilter(SkScalar scale) : INHERITED(0), fScale(scale) {}
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDownSampleImageFilter)
 
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index bb31b79..573d49d 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -6,10 +6,76 @@
  */
 
 #include "SkImageFilter.h"
+
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
 #include "SkRect.h"
+#include "stdarg.h"
 
 SK_DEFINE_INST_COUNT(SkImageFilter)
 
+SkImageFilter::SkImageFilter(int numInputs, SkImageFilter** inputs)
+  : fNumInputs(numInputs), fInputs(new SkImageFilter*[numInputs]) {
+    for (int i = 0; i < numInputs; ++i) {
+        fInputs[i] = inputs[i];
+        SkSafeRef(fInputs[i]);
+    }
+}
+
+SkImageFilter::SkImageFilter(int numInputs, ...)
+  : fNumInputs(numInputs), fInputs(new SkImageFilter*[numInputs]) {
+    va_list ap;
+    va_start(ap, numInputs);
+    for (int i = 0; i < numInputs; ++i) {
+        fInputs[i] = va_arg(ap, SkImageFilter*);
+        SkSafeRef(fInputs[i]);
+    }
+    va_end(ap);
+}
+
+SkImageFilter::~SkImageFilter() {
+    for (int i = 0; i < fNumInputs; i++) {
+        SkSafeUnref(fInputs[i]);
+    }
+    delete[] fInputs;
+}
+
+SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer) 
+    : fNumInputs(buffer.readInt()), fInputs(new SkImageFilter*[fNumInputs]) {
+    for (int i = 0; i < fNumInputs; i++) {
+        if (buffer.readBool()) {
+            fInputs[i] = static_cast<SkImageFilter*>(buffer.readFlattenable());
+        } else {
+            fInputs[i] = NULL;
+        }
+    }
+}
+
+void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeInt(fNumInputs);
+    for (int i = 0; i < fNumInputs; i++) {
+        SkImageFilter* input = getInput(i);
+        buffer.writeBool(input != NULL);
+        if (input != NULL) {
+            buffer.writeFlattenable(input);
+        }
+    }
+}
+
+SkBitmap SkImageFilter::getInputResult(int index, Proxy* proxy,
+                                       const SkBitmap& src, const SkMatrix& ctm,
+                                       SkIPoint* loc) {
+    SkASSERT(index < fNumInputs);
+    SkImageFilter* input = getInput(index);
+    SkBitmap result;
+    if (input && input->filterImage(proxy, src, ctm, &result, loc)) {
+        return result;
+    } else {
+        return src;
+    }
+}
+
+
 bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
                                 const SkMatrix& ctm,
                                 SkBitmap* result, SkIPoint* loc) {
diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp
index 8aa0433..854df9d 100644
--- a/src/effects/SkBitmapSource.cpp
+++ b/src/effects/SkBitmapSource.cpp
@@ -7,7 +7,9 @@
 
 #include "SkBitmapSource.h"
 
-SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap) : fBitmap(bitmap) {
+SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap)
+  : INHERITED(0),
+    fBitmap(bitmap) {
 }
 
 SkBitmapSource::SkBitmapSource(SkFlattenableReadBuffer& buffer)
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index 1388e33..a1ad8ec 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -52,38 +52,22 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkBlendImageFilter::SkBlendImageFilter(SkBlendImageFilter::Mode mode, SkImageFilter* background, SkImageFilter* foreground)
-  : fMode(mode), fBackground(background), fForeground(foreground)
+  : INHERITED(2, background, foreground), fMode(mode)
 {
-    SkASSERT(NULL != background);
-    SkSafeRef(fBackground);
-    SkSafeRef(fForeground);
 }
 
 SkBlendImageFilter::~SkBlendImageFilter() {
-    SkSafeUnref(fBackground);
-    SkSafeUnref(fForeground);
 }
 
 SkBlendImageFilter::SkBlendImageFilter(SkFlattenableReadBuffer& buffer)
   : INHERITED(buffer)
 {
     fMode = (SkBlendImageFilter::Mode) buffer.readInt();
-    fBackground = buffer.readFlattenableT<SkImageFilter>();
-    if (buffer.readBool()) {
-        fForeground = buffer.readFlattenableT<SkImageFilter>();
-    } else {
-        fForeground = NULL;
-    }
 }
 
 void SkBlendImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     buffer.writeInt((int) fMode);
-    buffer.writeFlattenable(fBackground);
-    buffer.writeBool(NULL != fForeground);
-    if (NULL != fForeground) {
-        buffer.writeFlattenable(fForeground);
-    }
 }
 
 bool SkBlendImageFilter::onFilterImage(Proxy* proxy,
@@ -92,11 +76,13 @@
                                        SkBitmap* dst,
                                        SkIPoint* offset) {
     SkBitmap background, foreground = src;
-    SkASSERT(NULL != fBackground);
-    if (!fBackground->filterImage(proxy, src, ctm, &background, offset)) {
+    SkImageFilter* backgroundInput = getBackgroundInput();
+    SkImageFilter* foregroundInput = getForegroundInput();
+    SkASSERT(NULL != backgroundInput);
+    if (!backgroundInput->filterImage(proxy, src, ctm, &background, offset)) {
         return false;
     }
-    if (fForeground && !fForeground->filterImage(proxy, src, ctm, &foreground, offset)) {
+    if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) {
         return false;
     }
     SkAutoLockPixels alp_foreground(foreground), alp_background(background);
@@ -198,8 +184,8 @@
 }
 
 GrTexture* SkBlendImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
-    SkAutoTUnref<GrTexture> background(getInputResultAsTexture(proxy, fBackground, src, rect));
-    SkAutoTUnref<GrTexture> foreground(getInputResultAsTexture(proxy, fForeground, src, rect));
+    SkAutoTUnref<GrTexture> background(getInputResultAsTexture(proxy, getBackgroundInput(), src, rect));
+    SkAutoTUnref<GrTexture> foreground(getInputResultAsTexture(proxy, getForegroundInput(), src, rect));
     GrContext* context = src->getContext();
 
     GrTextureDesc desc;
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index cf64308..9871fc0 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -241,8 +241,9 @@
     fInset = buffer.readScalar();
 }
 
+// FIXME:  implement single-input semantics
 SkMagnifierImageFilter::SkMagnifierImageFilter(SkRect srcRect, SkScalar inset)
-    : fSrcRect(srcRect), fInset(inset) {
+    : INHERITED(0), fSrcRect(srcRect), fInset(inset) {
     SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0);
 }
 
diff --git a/src/effects/SkSingleInputImageFilter.cpp b/src/effects/SkSingleInputImageFilter.cpp
index 3dd9ef9..2019e25 100644
--- a/src/effects/SkSingleInputImageFilter.cpp
+++ b/src/effects/SkSingleInputImageFilter.cpp
@@ -15,57 +15,45 @@
 #include "SkGrPixelRef.h"
 #endif
 
-SkSingleInputImageFilter::SkSingleInputImageFilter(SkImageFilter* input) : fInput(input) {
-    SkSafeRef(fInput);
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkImageFilter* input) : INHERITED(1, input) {
 }
 
 SkSingleInputImageFilter::~SkSingleInputImageFilter() {
-    SkSafeUnref(fInput);
 }
 
-SkSingleInputImageFilter::SkSingleInputImageFilter(SkFlattenableReadBuffer& rb) {
-    if (rb.readBool()) {
-        fInput = rb.readFlattenableT<SkImageFilter>();
-    } else {
-        fInput = NULL;
-    }
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkFlattenableReadBuffer& rb)
+    : INHERITED(rb) {
 }
 
 void SkSingleInputImageFilter::flatten(SkFlattenableWriteBuffer& wb) const {
-    wb.writeBool(NULL != fInput);
-    if (NULL != fInput) {
-        wb.writeFlattenable(fInput);
-    }
+    this->INHERITED::flatten(wb);
 }
 
 SkBitmap SkSingleInputImageFilter::getInputResult(Proxy* proxy,
                                                   const SkBitmap& src,
                                                   const SkMatrix& ctm,
                                                   SkIPoint* offset) {
-    SkBitmap result;
-    if (fInput && fInput->filterImage(proxy, src, ctm, &result, offset)) {
-        return result;
-    } else {
-        return src;
-    }
+    return this->INHERITED::getInputResult(0, proxy, src, ctm, offset);
 }
 
 #if SK_SUPPORT_GPU
+// FIXME:  generalize and move to base class
 GrTexture* SkSingleInputImageFilter::getInputResultAsTexture(Proxy* proxy,
                                                              GrTexture* src,
                                                              const SkRect& rect) {
     GrTexture* resultTex = NULL;
-    if (!fInput) {
+    SkImageFilter* input = getInput(0);
+    if (!input) {
         resultTex = src;
-    } else if (fInput->canFilterImageGPU()) {
+    } else if (input->canFilterImageGPU()) {
         // onFilterImageGPU() already refs the result, so just return it here.
-        return fInput->onFilterImageGPU(proxy, src, rect);
+        return input->onFilterImageGPU(proxy, src, rect);
     } else {
         SkBitmap srcBitmap, result;
         srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, src->width(), src->height());
         srcBitmap.setPixelRef(new SkGrPixelRef(src))->unref();
         SkIPoint offset;
-        if (fInput->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
+        if (input->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
             if (result.getTexture()) {
                 resultTex = (GrTexture*) result.getTexture();
             } else {
diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
index d28fda9..a51149d 100755
--- a/src/effects/SkTestImageFilters.cpp
+++ b/src/effects/SkTestImageFilters.cpp
@@ -53,8 +53,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkComposeImageFilter::~SkComposeImageFilter() {
-    SkSafeUnref(fInner);
-    SkSafeUnref(fOuter);
 }
 
 bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
@@ -62,117 +60,102 @@
                                          const SkMatrix& ctm,
                                          SkBitmap* result,
                                          SkIPoint* loc) {
-    if (!fOuter && !fInner) {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    if (!outer && !inner) {
         return false;
     }
 
-    if (!fOuter || !fInner) {
-        return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
+    if (!outer || !inner) {
+        return (outer ? outer : inner)->filterImage(proxy, src, ctm, result, loc);
     }
 
     SkBitmap tmp;
-    return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
-           fOuter->filterImage(proxy, tmp, ctm, result, loc);
+    return inner->filterImage(proxy, src, ctm, &tmp, loc) &&
+           outer->filterImage(proxy, tmp, ctm, result, loc);
 }
 
 bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
                                           const SkMatrix& ctm,
                                           SkIRect* dst) {
-    if (!fOuter && !fInner) {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    if (!outer && !inner) {
         return false;
     }
 
-    if (!fOuter || !fInner) {
-        return (fOuter ? fOuter : fInner)->filterBounds(src, ctm, dst);
+    if (!outer || !inner) {
+        return (outer ? outer : inner)->filterBounds(src, ctm, dst);
     }
 
     SkIRect tmp;
-    return fInner->filterBounds(src, ctm, &tmp) &&
-           fOuter->filterBounds(tmp, ctm, dst);
-}
-
-void SkComposeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-
-    buffer.writeFlattenable(fOuter);
-    buffer.writeFlattenable(fInner);
+    return inner->filterBounds(src, ctm, &tmp) &&
+           outer->filterBounds(tmp, ctm, dst);
 }
 
 SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    fOuter = buffer.readFlattenableT<SkImageFilter>();
-    fInner = buffer.readFlattenableT<SkImageFilter>();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-template <typename T> T* SkSafeRefReturn(T* obj) {
-    SkSafeRef(obj);
-    return obj;
-}
-
-void SkMergeImageFilter::initAlloc(int count, bool hasModes) {
-    if (count < 1) {
-        fFilters = NULL;
-        fModes = NULL;
-        fCount = 0;
-    } else {
-        int modeCount = hasModes ? count : 0;
-        size_t size = sizeof(SkImageFilter*) * count + sizeof(uint8_t) * modeCount;
+void SkMergeImageFilter::initAllocModes() {
+    if (numInputs()) {
+        size_t size = sizeof(uint8_t) * numInputs();
         if (size <= sizeof(fStorage)) {
-            fFilters = SkTCast<SkImageFilter**>(fStorage);
+            fModes = SkTCast<uint8_t*>(fStorage);
         } else {
-            fFilters = SkTCast<SkImageFilter**>(sk_malloc_throw(size));
+            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
         }
-        fModes = hasModes ? SkTCast<uint8_t*>(fFilters + count) : NULL;
-        fCount = count;
+    } else {
+        fModes = NULL;
     }
 }
 
-void SkMergeImageFilter::init(SkImageFilter* const filters[], int count,
-                              const SkXfermode::Mode modes[]) {
-    this->initAlloc(count, !!modes);
-    for (int i = 0; i < count; ++i) {
-        fFilters[i] = SkSafeRefReturn(filters[i]);
-        if (modes) {
+void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
+    if (modes) {
+        this->initAllocModes();
+        for (int i = 0; i < numInputs(); ++i) {
             fModes[i] = SkToU8(modes[i]);
         }
+    } else {
+        fModes = NULL;
     }
 }
 
 SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
-                                       SkXfermode::Mode mode) {
-    SkImageFilter* filters[] = { first, second };
-    SkXfermode::Mode modes[] = { mode, mode };
-    this->init(filters, 2, SkXfermode::kSrcOver_Mode == mode ? NULL : modes);
+                                       SkXfermode::Mode mode) : INHERITED(2, first, second) {
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        SkXfermode::Mode modes[] = { mode, mode };
+        this->initModes(modes);
+    } else {
+        fModes = NULL;
+    }
 }
 
 SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const filters[], int count,
-                                       const SkXfermode::Mode modes[]) {
-    this->init(filters, count, modes);
+                                       const SkXfermode::Mode modes[]) : INHERITED(count, filters) {
+    this->initModes(modes);
 }
 
 SkMergeImageFilter::~SkMergeImageFilter() {
-    for (int i = 0; i < fCount; ++i) {
-        SkSafeUnref(fFilters[i]);
-    }
 
-    if (fFilters != SkTCast<SkImageFilter**>(fStorage)) {
-        sk_free(fFilters);
-        // fModes is allocated in the same block as fFilters, so no need to
-        // separately free it.
+    if (fModes != SkTCast<uint8_t*>(fStorage)) {
+        sk_free(fModes);
     }
 }
 
 bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                         SkIRect* dst) {
-    if (fCount < 1) {
+    if (numInputs() < 1) {
         return false;
     }
 
     SkIRect totalBounds;
 
-    for (int i = 0; i < fCount; ++i) {
-        SkImageFilter* filter = fFilters[i];
+    for (int i = 0; i < numInputs(); ++i) {
+        SkImageFilter* filter = getInput(i);
         SkIRect r;
         if (filter) {
             if (!filter->filterBounds(src, ctm, &r)) {
@@ -197,7 +180,7 @@
 bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                        const SkMatrix& ctm,
                                        SkBitmap* result, SkIPoint* loc) {
-    if (fCount < 1) {
+    if (numInputs() < 1) {
         return false;
     }
 
@@ -218,11 +201,11 @@
     OwnDeviceCanvas canvas(dst);
     SkPaint paint;
 
-    for (int i = 0; i < fCount; ++i) {
+    for (int i = 0; i < numInputs(); ++i) {
         SkBitmap tmp;
         const SkBitmap* srcPtr;
         SkIPoint pos = *loc;
-        SkImageFilter* filter = fFilters[i];
+        SkImageFilter* filter = getInput(i);
         if (filter) {
             if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
                 return false;
@@ -248,37 +231,20 @@
 void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    int storedCount = fCount;
+    buffer.writeBool(fModes != NULL);
     if (fModes) {
-        // negative count signals we have modes
-        storedCount = -storedCount;
-    }
-    buffer.writeInt(storedCount);
-
-    if (fCount) {
-        for (int i = 0; i < fCount; ++i) {
-            buffer.writeFlattenable(fFilters[i]);
-        }
-        if (fModes) {
-            buffer.writeByteArray(fModes, fCount * sizeof(fModes[0]));
-        }
+        buffer.writeByteArray(fModes, numInputs() * sizeof(fModes[0]));
     }
 }
 
 SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    int storedCount = buffer.readInt();
-    this->initAlloc(SkAbs32(storedCount), storedCount < 0);
-
-    for (int i = 0; i < fCount; ++i) {
-        fFilters[i] = buffer.readFlattenableT<SkImageFilter>();
-    }
-
-    if (fModes) {
-        SkASSERT(storedCount < 0);
-        SkASSERT(buffer.getArrayCount() == fCount * sizeof(fModes[0]));
+    bool hasModes = buffer.readBool();
+    if (hasModes) {
+        this->initAllocModes();
+        SkASSERT(buffer.getArrayCount() == numInputs() * sizeof(fModes[0]));
         buffer.readByteArray(fModes);
     } else {
-        SkASSERT(storedCount >= 0);
+        fModes = 0;
     }
 }