SkMergeImageFilter.cpp revision c245574ba3d0e2ade6c94b2812de3baa383bf4c4
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
10#include "SkCanvas.h"
11#include "SkReadBuffer.h"
12#include "SkSpecialImage.h"
13#include "SkSpecialSurface.h"
14#include "SkWriteBuffer.h"
15#include "SkValidationUtils.h"
16
17sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter> first,
18                                              sk_sp<SkImageFilter> second,
19                                              SkXfermode::Mode mode,
20                                              const CropRect* cropRect) {
21    sk_sp<SkImageFilter> inputs[2] = { first, second };
22    SkXfermode::Mode modes[2] = { mode, mode };
23    return sk_sp<SkImageFilter>(new SkMergeImageFilter(inputs, 2, modes, cropRect));
24}
25
26sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter> filters[],
27                                              int count,
28                                              const SkXfermode::Mode modes[],
29                                              const CropRect* cropRect) {
30    return sk_sp<SkImageFilter>(new SkMergeImageFilter(filters, count, modes, cropRect));
31}
32
33///////////////////////////////////////////////////////////////////////////////
34
35void SkMergeImageFilter::initAllocModes() {
36    int inputCount = this->countInputs();
37    if (inputCount) {
38        size_t size = sizeof(uint8_t) * inputCount;
39        if (size <= sizeof(fStorage)) {
40            fModes = SkTCast<uint8_t*>(fStorage);
41        } else {
42            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
43        }
44    } else {
45        fModes = nullptr;
46    }
47}
48
49void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
50    if (modes) {
51        this->initAllocModes();
52        int inputCount = this->countInputs();
53        for (int i = 0; i < inputCount; ++i) {
54            fModes[i] = SkToU8(modes[i]);
55        }
56    } else {
57        fModes = nullptr;
58    }
59}
60
61SkMergeImageFilter::SkMergeImageFilter(sk_sp<SkImageFilter> filters[], int count,
62                                       const SkXfermode::Mode modes[],
63                                       const CropRect* cropRect)
64    : INHERITED(filters, count, cropRect) {
65    SkASSERT(count >= 0);
66    this->initModes(modes);
67}
68
69SkMergeImageFilter::~SkMergeImageFilter() {
70
71    if (fModes != SkTCast<uint8_t*>(fStorage)) {
72        sk_free(fModes);
73    }
74}
75
76sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx,
77                                                        SkIPoint* offset) const {
78    int inputCount = this->countInputs();
79    if (inputCount < 1) {
80        return nullptr;
81    }
82
83    SkIRect bounds;
84    bounds.setEmpty();
85
86    SkAutoTDeleteArray<sk_sp<SkSpecialImage>> inputs(new sk_sp<SkSpecialImage>[inputCount]);
87    SkAutoTDeleteArray<SkIPoint> offsets(new SkIPoint[inputCount]);
88
89    // Filter all of the inputs.
90    for (int i = 0; i < inputCount; ++i) {
91        offsets[i].setZero();
92        inputs[i] = this->filterInput(i, source, ctx, &offsets[i]);
93        if (!inputs[i]) {
94            continue;
95        }
96        const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY,
97                                                      inputs[i]->width(), inputs[i]->height());
98        bounds.join(inputBounds);
99    }
100    if (bounds.isEmpty()) {
101        return nullptr;
102    }
103
104    // Apply the crop rect to the union of the inputs' bounds.
105    // Note that the crop rect can only reduce the bounds, since this
106    // filter does not affect transparent black.
107    bool embiggen = false;
108    this->getCropRect().applyTo(bounds, ctx.ctm(), embiggen, &bounds);
109    if (!bounds.intersect(ctx.clipBounds())) {
110        return nullptr;
111    }
112
113    const int x0 = bounds.left();
114    const int y0 = bounds.top();
115
116    sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
117    if (!surf) {
118        return nullptr;
119    }
120
121    SkCanvas* canvas = surf->getCanvas();
122    SkASSERT(canvas);
123
124    canvas->clear(0x0);
125
126    // Composite all of the filter inputs.
127    for (int i = 0; i < inputCount; ++i) {
128        if (!inputs[i]) {
129            continue;
130        }
131
132        SkPaint paint;
133        if (fModes) {
134            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
135        }
136
137        inputs[i]->draw(canvas,
138                        SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0),
139                        &paint);
140    }
141
142    offset->fX = bounds.left();
143    offset->fY = bounds.top();
144    return surf->makeImageSnapshot();
145}
146
147sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
148    Common common;
149    if (!common.unflatten(buffer, -1)) {
150        return nullptr;
151    }
152
153    const int count = common.inputCount();
154    bool hasModes = buffer.readBool();
155    if (hasModes) {
156        SkAutoSTArray<4, SkXfermode::Mode> modes(count);
157        SkAutoSTArray<4, uint8_t> modes8(count);
158        if (!buffer.readByteArray(modes8.get(), count)) {
159            return nullptr;
160        }
161        for (int i = 0; i < count; ++i) {
162            modes[i] = (SkXfermode::Mode)modes8[i];
163            buffer.validate(SkIsValidMode(modes[i]));
164        }
165        if (!buffer.isValid()) {
166            return nullptr;
167        }
168        return Make(common.inputs(), count, modes.get(), &common.cropRect());
169    }
170    return Make(common.inputs(), count, nullptr, &common.cropRect());
171}
172
173void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
174    this->INHERITED::flatten(buffer);
175    buffer.writeBool(fModes != nullptr);
176    if (fModes) {
177        buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0]));
178    }
179}
180
181#ifndef SK_IGNORE_TO_STRING
182void SkMergeImageFilter::toString(SkString* str) const {
183    str->appendf("SkMergeImageFilter: (");
184
185    for (int i = 0; i < this->countInputs(); ++i) {
186        SkImageFilter* filter = this->getInput(i);
187        str->appendf("%d: (", i);
188        filter->toString(str);
189        str->appendf(")");
190    }
191
192    str->append(")");
193}
194#endif
195