Pull SkMergeImageFilter out into its own file.

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

git-svn-id: http://skia.googlecode.com/svn/trunk@6662 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp
new file mode 100755
index 0000000..0c1388f
--- /dev/null
+++ b/src/effects/SkMergeImageFilter.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 "SkMergeImageFilter.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkFlattenableBuffers.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMergeImageFilter::initAllocModes() {
+    int inputCount = countInputs();
+    if (inputCount) {
+        size_t size = sizeof(uint8_t) * inputCount;
+        if (size <= sizeof(fStorage)) {
+            fModes = SkTCast<uint8_t*>(fStorage);
+        } else {
+            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
+        }
+    } else {
+        fModes = NULL;
+    }
+}
+
+void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
+    if (modes) {
+        this->initAllocModes();
+        int inputCount = countInputs();
+        for (int i = 0; i < inputCount; ++i) {
+            fModes[i] = SkToU8(modes[i]);
+        }
+    } else {
+        fModes = NULL;
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
+                                       SkXfermode::Mode mode) : INHERITED(first, second) {
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        SkXfermode::Mode modes[] = { mode, mode };
+        this->initModes(modes);
+    } else {
+        fModes = NULL;
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
+                                       const SkXfermode::Mode modes[]) : INHERITED(count, filters) {
+    this->initModes(modes);
+}
+
+SkMergeImageFilter::~SkMergeImageFilter() {
+
+    if (fModes != SkTCast<uint8_t*>(fStorage)) {
+        sk_free(fModes);
+    }
+}
+
+bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                        SkIRect* dst) {
+    if (countInputs() < 1) {
+        return false;
+    }
+
+    SkIRect totalBounds;
+
+    int inputCount = countInputs();
+    for (int i = 0; i < inputCount; ++i) {
+        SkImageFilter* filter = getInput(i);
+        SkIRect r;
+        if (filter) {
+            if (!filter->filterBounds(src, ctm, &r)) {
+                return false;
+            }
+        } else {
+            r = src;
+        }
+        if (0 == i) {
+            totalBounds = r;
+        } else {
+            totalBounds.join(r);
+        }
+    }
+
+    // don't modify dst until now, so we don't accidentally change it in the
+    // loop, but then return false on the next filter.
+    *dst = totalBounds;
+    return true;
+}
+
+bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
+                                       const SkMatrix& ctm,
+                                       SkBitmap* result, SkIPoint* loc) {
+    if (countInputs() < 1) {
+        return false;
+    }
+
+    const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
+                                                src.width(), src.height());
+    SkIRect bounds;
+    if (!this->filterBounds(srcBounds, ctm, &bounds)) {
+        return false;
+    }
+
+    const int x0 = bounds.left();
+    const int y0 = bounds.top();
+
+    SkAutoTUnref<SkDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
+    if (NULL == dst) {
+        return false;
+    }
+    SkCanvas canvas(dst);
+    SkPaint paint;
+
+    int inputCount = countInputs();
+    for (int i = 0; i < inputCount; ++i) {
+        SkBitmap tmp;
+        const SkBitmap* srcPtr;
+        SkIPoint pos = *loc;
+        SkImageFilter* filter = getInput(i);
+        if (filter) {
+            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
+                return false;
+            }
+            srcPtr = &tmp;
+        } else {
+            srcPtr = &src;
+        }
+
+        if (fModes) {
+            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
+        } else {
+            paint.setXfermode(NULL);
+        }
+        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
+    }
+
+    loc->set(bounds.left(), bounds.top());
+    *result = dst->accessBitmap(false);
+    return true;
+}
+
+void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeBool(fModes != NULL);
+    if (fModes) {
+        buffer.writeByteArray(fModes, countInputs() * sizeof(fModes[0]));
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    bool hasModes = buffer.readBool();
+    if (hasModes) {
+        this->initAllocModes();
+        SkASSERT(buffer.getArrayCount() == countInputs() * sizeof(fModes[0]));
+        buffer.readByteArray(fModes);
+    } else {
+        fModes = 0;
+    }
+}