SkMergeImageFilter.cpp revision 478884f7d3b8c7be8b62f3fa2b79192f411c3fec
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
13///////////////////////////////////////////////////////////////////////////////
14
15void SkMergeImageFilter::initAllocModes() {
16    int inputCount = countInputs();
17    if (inputCount) {
18        size_t size = sizeof(uint8_t) * inputCount;
19        if (size <= sizeof(fStorage)) {
20            fModes = SkTCast<uint8_t*>(fStorage);
21        } else {
22            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
23        }
24    } else {
25        fModes = NULL;
26    }
27}
28
29void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
30    if (modes) {
31        this->initAllocModes();
32        int inputCount = countInputs();
33        for (int i = 0; i < inputCount; ++i) {
34            fModes[i] = SkToU8(modes[i]);
35        }
36    } else {
37        fModes = NULL;
38    }
39}
40
41SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
42                                       SkXfermode::Mode mode) : INHERITED(first, second) {
43    if (SkXfermode::kSrcOver_Mode != mode) {
44        SkXfermode::Mode modes[] = { mode, mode };
45        this->initModes(modes);
46    } else {
47        fModes = NULL;
48    }
49}
50
51SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
52                                       const SkXfermode::Mode modes[]) : INHERITED(count, filters) {
53    this->initModes(modes);
54}
55
56SkMergeImageFilter::~SkMergeImageFilter() {
57
58    if (fModes != SkTCast<uint8_t*>(fStorage)) {
59        sk_free(fModes);
60    }
61}
62
63bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
64                                        SkIRect* dst) {
65    if (countInputs() < 1) {
66        return false;
67    }
68
69    SkIRect totalBounds;
70
71    int inputCount = countInputs();
72    for (int i = 0; i < inputCount; ++i) {
73        SkImageFilter* filter = getInput(i);
74        SkIRect r;
75        if (filter) {
76            if (!filter->filterBounds(src, ctm, &r)) {
77                return false;
78            }
79        } else {
80            r = src;
81        }
82        if (0 == i) {
83            totalBounds = r;
84        } else {
85            totalBounds.join(r);
86        }
87    }
88
89    // don't modify dst until now, so we don't accidentally change it in the
90    // loop, but then return false on the next filter.
91    *dst = totalBounds;
92    return true;
93}
94
95bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
96                                       const SkMatrix& ctm,
97                                       SkBitmap* result, SkIPoint* loc) {
98    if (countInputs() < 1) {
99        return false;
100    }
101
102    const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
103                                                src.width(), src.height());
104    SkIRect bounds;
105    if (!this->filterBounds(srcBounds, ctm, &bounds)) {
106        return false;
107    }
108
109    const int x0 = bounds.left();
110    const int y0 = bounds.top();
111
112    SkAutoTUnref<SkBaseDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
113    if (NULL == dst) {
114        return false;
115    }
116    SkCanvas canvas(dst);
117    SkPaint paint;
118
119    int inputCount = countInputs();
120    for (int i = 0; i < inputCount; ++i) {
121        SkBitmap tmp;
122        const SkBitmap* srcPtr;
123        SkIPoint pos = *loc;
124        SkImageFilter* filter = getInput(i);
125        if (filter) {
126            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
127                return false;
128            }
129            srcPtr = &tmp;
130        } else {
131            srcPtr = &src;
132        }
133
134        if (fModes) {
135            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
136        } else {
137            paint.setXfermode(NULL);
138        }
139        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
140    }
141
142    loc->set(bounds.left(), bounds.top());
143    *result = dst->accessBitmap(false);
144    return true;
145}
146
147void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
148    this->INHERITED::flatten(buffer);
149
150    buffer.writeBool(fModes != NULL);
151    if (fModes) {
152        buffer.writeByteArray(fModes, countInputs() * sizeof(fModes[0]));
153    }
154}
155
156SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
157    bool hasModes = buffer.readBool();
158    if (hasModes) {
159        this->initAllocModes();
160        SkASSERT(buffer.getArrayCount() == countInputs() * sizeof(fModes[0]));
161        buffer.readByteArray(fModes);
162    } else {
163        fModes = 0;
164    }
165}
166