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