1/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkMergeImageFilter.h"
9#include "SkCanvas.h"
10#include "SkDevice.h"
11#include "SkFlattenableBuffers.h"
12#include "SkValidationUtils.h"
13
14///////////////////////////////////////////////////////////////////////////////
15
16void SkMergeImageFilter::initAllocModes() {
17    int inputCount = countInputs();
18    if (inputCount) {
19        size_t size = sizeof(uint8_t) * inputCount;
20        if (size <= sizeof(fStorage)) {
21            fModes = SkTCast<uint8_t*>(fStorage);
22        } else {
23            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
24        }
25    } else {
26        fModes = NULL;
27    }
28}
29
30void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
31    if (modes) {
32        this->initAllocModes();
33        int inputCount = countInputs();
34        for (int i = 0; i < inputCount; ++i) {
35            fModes[i] = SkToU8(modes[i]);
36        }
37    } else {
38        fModes = NULL;
39    }
40}
41
42SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
43                                       SkXfermode::Mode mode,
44                                       const CropRect* cropRect) : INHERITED(first, second, cropRect) {
45    if (SkXfermode::kSrcOver_Mode != mode) {
46        SkXfermode::Mode modes[] = { mode, mode };
47        this->initModes(modes);
48    } else {
49        fModes = NULL;
50    }
51}
52
53SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
54                                       const SkXfermode::Mode modes[],
55                                       const CropRect* cropRect) : INHERITED(count, filters, cropRect) {
56    SkASSERT(count >= 0);
57    this->initModes(modes);
58}
59
60SkMergeImageFilter::~SkMergeImageFilter() {
61
62    if (fModes != SkTCast<uint8_t*>(fStorage)) {
63        sk_free(fModes);
64    }
65}
66
67bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
68                                        SkIRect* dst) {
69    if (countInputs() < 1) {
70        return false;
71    }
72
73    SkIRect totalBounds;
74
75    int inputCount = countInputs();
76    for (int i = 0; i < inputCount; ++i) {
77        SkImageFilter* filter = getInput(i);
78        SkIRect r;
79        if (filter) {
80            if (!filter->filterBounds(src, ctm, &r)) {
81                return false;
82            }
83        } else {
84            r = src;
85        }
86        if (0 == i) {
87            totalBounds = r;
88        } else {
89            totalBounds.join(r);
90        }
91    }
92
93    // don't modify dst until now, so we don't accidentally change it in the
94    // loop, but then return false on the next filter.
95    *dst = totalBounds;
96    return true;
97}
98
99bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
100                                       const SkMatrix& ctm,
101                                       SkBitmap* result, SkIPoint* loc) {
102    if (countInputs() < 1) {
103        return false;
104    }
105
106    SkIRect bounds;
107    src.getBounds(&bounds);
108    if (!this->applyCropRect(&bounds, ctm)) {
109        return false;
110    }
111
112    const int x0 = bounds.left();
113    const int y0 = bounds.top();
114
115    SkAutoTUnref<SkBaseDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
116    if (NULL == dst) {
117        return false;
118    }
119    SkCanvas canvas(dst);
120    SkPaint paint;
121
122    int inputCount = countInputs();
123    for (int i = 0; i < inputCount; ++i) {
124        SkBitmap tmp;
125        const SkBitmap* srcPtr;
126        SkIPoint pos = SkIPoint::Make(0, 0);
127        SkImageFilter* filter = getInput(i);
128        if (filter) {
129            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
130                return false;
131            }
132            srcPtr = &tmp;
133        } else {
134            srcPtr = &src;
135        }
136
137        if (fModes) {
138            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
139        } else {
140            paint.setXfermode(NULL);
141        }
142        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
143    }
144
145    loc->fX += bounds.left();
146    loc->fY += bounds.top();
147    *result = dst->accessBitmap(false);
148    return true;
149}
150
151void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
152    this->INHERITED::flatten(buffer);
153
154    buffer.writeBool(fModes != NULL);
155    if (fModes) {
156        buffer.writeByteArray(fModes, countInputs() * sizeof(fModes[0]));
157    }
158}
159
160SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer)
161  : INHERITED(-1, buffer) {
162    bool hasModes = buffer.readBool();
163    if (hasModes) {
164        this->initAllocModes();
165        int nbInputs = countInputs();
166        size_t size = nbInputs * sizeof(fModes[0]);
167        SkASSERT(buffer.getArrayCount() == size);
168        if (buffer.readByteArray(fModes, size)) {
169            for (int i = 0; i < nbInputs; ++i) {
170                buffer.validate(SkIsValidMode((SkXfermode::Mode)fModes[i]));
171            }
172        }
173    } else {
174        fModes = 0;
175    }
176}
177