Implements a new class, SkSingleInputImageFilter, to handle DAG connectivity
for filters with a single image input.  This provides functionality to store,
flatten and unflatten a single SkImageFilter input, as well as to recursively
evaluate it on the CPU or GPU.  The following classes were re-parented to 
implement DAG connectivity:  SkBlurImageFilter, SkDilateImageFilter,
SkErodeImageFilter, SkColorFilterImageFilter.  The constructors for each
have been appended with a new parameter, representing the input filter
(default NULL).

This change also implements an arbitrary SkBitmap input source for filtering,
SkBitmapSource.

NOTE:  This CL will require gyp file changes when rolling past this revision.

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



git-svn-id: http://skia.googlecode.com/svn/trunk@5170 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 2cfb67b..0f1aecf 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -2309,14 +2309,13 @@
 bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
                                 const SkMatrix& ctm,
                                 SkBitmap* result, SkIPoint* loc) {
-    SkASSERT(proxy);
     SkASSERT(result);
     SkASSERT(loc);
     /*
      *  Give the proxy first shot at the filter. If it returns false, ask
      *  the filter to do it.
      */
-    return proxy->filterImage(this, src, ctm, result, loc) ||
+    return (proxy && proxy->filterImage(this, src, ctm, result, loc)) ||
            this->onFilterImage(proxy, src, ctm, result, loc);
 }
 
diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp
new file mode 100644
index 0000000..3092b93
--- /dev/null
+++ b/src/effects/SkBitmapSource.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapSource.h"
+
+SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap) : fBitmap(bitmap) {
+}
+
+SkBitmapSource::SkBitmapSource(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fBitmap.unflatten(buffer);
+}
+
+void SkBitmapSource::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    fBitmap.flatten(buffer);
+}
+
+bool SkBitmapSource::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
+                                   SkBitmap* result, SkIPoint* offset) {
+    *result = fBitmap;
+    return true;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkBitmapSource)
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index b01795b..c822626 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -19,8 +19,8 @@
     fSigma.fHeight = buffer.readScalar();
 }
 
-SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY)
-    : fSigma(SkSize::Make(sigmaX, sigmaY)) {
+SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input)
+    : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) {
     SkASSERT(sigmaX >= 0 && sigmaY >= 0);
 }
 
@@ -133,9 +133,10 @@
     }
 }
 
-bool SkBlurImageFilter::onFilterImage(Proxy*,
-                                      const SkBitmap& src, const SkMatrix&,
-                                      SkBitmap* dst, SkIPoint*) {
+bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
+                                      const SkBitmap& source, const SkMatrix& ctm,
+                                      SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
@@ -188,7 +189,8 @@
 
 GrTexture* SkBlurImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
 #if SK_SUPPORT_GPU
-    return src->getContext()->gaussianBlur(src, false, rect,
+    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(src, rect));
+    return src->getContext()->gaussianBlur(input.get(), false, rect,
                                            fSigma.width(), fSigma.height());
 #else
     SkDEBUGFAIL("Should not call in GPU-less build");
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index c1e2d26..34b3c09 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -21,8 +21,8 @@
     fRadius.fHeight = buffer.readInt();
 }
 
-SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY)
-    : fRadius(SkISize::Make(radiusX, radiusY)) {
+SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input)
+    : INHERITED(input), fRadius(SkISize::Make(radiusX, radiusY)) {
 }
 
 
@@ -128,9 +128,10 @@
            src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
 }
 
-bool SkErodeImageFilter::onFilterImage(Proxy*,
-                                       const SkBitmap& src, const SkMatrix&,
-                                       SkBitmap* dst, SkIPoint*) {
+bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
+                                       const SkBitmap& source, const SkMatrix& ctm,
+                                       SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
@@ -172,9 +173,10 @@
     return true;
 }
 
-bool SkDilateImageFilter::onFilterImage(Proxy*,
-                                        const SkBitmap& src, const SkMatrix&,
-                                        SkBitmap* dst, SkIPoint*) {
+bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
+                                        const SkBitmap& source, const SkMatrix& ctm,
+                                        SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
@@ -218,7 +220,8 @@
 
 GrTexture* SkDilateImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
 #if SK_SUPPORT_GPU
-    return src->getContext()->applyMorphology(src, rect,
+    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(src, rect));
+    return src->getContext()->applyMorphology(input.get(), rect,
                                               GrContext::kDilate_MorphologyType,
                                               radius());
 #else
@@ -229,7 +232,8 @@
 
 GrTexture* SkErodeImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
 #if SK_SUPPORT_GPU
-    return src->getContext()->applyMorphology(src, rect,
+    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(src, rect));
+    return src->getContext()->applyMorphology(input.get(), rect,
                                               GrContext::kErode_MorphologyType,
                                               radius());
 #else
diff --git a/src/effects/SkSingleInputImageFilter.cpp b/src/effects/SkSingleInputImageFilter.cpp
new file mode 100644
index 0000000..291d81c
--- /dev/null
+++ b/src/effects/SkSingleInputImageFilter.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSingleInputImageFilter.h"
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMatrix.h"
+#if SK_SUPPORT_GPU
+#include "GrTexture.h"
+#include "SkGr.h"
+#include "SkGrPixelRef.h"
+#endif
+
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkImageFilter* input) : fInput(input) {
+    SkSafeRef(fInput);
+}
+
+SkSingleInputImageFilter::~SkSingleInputImageFilter() {
+    SkSafeUnref(fInput);
+}
+
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkFlattenableReadBuffer& rb) {
+    if (rb.readBool()) {
+        fInput = rb.readFlattenableT<SkImageFilter>();
+    } else {
+        fInput = NULL;
+    }
+}
+
+void SkSingleInputImageFilter::flatten(SkFlattenableWriteBuffer& wb) const {
+    wb.writeBool(NULL != fInput);
+    if (NULL != fInput) {
+        wb.writeFlattenable(fInput);
+    }
+}
+
+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;
+    }
+}
+
+#if SK_SUPPORT_GPU
+GrTexture* SkSingleInputImageFilter::getInputResultAsTexture(GrTexture* src,
+                                                             const SkRect& rect) {
+    GrTexture* resultTex;
+    if (!fInput) {
+        resultTex = src;
+    } else if (fInput->canFilterImageGPU()) {
+        // onFilterImageGPU() already refs the result, so just return it here.
+        return fInput->onFilterImageGPU(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(NULL, srcBitmap, SkMatrix(), &result, &offset)) {
+            if (result.getTexture()) {
+                resultTex = (GrTexture*) result.getTexture();
+            } else {
+                resultTex = GrLockCachedBitmapTexture(src->getContext(), result, NULL);
+                SkSafeRef(resultTex);
+                GrUnlockCachedBitmapTexture(resultTex);
+                return resultTex;
+            }
+        } else {
+            resultTex = src;
+        }
+    }
+    SkSafeRef(resultTex);
+    return resultTex;
+}
+#endif
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkSingleInputImageFilter)
diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
index 70e39ef..0fbc293 100755
--- a/src/effects/SkTestImageFilters.cpp
+++ b/src/effects/SkTestImageFilters.cpp
@@ -290,10 +290,11 @@
     SkSafeUnref(fColorFilter);
 }
 
-bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
+bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
                                              const SkMatrix& matrix,
                                              SkBitmap* result,
                                              SkIPoint* loc) {
+    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
     SkColorFilter* cf = fColorFilter;
     if (NULL == cf) {
         *result = src;