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 
initAllocModes()17 void 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 
initModes(const SkXfermode::Mode modes[])31 void 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 
SkMergeImageFilter(SkImageFilter * filters[],int count,const SkXfermode::Mode modes[],const CropRect * cropRect)43 SkMergeImageFilter::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 
~SkMergeImageFilter()51 SkMergeImageFilter::~SkMergeImageFilter() {
52 
53     if (fModes != SkTCast<uint8_t*>(fStorage)) {
54         sk_free(fModes);
55     }
56 }
57 
onFilterImageDeprecated(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const58 bool 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 
CreateProc(SkReadBuffer & buffer)126 SkFlattenable* 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 
flatten(SkWriteBuffer & buffer) const152 void 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
toString(SkString * str) const161 void 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