blob: aeb5d798ed0066ed45cf80a022edef3d6c6fd110 [file] [log] [blame]
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001
reed@google.com32d25b62011-12-20 16:19:00 +00002#include "SkTestImageFilters.h"
3#include "SkCanvas.h"
reed@google.com76dd2772012-01-05 21:15:07 +00004#include "SkDevice.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00005#include "SkFlattenableBuffers.h"
reed@google.com32d25b62011-12-20 16:19:00 +00006
reed@google.come7639512012-08-07 14:25:44 +00007// Simple helper canvas that "takes ownership" of the provided device, so that
8// when this canvas goes out of scope, so will its device. Could be replaced
9// with the following:
10//
11// SkCanvas canvas(device);
12// SkAutoTUnref<SkDevice> aur(device);
13//
reed@google.com76dd2772012-01-05 21:15:07 +000014class OwnDeviceCanvas : public SkCanvas {
15public:
16 OwnDeviceCanvas(SkDevice* device) : SkCanvas(device) {
17 SkSafeUnref(device);
18 }
19};
20
21bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
reed@google.com32d25b62011-12-20 16:19:00 +000022 const SkMatrix& matrix,
23 SkBitmap* result,
24 SkIPoint* loc) {
25 SkVector vec;
26 matrix.mapVectors(&vec, &fOffset, 1);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000027
reed@google.com32d25b62011-12-20 16:19:00 +000028 loc->fX += SkScalarRoundToInt(vec.fX);
29 loc->fY += SkScalarRoundToInt(vec.fY);
30 *result = src;
31 return true;
32}
33
34bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
35 SkIRect* dst) {
36 SkVector vec;
37 ctm.mapVectors(&vec, &fOffset, 1);
38
39 *dst = src;
40 dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
41 return true;
42}
43
djsollen@google.com54924242012-03-29 15:18:04 +000044void SkOffsetImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@google.com32d25b62011-12-20 16:19:00 +000045 this->INHERITED::flatten(buffer);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000046 buffer.writePoint(fOffset);
reed@google.com32d25b62011-12-20 16:19:00 +000047}
48
49SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000050 buffer.readPoint(&fOffset);
reed@google.com32d25b62011-12-20 16:19:00 +000051}
52
reed@google.com32d25b62011-12-20 16:19:00 +000053///////////////////////////////////////////////////////////////////////////////
54
55SkComposeImageFilter::~SkComposeImageFilter() {
56 SkSafeUnref(fInner);
57 SkSafeUnref(fOuter);
58}
59
reed@google.com76dd2772012-01-05 21:15:07 +000060bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
61 const SkBitmap& src,
reed@google.com32d25b62011-12-20 16:19:00 +000062 const SkMatrix& ctm,
63 SkBitmap* result,
64 SkIPoint* loc) {
65 if (!fOuter && !fInner) {
66 return false;
67 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000068
reed@google.com32d25b62011-12-20 16:19:00 +000069 if (!fOuter || !fInner) {
reed@google.com76dd2772012-01-05 21:15:07 +000070 return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
reed@google.com32d25b62011-12-20 16:19:00 +000071 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000072
reed@google.com32d25b62011-12-20 16:19:00 +000073 SkBitmap tmp;
reed@google.com76dd2772012-01-05 21:15:07 +000074 return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
75 fOuter->filterImage(proxy, tmp, ctm, result, loc);
reed@google.com32d25b62011-12-20 16:19:00 +000076}
77
78bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
79 const SkMatrix& ctm,
80 SkIRect* dst) {
81 if (!fOuter && !fInner) {
82 return false;
83 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000084
reed@google.com32d25b62011-12-20 16:19:00 +000085 if (!fOuter || !fInner) {
86 return (fOuter ? fOuter : fInner)->filterBounds(src, ctm, dst);
87 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000088
reed@google.com32d25b62011-12-20 16:19:00 +000089 SkIRect tmp;
90 return fInner->filterBounds(src, ctm, &tmp) &&
91 fOuter->filterBounds(tmp, ctm, dst);
92}
93
djsollen@google.com54924242012-03-29 15:18:04 +000094void SkComposeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@google.com32d25b62011-12-20 16:19:00 +000095 this->INHERITED::flatten(buffer);
96
97 buffer.writeFlattenable(fOuter);
98 buffer.writeFlattenable(fInner);
99}
100
101SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000102 fOuter = buffer.readFlattenableT<SkImageFilter>();
103 fInner = buffer.readFlattenableT<SkImageFilter>();
reed@google.com32d25b62011-12-20 16:19:00 +0000104}
105
reed@google.com32d25b62011-12-20 16:19:00 +0000106///////////////////////////////////////////////////////////////////////////////
107
108template <typename T> T* SkSafeRefReturn(T* obj) {
109 SkSafeRef(obj);
110 return obj;
111}
112
113void SkMergeImageFilter::initAlloc(int count, bool hasModes) {
114 if (count < 1) {
115 fFilters = NULL;
116 fModes = NULL;
117 fCount = 0;
118 } else {
119 int modeCount = hasModes ? count : 0;
120 size_t size = sizeof(SkImageFilter*) * count + sizeof(uint8_t) * modeCount;
121 if (size <= sizeof(fStorage)) {
122 fFilters = SkTCast<SkImageFilter**>(fStorage);
123 } else {
124 fFilters = SkTCast<SkImageFilter**>(sk_malloc_throw(size));
125 }
126 fModes = hasModes ? SkTCast<uint8_t*>(fFilters + count) : NULL;
127 fCount = count;
128 }
129}
130
131void SkMergeImageFilter::init(SkImageFilter* const filters[], int count,
132 const SkXfermode::Mode modes[]) {
133 this->initAlloc(count, !!modes);
134 for (int i = 0; i < count; ++i) {
135 fFilters[i] = SkSafeRefReturn(filters[i]);
136 if (modes) {
137 fModes[i] = SkToU8(modes[i]);
138 }
139 }
140}
141
142SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
143 SkXfermode::Mode mode) {
144 SkImageFilter* filters[] = { first, second };
145 SkXfermode::Mode modes[] = { mode, mode };
146 this->init(filters, 2, SkXfermode::kSrcOver_Mode == mode ? NULL : modes);
147}
148
149SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const filters[], int count,
150 const SkXfermode::Mode modes[]) {
151 this->init(filters, count, modes);
152}
153
154SkMergeImageFilter::~SkMergeImageFilter() {
155 for (int i = 0; i < fCount; ++i) {
156 SkSafeUnref(fFilters[i]);
157 }
158
159 if (fFilters != SkTCast<SkImageFilter**>(fStorage)) {
160 sk_free(fFilters);
161 // fModes is allocated in the same block as fFilters, so no need to
162 // separately free it.
163 }
164}
165
166bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
167 SkIRect* dst) {
168 if (fCount < 1) {
169 return false;
170 }
171
172 SkIRect totalBounds;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000173
reed@google.com32d25b62011-12-20 16:19:00 +0000174 for (int i = 0; i < fCount; ++i) {
175 SkImageFilter* filter = fFilters[i];
176 SkIRect r;
177 if (filter) {
178 if (!filter->filterBounds(src, ctm, &r)) {
179 return false;
180 }
181 } else {
182 r = src;
183 }
184 if (0 == i) {
185 totalBounds = r;
186 } else {
187 totalBounds.join(r);
188 }
189 }
190
191 // don't modify dst until now, so we don't accidentally change it in the
192 // loop, but then return false on the next filter.
193 *dst = totalBounds;
194 return true;
195}
196
reed@google.com76dd2772012-01-05 21:15:07 +0000197bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
198 const SkMatrix& ctm,
reed@google.com32d25b62011-12-20 16:19:00 +0000199 SkBitmap* result, SkIPoint* loc) {
200 if (fCount < 1) {
201 return false;
202 }
203
204 const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
205 src.width(), src.height());
206 SkIRect bounds;
207 if (!this->filterBounds(srcBounds, ctm, &bounds)) {
208 return false;
209 }
210
211 const int x0 = bounds.left();
212 const int y0 = bounds.top();
213
reed@google.com76dd2772012-01-05 21:15:07 +0000214 SkDevice* dst = proxy->createDevice(bounds.width(), bounds.height());
215 if (NULL == dst) {
216 return false;
217 }
218 OwnDeviceCanvas canvas(dst);
reed@google.com32d25b62011-12-20 16:19:00 +0000219 SkPaint paint;
220
221 for (int i = 0; i < fCount; ++i) {
222 SkBitmap tmp;
223 const SkBitmap* srcPtr;
224 SkIPoint pos = *loc;
225 SkImageFilter* filter = fFilters[i];
226 if (filter) {
reed@google.com76dd2772012-01-05 21:15:07 +0000227 if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
reed@google.com32d25b62011-12-20 16:19:00 +0000228 return false;
229 }
230 srcPtr = &tmp;
231 } else {
232 srcPtr = &src;
233 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000234
reed@google.com32d25b62011-12-20 16:19:00 +0000235 if (fModes) {
236 paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
237 } else {
238 paint.setXfermode(NULL);
239 }
240 canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
241 }
242
243 loc->set(bounds.left(), bounds.top());
reed@google.com76dd2772012-01-05 21:15:07 +0000244 *result = dst->accessBitmap(false);
reed@google.com32d25b62011-12-20 16:19:00 +0000245 return true;
246}
247
djsollen@google.com54924242012-03-29 15:18:04 +0000248void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@google.com32d25b62011-12-20 16:19:00 +0000249 this->INHERITED::flatten(buffer);
250
251 int storedCount = fCount;
252 if (fModes) {
253 // negative count signals we have modes
254 storedCount = -storedCount;
255 }
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000256 buffer.writeInt(storedCount);
reed@google.com32d25b62011-12-20 16:19:00 +0000257
258 if (fCount) {
259 for (int i = 0; i < fCount; ++i) {
260 buffer.writeFlattenable(fFilters[i]);
261 }
262 if (fModes) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000263 buffer.writeByteArray(fModes, fCount * sizeof(fModes[0]));
reed@google.com32d25b62011-12-20 16:19:00 +0000264 }
265 }
266}
267
268SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000269 int storedCount = buffer.readInt();
reed@google.com32d25b62011-12-20 16:19:00 +0000270 this->initAlloc(SkAbs32(storedCount), storedCount < 0);
271
272 for (int i = 0; i < fCount; ++i) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000273 fFilters[i] = buffer.readFlattenableT<SkImageFilter>();
reed@google.com32d25b62011-12-20 16:19:00 +0000274 }
275
276 if (fModes) {
277 SkASSERT(storedCount < 0);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000278 SkASSERT(buffer.getArrayCount() == fCount * sizeof(fModes[0]));
279 buffer.readByteArray(fModes);
reed@google.com32d25b62011-12-20 16:19:00 +0000280 } else {
281 SkASSERT(storedCount >= 0);
282 }
283}
284
reed@google.com32d25b62011-12-20 16:19:00 +0000285///////////////////////////////////////////////////////////////////////////////
286
reed@google.com76dd2772012-01-05 21:15:07 +0000287bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
reed@google.com32d25b62011-12-20 16:19:00 +0000288 const SkMatrix& matrix,
289 SkBitmap* result, SkIPoint*) {
290 SkScalar scale = fScale;
291 if (scale > SK_Scalar1 || scale <= 0) {
292 return false;
293 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000294
reed@google.com32d25b62011-12-20 16:19:00 +0000295 int dstW = SkScalarRoundToInt(src.width() * scale);
296 int dstH = SkScalarRoundToInt(src.height() * scale);
297 if (dstW < 1) {
298 dstW = 1;
299 }
300 if (dstH < 1) {
301 dstH = 1;
302 }
reed@google.com76dd2772012-01-05 21:15:07 +0000303
304 SkBitmap tmp;
305
reed@google.com32d25b62011-12-20 16:19:00 +0000306 // downsample
307 {
reed@google.com76dd2772012-01-05 21:15:07 +0000308 SkDevice* dev = proxy->createDevice(dstW, dstH);
309 if (NULL == dev) {
310 return false;
311 }
312 OwnDeviceCanvas canvas(dev);
reed@google.com32d25b62011-12-20 16:19:00 +0000313 SkPaint paint;
reed@google.com76dd2772012-01-05 21:15:07 +0000314
reed@google.com32d25b62011-12-20 16:19:00 +0000315 paint.setFilterBitmap(true);
reed@google.com32d25b62011-12-20 16:19:00 +0000316 canvas.scale(scale, scale);
317 canvas.drawBitmap(src, 0, 0, &paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000318 tmp = dev->accessBitmap(false);
reed@google.com32d25b62011-12-20 16:19:00 +0000319 }
reed@google.com76dd2772012-01-05 21:15:07 +0000320
reed@google.com32d25b62011-12-20 16:19:00 +0000321 // upscale
322 {
reed@google.com76dd2772012-01-05 21:15:07 +0000323 SkDevice* dev = proxy->createDevice(src.width(), src.height());
324 if (NULL == dev) {
325 return false;
326 }
327 OwnDeviceCanvas canvas(dev);
328
329 SkRect r = SkRect::MakeWH(SkIntToScalar(src.width()),
330 SkIntToScalar(src.height()));
331 canvas.drawBitmapRect(tmp, NULL, r, NULL);
332 *result = dev->accessBitmap(false);
reed@google.com32d25b62011-12-20 16:19:00 +0000333 }
334 return true;
335}
336
djsollen@google.com54924242012-03-29 15:18:04 +0000337void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@google.com32d25b62011-12-20 16:19:00 +0000338 this->INHERITED::flatten(buffer);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339
reed@google.com32d25b62011-12-20 16:19:00 +0000340 buffer.writeScalar(fScale);
341}
342
343SkDownSampleImageFilter::SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
344 fScale = buffer.readScalar();
345}
346
djsollen@google.coma2ca41e2012-03-23 19:00:34 +0000347SK_DEFINE_FLATTENABLE_REGISTRAR(SkOffsetImageFilter)
348SK_DEFINE_FLATTENABLE_REGISTRAR(SkComposeImageFilter)
349SK_DEFINE_FLATTENABLE_REGISTRAR(SkMergeImageFilter)
djsollen@google.coma2ca41e2012-03-23 19:00:34 +0000350SK_DEFINE_FLATTENABLE_REGISTRAR(SkDownSampleImageFilter)