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 "SkReadBuffer.h"
12#include "SkWriteBuffer.h"
13#include "SkValidationUtils.h"
14
15///////////////////////////////////////////////////////////////////////////////
16
17void SkMergeImageFilter::initAllocModes() {
18    int inputCount = this->countInputs();
19    if (inputCount) {
20        size_t size = sizeof(uint8_t) * inputCount;
21        if (size <= sizeof(fStorage)) {
22            fModes = SkTCast<uint8_t*>(fStorage);
23        } else {
24            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
25        }
26    } else {
27        fModes = nullptr;
28    }
29}
30
31void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
32    if (modes) {
33        this->initAllocModes();
34        int inputCount = this->countInputs();
35        for (int i = 0; i < inputCount; ++i) {
36            fModes[i] = SkToU8(modes[i]);
37        }
38    } else {
39        fModes = nullptr;
40    }
41}
42
43SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
44                                       const SkXfermode::Mode modes[],
45                                       const CropRect* cropRect)
46  : INHERITED(count, filters, cropRect) {
47    SkASSERT(count >= 0);
48    this->initModes(modes);
49}
50
51SkMergeImageFilter::~SkMergeImageFilter() {
52
53    if (fModes != SkTCast<uint8_t*>(fStorage)) {
54        sk_free(fModes);
55    }
56}
57
58bool SkMergeImageFilter::onFilterImageDeprecated(Proxy* proxy, const SkBitmap& src,
59                                                 const Context& ctx,
60                                                 SkBitmap* result, SkIPoint* offset) const {
61    int inputCount = this->countInputs();
62    if (inputCount < 1) {
63        return false;
64    }
65
66    SkIRect bounds;
67
68    SkAutoTDeleteArray<SkBitmap> inputs(new SkBitmap[inputCount]);
69    SkAutoTDeleteArray<SkIPoint> offsets(new SkIPoint[inputCount]);
70    bool didProduceResult = false;
71
72    // Filter all of the inputs.
73    for (int i = 0; i < inputCount; ++i) {
74        inputs[i] = src;
75        offsets[i].setZero();
76        if (!this->filterInputDeprecated(i, proxy, src, ctx, &inputs[i], &offsets[i])) {
77            inputs[i].reset();
78            continue;
79        }
80        SkIRect srcBounds;
81        inputs[i].getBounds(&srcBounds);
82        srcBounds.offset(offsets[i]);
83        if (!didProduceResult) {
84            bounds = srcBounds;
85            didProduceResult = true;
86        } else {
87            bounds.join(srcBounds);
88        }
89    }
90    if (!didProduceResult) {
91        return false;
92    }
93
94    // Apply the crop rect to the union of the inputs' bounds.
95    this->getCropRect().applyTo(bounds, ctx.ctm(), &bounds);
96    if (!bounds.intersect(ctx.clipBounds())) {
97        return false;
98    }
99
100    const int x0 = bounds.left();
101    const int y0 = bounds.top();
102
103    // Allocate the destination buffer.
104    SkAutoTUnref<SkBaseDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
105    if (nullptr == dst) {
106        return false;
107    }
108    SkCanvas canvas(dst);
109
110    // Composite all of the filter inputs.
111    for (int i = 0; i < inputCount; ++i) {
112        SkPaint paint;
113        if (fModes) {
114            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
115        }
116        canvas.drawBitmap(inputs[i], SkIntToScalar(offsets[i].x() - x0),
117                                     SkIntToScalar(offsets[i].y() - y0), &paint);
118    }
119
120    offset->fX = bounds.left();
121    offset->fY = bounds.top();
122    *result = dst->accessBitmap(false);
123    return true;
124}
125
126SkFlattenable* SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
127    Common common;
128    if (!common.unflatten(buffer, -1)) {
129        return nullptr;
130    }
131
132    const int count = common.inputCount();
133    bool hasModes = buffer.readBool();
134    if (hasModes) {
135        SkAutoSTArray<4, SkXfermode::Mode> modes(count);
136        SkAutoSTArray<4, uint8_t> modes8(count);
137        if (!buffer.readByteArray(modes8.get(), count)) {
138            return nullptr;
139        }
140        for (int i = 0; i < count; ++i) {
141            modes[i] = (SkXfermode::Mode)modes8[i];
142            buffer.validate(SkIsValidMode(modes[i]));
143        }
144        if (!buffer.isValid()) {
145            return nullptr;
146        }
147        return Create(common.inputs(), count, modes.get(), &common.cropRect());
148    }
149    return Create(common.inputs(), count, nullptr, &common.cropRect());
150}
151
152void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
153    this->INHERITED::flatten(buffer);
154    buffer.writeBool(fModes != nullptr);
155    if (fModes) {
156        buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0]));
157    }
158}
159
160#ifndef SK_IGNORE_TO_STRING
161void SkMergeImageFilter::toString(SkString* str) const {
162    str->appendf("SkMergeImageFilter: (");
163
164    for (int i = 0; i < this->countInputs(); ++i) {
165        SkImageFilter* filter = this->getInput(i);
166        str->appendf("%d: (", i);
167        filter->toString(str);
168        str->appendf(")");
169    }
170
171    str->append(")");
172}
173#endif
174