blob: b67383682bf77b70ad7fb8598ae42dc3689a68e5 [file] [log] [blame]
senorblanco@chromium.org194d7752013-07-24 22:19:24 +00001/*
2 * Copyright 2013 Google Inc.
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
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +00008#include "SkBitmap.h"
9#include "SkBitmapDevice.h"
10#include "SkBitmapSource.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000011#include "SkBlurImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000012#include "SkCanvas.h"
13#include "SkColorFilterImageFilter.h"
14#include "SkColorMatrixFilter.h"
15#include "SkDeviceImageFilterProxy.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000016#include "SkDisplacementMapEffect.h"
17#include "SkDropShadowImageFilter.h"
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +000018#include "SkFlattenableSerialization.h"
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +000019#include "SkGradientShader.h"
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +000020#include "SkLightingImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000021#include "SkMatrixConvolutionImageFilter.h"
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +000022#include "SkMatrixImageFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000023#include "SkMergeImageFilter.h"
24#include "SkMorphologyImageFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000025#include "SkOffsetImageFilter.h"
senorblanco8f3937d2014-10-29 12:36:32 -070026#include "SkPerlinNoiseShader.h"
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000027#include "SkPicture.h"
senorblanco@chromium.org910702b2014-05-30 20:36:15 +000028#include "SkPictureImageFilter.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000029#include "SkPictureRecorder.h"
halcanary97d2c0a2014-08-19 06:27:53 -070030#include "SkReadBuffer.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000031#include "SkRect.h"
senorblanco8f3937d2014-10-29 12:36:32 -070032#include "SkRectShaderImageFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000033#include "SkTileImageFilter.h"
34#include "SkXfermodeImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000035#include "Test.h"
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000036
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +000037#if SK_SUPPORT_GPU
38#include "GrContextFactory.h"
39#include "SkGpuDevice.h"
40#endif
41
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000042static const int kBitmapSize = 4;
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +000043
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000044namespace {
45
46class MatrixTestImageFilter : public SkImageFilter {
47public:
48 MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
senorblanco9ea3d572014-07-08 09:16:22 -070049 : SkImageFilter(0, NULL), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000050 }
51
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000052 virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
senorblanco@chromium.org09373352014-02-05 23:04:28 +000053 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000054 REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000055 return true;
56 }
57
robertphillipsf3f5bad2014-12-19 13:49:15 -080058 SK_TO_STRING_OVERRIDE()
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000059 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
60
61protected:
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000062 virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
reed9fa60da2014-08-21 07:59:51 -070063 this->INHERITED::flatten(buffer);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000064 buffer.writeFunctionPtr(fReporter);
65 buffer.writeMatrix(fExpectedMatrix);
66 }
67
68private:
69 skiatest::Reporter* fReporter;
70 SkMatrix fExpectedMatrix;
mtklein3f3b3d02014-12-01 11:47:08 -080071
reed9fa60da2014-08-21 07:59:51 -070072 typedef SkImageFilter INHERITED;
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000073};
74
75}
76
reed9fa60da2014-08-21 07:59:51 -070077SkFlattenable* MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
78 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
79 skiatest::Reporter* reporter = (skiatest::Reporter*)buffer.readFunctionPtr();
80 SkMatrix matrix;
81 buffer.readMatrix(&matrix);
82 return SkNEW_ARGS(MatrixTestImageFilter, (reporter, matrix));
83}
84
robertphillipsf3f5bad2014-12-19 13:49:15 -080085#ifndef SK_IGNORE_TO_STRING
86void MatrixTestImageFilter::toString(SkString* str) const {
87 str->appendf("MatrixTestImageFilter: (");
88 str->append(")");
89}
90#endif
91
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000092static void make_small_bitmap(SkBitmap& bitmap) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000093 bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
94 SkCanvas canvas(bitmap);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000095 canvas.clear(0x00000000);
96 SkPaint darkPaint;
97 darkPaint.setColor(0xFF804020);
98 SkPaint lightPaint;
99 lightPaint.setColor(0xFF244484);
100 const int i = kBitmapSize / 4;
101 for (int y = 0; y < kBitmapSize; y += i) {
102 for (int x = 0; x < kBitmapSize; x += i) {
103 canvas.save();
104 canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
105 canvas.drawRect(SkRect::MakeXYWH(0, 0,
106 SkIntToScalar(i),
107 SkIntToScalar(i)), darkPaint);
108 canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
109 0,
110 SkIntToScalar(i),
111 SkIntToScalar(i)), lightPaint);
112 canvas.drawRect(SkRect::MakeXYWH(0,
113 SkIntToScalar(i),
114 SkIntToScalar(i),
115 SkIntToScalar(i)), lightPaint);
116 canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
117 SkIntToScalar(i),
118 SkIntToScalar(i),
119 SkIntToScalar(i)), darkPaint);
120 canvas.restore();
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +0000121 }
122 }
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000123}
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000124
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000125static SkImageFilter* make_scale(float amount, SkImageFilter* input = NULL) {
126 SkScalar s = amount;
127 SkScalar matrix[20] = { s, 0, 0, 0, 0,
128 0, s, 0, 0, 0,
129 0, 0, s, 0, 0,
130 0, 0, 0, s, 0 };
commit-bot@chromium.org727a3522014-02-21 18:46:30 +0000131 SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000132 return SkColorFilterImageFilter::Create(filter, input);
133}
134
135static SkImageFilter* make_grayscale(SkImageFilter* input = NULL, const SkImageFilter::CropRect* cropRect = NULL) {
136 SkScalar matrix[20];
137 memset(matrix, 0, 20 * sizeof(SkScalar));
138 matrix[0] = matrix[5] = matrix[10] = 0.2126f;
139 matrix[1] = matrix[6] = matrix[11] = 0.7152f;
140 matrix[2] = matrix[7] = matrix[12] = 0.0722f;
141 matrix[18] = 1.0f;
commit-bot@chromium.org727a3522014-02-21 18:46:30 +0000142 SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000143 return SkColorFilterImageFilter::Create(filter, input, cropRect);
144}
145
146DEF_TEST(ImageFilter, reporter) {
147 {
148 // Check that two non-clipping color matrices concatenate into a single filter.
149 SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
150 SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
151 REPORTER_ASSERT(reporter, NULL == quarterBrightness->getInput(0));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000152 }
153
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000154 {
155 // Check that a clipping color matrix followed by a grayscale does not concatenate into a single filter.
156 SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
157 SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
bsalomon49f085d2014-09-05 13:34:00 -0700158 REPORTER_ASSERT(reporter, halfBrightness->getInput(0));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000159 }
160
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000161 {
162 // Check that a color filter image filter without a crop rect can be
163 // expressed as a color filter.
164 SkAutoTUnref<SkImageFilter> gray(make_grayscale());
165 REPORTER_ASSERT(reporter, true == gray->asColorFilter(NULL));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000166 }
167
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000168 {
169 // Check that a color filter image filter with a crop rect cannot
170 // be expressed as a color filter.
171 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
172 SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(NULL, &cropRect));
173 REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(NULL));
174 }
175
176 {
senorblanco3df05012014-07-03 11:13:09 -0700177 // Check that two non-commutative matrices are concatenated in
178 // the correct order.
179 SkScalar blueToRedMatrix[20] = { 0 };
180 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
181 SkScalar redToGreenMatrix[20] = { 0 };
182 redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
183 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
184 SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get()));
185 SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix));
186 SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get()));
187
188 SkBitmap result;
189 result.allocN32Pixels(kBitmapSize, kBitmapSize);
190
191 SkPaint paint;
192 paint.setColor(SK_ColorBLUE);
193 paint.setImageFilter(filter2.get());
194 SkCanvas canvas(result);
195 canvas.clear(0x0);
196 SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
197 canvas.drawRect(rect, paint);
198 uint32_t pixel = *result.getAddr32(0, 0);
199 // The result here should be green, since we have effectively shifted blue to green.
200 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
201 }
202
203 {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000204 // Tests pass by not asserting
205 SkBitmap bitmap, result;
206 make_small_bitmap(bitmap);
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000207 result.allocN32Pixels(kBitmapSize, kBitmapSize);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000208
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000209 {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000210 // This tests for :
211 // 1 ) location at (0,0,1)
212 SkPoint3 location(0, 0, SK_Scalar1);
213 // 2 ) location and target at same value
214 SkPoint3 target(location.fX, location.fY, location.fZ);
215 // 3 ) large negative specular exponent value
216 SkScalar specularExponent = -1000;
217
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000218 SkAutoTUnref<SkImageFilter> bmSrc(SkBitmapSource::Create(bitmap));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000219 SkPaint paint;
220 paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(
221 location, target, specularExponent, 180,
222 0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
223 bmSrc))->unref();
224 SkCanvas canvas(result);
225 SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
226 SkIntToScalar(kBitmapSize));
227 canvas.drawRect(r, paint);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000228 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000229 }
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000230}
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000231
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000232static void test_crop_rects(SkBaseDevice* device, skiatest::Reporter* reporter) {
233 // Check that all filters offset to their absolute crop rect,
234 // unaffected by the input crop rect.
235 // Tests pass by not asserting.
236 SkBitmap bitmap;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000237 bitmap.allocN32Pixels(100, 100);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000238 bitmap.eraseARGB(0, 0, 0, 0);
fmalita2d97bc12014-11-20 10:44:58 -0800239 SkDeviceImageFilterProxy proxy(device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000240
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000241 SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
242 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
243 SkAutoTUnref<SkImageFilter> input(make_grayscale(NULL, &inputCropRect));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000244
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000245 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
246 SkPoint3 location(0, 0, SK_Scalar1);
247 SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
248 SkScalar kernel[9] = {
249 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
250 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
251 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
252 };
253 SkISize kernelSize = SkISize::Make(3, 3);
254 SkScalar gain = SK_Scalar1, bias = 0;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000255
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000256 SkImageFilter* filters[] = {
257 SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect),
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000258 SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
259 SkDisplacementMapEffect::kB_ChannelSelectorType,
260 40.0f, input.get(), input.get(), &cropRect),
261 SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
sugoi234f0362014-10-23 13:59:52 -0700262 SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1,
263 SK_ColorGREEN, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
264 input.get(), &cropRect, 0),
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000265 SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
266 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000267 SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
268 SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect),
269 SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
270 SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
271 SkDilateImageFilter::Create(3, 2, input.get(), &cropRect),
272 SkErodeImageFilter::Create(2, 3, input.get(), &cropRect),
273 SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()),
274 SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect),
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000275 };
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000276
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000277 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
278 SkImageFilter* filter = filters[i];
279 SkBitmap result;
280 SkIPoint offset;
281 SkString str;
senorblanco@chromium.orgf4e1a762014-02-04 00:28:46 +0000282 str.printf("filter %d", static_cast<int>(i));
senorblanco55b6d8b2014-07-30 11:26:46 -0700283 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000284 REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, ctx,
285 &result, &offset), str.c_str());
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000286 REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000287 }
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000288
289 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
290 SkSafeUnref(filters[i]);
291 }
292}
293
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000294static SkBitmap make_gradient_circle(int width, int height) {
295 SkBitmap bitmap;
296 SkScalar x = SkIntToScalar(width / 2);
297 SkScalar y = SkIntToScalar(height / 2);
298 SkScalar radius = SkMinScalar(x, y) * 0.8f;
299 bitmap.allocN32Pixels(width, height);
300 SkCanvas canvas(bitmap);
301 canvas.clear(0x00000000);
302 SkColor colors[2];
303 colors[0] = SK_ColorWHITE;
304 colors[1] = SK_ColorBLACK;
305 SkAutoTUnref<SkShader> shader(
306 SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2,
307 SkShader::kClamp_TileMode)
308 );
309 SkPaint paint;
310 paint.setShader(shader);
311 canvas.drawCircle(x, y, radius, paint);
312 return bitmap;
313}
314
senorblanco32673b92014-09-09 09:15:04 -0700315static void test_negative_blur_sigma(SkBaseDevice* device, skiatest::Reporter* reporter) {
316 // Check that SkBlurImageFilter will accept a negative sigma, either in
317 // the given arguments or after CTM application.
318 int width = 32, height = 32;
fmalita2d97bc12014-11-20 10:44:58 -0800319 SkDeviceImageFilterProxy proxy(device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
senorblanco32673b92014-09-09 09:15:04 -0700320 SkScalar five = SkIntToScalar(5);
321
322 SkAutoTUnref<SkBlurImageFilter> positiveFilter(
323 SkBlurImageFilter::Create(five, five)
324 );
325
326 SkAutoTUnref<SkBlurImageFilter> negativeFilter(
327 SkBlurImageFilter::Create(-five, five)
328 );
329
330 SkBitmap gradient = make_gradient_circle(width, height);
331 SkBitmap positiveResult1, negativeResult1;
332 SkBitmap positiveResult2, negativeResult2;
333 SkIPoint offset;
334 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
335 positiveFilter->filterImage(&proxy, gradient, ctx, &positiveResult1, &offset);
336 negativeFilter->filterImage(&proxy, gradient, ctx, &negativeResult1, &offset);
337 SkMatrix negativeScale;
338 negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
339 SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeLargest(), NULL);
340 positiveFilter->filterImage(&proxy, gradient, negativeCTX, &negativeResult2, &offset);
341 negativeFilter->filterImage(&proxy, gradient, negativeCTX, &positiveResult2, &offset);
342 SkAutoLockPixels lockP1(positiveResult1);
343 SkAutoLockPixels lockP2(positiveResult2);
344 SkAutoLockPixels lockN1(negativeResult1);
345 SkAutoLockPixels lockN2(negativeResult2);
346 for (int y = 0; y < height; y++) {
347 int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes());
348 REPORTER_ASSERT(reporter, !diffs);
349 if (diffs) {
350 break;
351 }
352 diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes());
353 REPORTER_ASSERT(reporter, !diffs);
354 if (diffs) {
355 break;
356 }
357 diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes());
358 REPORTER_ASSERT(reporter, !diffs);
359 if (diffs) {
360 break;
361 }
362 }
363}
364
365DEF_TEST(TestNegativeBlurSigma, reporter) {
366 SkBitmap temp;
367 temp.allocN32Pixels(100, 100);
368 SkBitmapDevice device(temp);
369 test_negative_blur_sigma(&device, reporter);
370}
371
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000372DEF_TEST(ImageFilterDrawTiled, reporter) {
373 // Check that all filters when drawn tiled (with subsequent clip rects) exactly
374 // match the same filters drawn with a single full-canvas bitmap draw.
375 // Tests pass by not asserting.
376
377 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
378 SkPoint3 location(0, 0, SK_Scalar1);
379 SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
380 SkScalar kernel[9] = {
381 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
382 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
383 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
384 };
385 SkISize kernelSize = SkISize::Make(3, 3);
386 SkScalar gain = SK_Scalar1, bias = 0;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000387 SkScalar five = SkIntToScalar(5);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000388
389 SkAutoTUnref<SkImageFilter> gradient_source(SkBitmapSource::Create(make_gradient_circle(64, 64)));
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000390 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000391 SkMatrix matrix;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000392
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000393 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
394 matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000395
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000396 SkRTreeFactory factory;
397 SkPictureRecorder recorder;
398 SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
399
400 SkPaint greenPaint;
401 greenPaint.setColor(SK_ColorGREEN);
402 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
403 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
404 SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
senorblanco8f3937d2014-10-29 12:36:32 -0700405 SkAutoTUnref<SkShader> shader(SkPerlinNoiseShader::CreateTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
406
407 SkAutoTUnref<SkImageFilter> rectShaderFilter(SkRectShaderImageFilter::Create(shader.get()));
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000408
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000409 struct {
410 const char* fName;
411 SkImageFilter* fFilter;
412 } filters[] = {
413 { "color filter", SkColorFilterImageFilter::Create(cf.get()) },
414 { "displacement map", SkDisplacementMapEffect::Create(
415 SkDisplacementMapEffect::kR_ChannelSelectorType,
416 SkDisplacementMapEffect::kB_ChannelSelectorType,
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000417 20.0f, gradient_source.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000418 { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
419 { "drop shadow", SkDropShadowImageFilter::Create(
sugoi234f0362014-10-23 13:59:52 -0700420 SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
421 SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000422 { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
423 location, SK_ColorGREEN, 0, 0) },
424 { "specular lighting",
425 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
426 { "matrix convolution",
427 SkMatrixConvolutionImageFilter::Create(
428 kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
429 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
430 { "merge", SkMergeImageFilter::Create(NULL, NULL, SkXfermode::kSrcOver_Mode) },
431 { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
432 { "dilate", SkDilateImageFilter::Create(3, 2) },
433 { "erode", SkErodeImageFilter::Create(2, 3) },
434 { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
435 SkRect::MakeXYWH(0, 0, 100, 100), NULL) },
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000436 { "matrix", SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel) },
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000437 { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000438 { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
senorblanco8f3937d2014-10-29 12:36:32 -0700439 { "rect shader and blur", SkBlurImageFilter::Create(five, five, rectShaderFilter.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000440 };
441
442 SkBitmap untiledResult, tiledResult;
443 int width = 64, height = 64;
444 untiledResult.allocN32Pixels(width, height);
445 tiledResult.allocN32Pixels(width, height);
446 SkCanvas tiledCanvas(tiledResult);
447 SkCanvas untiledCanvas(untiledResult);
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000448 int tileSize = 8;
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000449
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000450 for (int scale = 1; scale <= 2; ++scale) {
451 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
452 tiledCanvas.clear(0);
453 untiledCanvas.clear(0);
454 SkPaint paint;
455 paint.setImageFilter(filters[i].fFilter);
456 paint.setTextSize(SkIntToScalar(height));
457 paint.setColor(SK_ColorWHITE);
458 SkString str;
459 const char* text = "ABC";
460 SkScalar ypos = SkIntToScalar(height);
461 untiledCanvas.save();
462 untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
463 untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
464 untiledCanvas.restore();
465 for (int y = 0; y < height; y += tileSize) {
466 for (int x = 0; x < width; x += tileSize) {
467 tiledCanvas.save();
468 tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
469 tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
470 tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
471 tiledCanvas.restore();
472 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000473 }
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000474 untiledCanvas.flush();
475 tiledCanvas.flush();
476 for (int y = 0; y < height; y++) {
477 int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
478 REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
479 if (diffs) {
480 break;
481 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000482 }
483 }
484 }
485
486 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
487 SkSafeUnref(filters[i].fFilter);
488 }
489}
490
mtklein3f3b3d02014-12-01 11:47:08 -0800491static void draw_saveLayer_picture(int width, int height, int tileSize,
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700492 SkBBHFactory* factory, SkBitmap* result) {
mtkleind910f542014-08-22 09:06:34 -0700493
494 SkMatrix matrix;
495 matrix.setTranslate(SkIntToScalar(50), 0);
496
497 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
498 SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
499 SkAutoTUnref<SkImageFilter> imageFilter(SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel, cfif.get()));
500
501 SkPaint paint;
502 paint.setImageFilter(imageFilter.get());
503 SkPictureRecorder recorder;
504 SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
mtklein3f3b3d02014-12-01 11:47:08 -0800505 SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
506 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700507 factory, 0);
mtkleind910f542014-08-22 09:06:34 -0700508 recordingCanvas->translate(-55, 0);
509 recordingCanvas->saveLayer(&bounds, &paint);
510 recordingCanvas->restore();
511 SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
512
513 result->allocN32Pixels(width, height);
514 SkCanvas canvas(*result);
515 canvas.clear(0);
516 canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
517 canvas.drawPicture(picture1.get());
518}
519
520DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
521 // Check that matrix filter when drawn tiled with BBH exactly
522 // matches the same thing drawn without BBH.
523 // Tests pass by not asserting.
524
525 const int width = 200, height = 200;
526 const int tileSize = 100;
527 SkBitmap result1, result2;
528 SkRTreeFactory factory;
529
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700530 draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
531 draw_saveLayer_picture(width, height, tileSize, NULL, &result2);
mtkleind910f542014-08-22 09:06:34 -0700532
533 for (int y = 0; y < height; y++) {
534 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
535 REPORTER_ASSERT(reporter, !diffs);
536 if (diffs) {
537 break;
538 }
539 }
540}
541
senorblanco1150a6d2014-08-25 12:46:58 -0700542static SkImageFilter* makeBlur(SkImageFilter* input = NULL) {
543 return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input);
544}
545
546static SkImageFilter* makeDropShadow(SkImageFilter* input = NULL) {
547 return SkDropShadowImageFilter::Create(
548 SkIntToScalar(100), SkIntToScalar(100),
549 SkIntToScalar(10), SkIntToScalar(10),
sugoi234f0362014-10-23 13:59:52 -0700550 SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
551 input, NULL, 0);
senorblanco1150a6d2014-08-25 12:46:58 -0700552}
553
554DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
555 SkAutoTUnref<SkImageFilter> filter1(makeBlur());
556 SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
557
558 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
559 SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
560 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
561
562 REPORTER_ASSERT(reporter, bounds == expectedBounds);
563}
564
565DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
566 SkAutoTUnref<SkImageFilter> filter1(makeDropShadow());
567 SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get()));
568
569 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
570 SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
571 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
572
573 REPORTER_ASSERT(reporter, bounds == expectedBounds);
574}
575
576DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
577 SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2));
578 SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
579
580 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
581 SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
582 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
583
584 REPORTER_ASSERT(reporter, bounds == expectedBounds);
585}
586
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700587static void draw_blurred_rect(SkCanvas* canvas) {
senorblanco837f5322014-07-14 10:19:54 -0700588 SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
589 SkPaint filterPaint;
590 filterPaint.setColor(SK_ColorWHITE);
591 filterPaint.setImageFilter(filter);
592 canvas->saveLayer(NULL, &filterPaint);
593 SkPaint whitePaint;
594 whitePaint.setColor(SK_ColorWHITE);
595 canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
596 canvas->restore();
597}
598
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700599static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
senorblanco837f5322014-07-14 10:19:54 -0700600 canvas->save();
601 canvas->clipRect(clipRect);
602 canvas->drawPicture(picture);
603 canvas->restore();
604}
605
606DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
607 // Check that the blur filter when recorded with RTree acceleration,
608 // and drawn tiled (with subsequent clip rects) exactly
609 // matches the same filter drawn with without RTree acceleration.
610 // This tests that the "bleed" from the blur into the otherwise-blank
611 // tiles is correctly rendered.
612 // Tests pass by not asserting.
613
614 int width = 16, height = 8;
615 SkBitmap result1, result2;
616 result1.allocN32Pixels(width, height);
617 result2.allocN32Pixels(width, height);
618 SkCanvas canvas1(result1);
619 SkCanvas canvas2(result2);
620 int tileSize = 8;
621
622 canvas1.clear(0);
623 canvas2.clear(0);
624
625 SkRTreeFactory factory;
626
627 SkPictureRecorder recorder1, recorder2;
628 // The only difference between these two pictures is that one has RTree aceleration.
mtklein3f3b3d02014-12-01 11:47:08 -0800629 SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
630 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700631 NULL, 0);
mtklein3f3b3d02014-12-01 11:47:08 -0800632 SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
633 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700634 &factory, 0);
635 draw_blurred_rect(recordingCanvas1);
636 draw_blurred_rect(recordingCanvas2);
senorblanco837f5322014-07-14 10:19:54 -0700637 SkAutoTUnref<SkPicture> picture1(recorder1.endRecording());
638 SkAutoTUnref<SkPicture> picture2(recorder2.endRecording());
639 for (int y = 0; y < height; y += tileSize) {
640 for (int x = 0; x < width; x += tileSize) {
641 SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700642 draw_picture_clipped(&canvas1, tileRect, picture1);
643 draw_picture_clipped(&canvas2, tileRect, picture2);
senorblanco837f5322014-07-14 10:19:54 -0700644 }
645 }
646 for (int y = 0; y < height; y++) {
647 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
648 REPORTER_ASSERT(reporter, !diffs);
649 if (diffs) {
650 break;
651 }
652 }
653}
654
senorblanco@chromium.org91957432014-05-01 14:03:41 +0000655DEF_TEST(ImageFilterMatrixConvolution, reporter) {
656 // Check that a 1x3 filter does not cause a spurious assert.
657 SkScalar kernel[3] = {
658 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
659 };
660 SkISize kernelSize = SkISize::Make(1, 3);
661 SkScalar gain = SK_Scalar1, bias = 0;
662 SkIPoint kernelOffset = SkIPoint::Make(0, 0);
663
664 SkAutoTUnref<SkImageFilter> filter(
665 SkMatrixConvolutionImageFilter::Create(
666 kernelSize, kernel, gain, bias, kernelOffset,
667 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
668
669 SkBitmap result;
670 int width = 16, height = 16;
671 result.allocN32Pixels(width, height);
672 SkCanvas canvas(result);
673 canvas.clear(0);
674
675 SkPaint paint;
676 paint.setImageFilter(filter);
677 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
678 canvas.drawRect(rect, paint);
679}
680
senorblanco@chromium.org8c7372b2014-05-02 19:13:11 +0000681DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
682 // Check that a filter with borders outside the target bounds
683 // does not crash.
684 SkScalar kernel[3] = {
685 0, 0, 0,
686 };
687 SkISize kernelSize = SkISize::Make(3, 1);
688 SkScalar gain = SK_Scalar1, bias = 0;
689 SkIPoint kernelOffset = SkIPoint::Make(2, 0);
690
691 SkAutoTUnref<SkImageFilter> filter(
692 SkMatrixConvolutionImageFilter::Create(
693 kernelSize, kernel, gain, bias, kernelOffset,
694 SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
695
696 SkBitmap result;
697
698 int width = 10, height = 10;
699 result.allocN32Pixels(width, height);
700 SkCanvas canvas(result);
701 canvas.clear(0);
702
703 SkPaint filterPaint;
704 filterPaint.setImageFilter(filter);
705 SkRect bounds = SkRect::MakeWH(1, 10);
706 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
707 SkPaint rectPaint;
708 canvas.saveLayer(&bounds, &filterPaint);
709 canvas.drawRect(rect, rectPaint);
710 canvas.restore();
711}
712
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000713DEF_TEST(ImageFilterCropRect, reporter) {
714 SkBitmap temp;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000715 temp.allocN32Pixels(100, 100);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000716 SkBitmapDevice device(temp);
717 test_crop_rects(&device, reporter);
718}
719
tfarina9ea53f92014-06-24 06:50:39 -0700720DEF_TEST(ImageFilterMatrix, reporter) {
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000721 SkBitmap temp;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000722 temp.allocN32Pixels(100, 100);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000723 SkBitmapDevice device(temp);
724 SkCanvas canvas(&device);
725 canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
726
727 SkMatrix expectedMatrix = canvas.getTotalMatrix();
728
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000729 SkRTreeFactory factory;
730 SkPictureRecorder recorder;
731 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000732
733 SkPaint paint;
734 SkAutoTUnref<MatrixTestImageFilter> imageFilter(
735 new MatrixTestImageFilter(reporter, expectedMatrix));
736 paint.setImageFilter(imageFilter.get());
commit-bot@chromium.org091a5942014-04-18 14:19:31 +0000737 recordingCanvas->saveLayer(NULL, &paint);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000738 SkPaint solidPaint;
739 solidPaint.setColor(0xFFFFFFFF);
740 recordingCanvas->save();
741 recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
742 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
743 recordingCanvas->restore(); // scale
744 recordingCanvas->restore(); // saveLayer
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000745 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000746
robertphillips9b14f262014-06-04 05:40:44 -0700747 canvas.drawPicture(picture);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000748}
749
senorblanco3d822c22014-07-30 14:49:31 -0700750DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000751 SkRTreeFactory factory;
752 SkPictureRecorder recorder;
753 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
754
755 // Create an SkPicture which simply draws a green 1x1 rectangle.
756 SkPaint greenPaint;
757 greenPaint.setColor(SK_ColorGREEN);
758 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
759 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
760
761 // Wrap that SkPicture in an SkPictureImageFilter.
762 SkAutoTUnref<SkImageFilter> imageFilter(
763 SkPictureImageFilter::Create(picture.get()));
764
765 // Check that SkPictureImageFilter successfully serializes its contained
766 // SkPicture when not in cross-process mode.
767 SkPaint paint;
768 paint.setImageFilter(imageFilter.get());
769 SkPictureRecorder outerRecorder;
770 SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
771 SkPaint redPaintWithFilter;
772 redPaintWithFilter.setColor(SK_ColorRED);
773 redPaintWithFilter.setImageFilter(imageFilter.get());
774 outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
775 SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
776
777 SkBitmap bitmap;
778 bitmap.allocN32Pixels(1, 1);
779 SkBitmapDevice device(bitmap);
780 SkCanvas canvas(&device);
781
782 // The result here should be green, since the filter replaces the primitive's red interior.
783 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700784 canvas.drawPicture(outerPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000785 uint32_t pixel = *bitmap.getAddr32(0, 0);
786 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
787
788 // Check that, for now, SkPictureImageFilter does not serialize or
789 // deserialize its contained picture when the filter is serialized
790 // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
791 SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
792 SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
793 data->data(), data->size(), SkImageFilter::GetFlattenableType()));
794 SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
795
796 redPaintWithFilter.setImageFilter(unflattenedFilter);
797 SkPictureRecorder crossProcessRecorder;
798 SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
799 crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
800 SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
801
802 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700803 canvas.drawPicture(crossProcessPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000804 pixel = *bitmap.getAddr32(0, 0);
805 // The result here should not be green, since the filter draws nothing.
806 REPORTER_ASSERT(reporter, pixel != SK_ColorGREEN);
807}
808
senorblanco3d822c22014-07-30 14:49:31 -0700809DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
810 SkRTreeFactory factory;
811 SkPictureRecorder recorder;
812 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
813
814 // Create an SkPicture which simply draws a green 1x1 rectangle.
815 SkPaint greenPaint;
816 greenPaint.setColor(SK_ColorGREEN);
817 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
818 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
819
820 SkAutoTUnref<SkImageFilter> imageFilter(
821 SkPictureImageFilter::Create(picture.get()));
822
823 SkBitmap result;
824 SkIPoint offset;
825 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), NULL);
826 SkBitmap bitmap;
827 bitmap.allocN32Pixels(2, 2);
828 SkBitmapDevice device(bitmap);
fmalita2d97bc12014-11-20 10:44:58 -0800829 SkDeviceImageFilterProxy proxy(&device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
senorblanco3d822c22014-07-30 14:49:31 -0700830 REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
831}
832
tfarina9ea53f92014-06-24 06:50:39 -0700833DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000834 // Even when there's an empty saveLayer()/restore(), ensure that an image
835 // filter or color filter which affects transparent black still draws.
836
837 SkBitmap bitmap;
838 bitmap.allocN32Pixels(10, 10);
839 SkBitmapDevice device(bitmap);
840 SkCanvas canvas(&device);
841
842 SkRTreeFactory factory;
843 SkPictureRecorder recorder;
844
845 SkAutoTUnref<SkColorFilter> green(
846 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
847 SkAutoTUnref<SkColorFilterImageFilter> imageFilter(
848 SkColorFilterImageFilter::Create(green.get()));
849 SkPaint imageFilterPaint;
850 imageFilterPaint.setImageFilter(imageFilter.get());
851 SkPaint colorFilterPaint;
852 colorFilterPaint.setColorFilter(green.get());
853
854 SkRect bounds = SkRect::MakeWH(10, 10);
855
856 SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
857 recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
858 recordingCanvas->restore();
859 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
860
861 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700862 canvas.drawPicture(picture);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000863 uint32_t pixel = *bitmap.getAddr32(0, 0);
864 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
865
866 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
867 recordingCanvas->saveLayer(NULL, &imageFilterPaint);
868 recordingCanvas->restore();
869 SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
870
871 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700872 canvas.drawPicture(picture2);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000873 pixel = *bitmap.getAddr32(0, 0);
874 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
875
876 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
877 recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
878 recordingCanvas->restore();
879 SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
880
881 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700882 canvas.drawPicture(picture3);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000883 pixel = *bitmap.getAddr32(0, 0);
884 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
885}
886
senorblanco@chromium.org28ae55d2014-03-24 21:32:28 +0000887static void test_huge_blur(SkBaseDevice* device, skiatest::Reporter* reporter) {
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000888 SkCanvas canvas(device);
889
890 SkBitmap bitmap;
891 bitmap.allocN32Pixels(100, 100);
892 bitmap.eraseARGB(0, 0, 0, 0);
893
894 // Check that a blur with an insane radius does not crash or assert.
895 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
896
897 SkPaint paint;
898 paint.setImageFilter(blur);
899 canvas.drawSprite(bitmap, 0, 0, &paint);
900}
901
902DEF_TEST(HugeBlurImageFilter, reporter) {
903 SkBitmap temp;
904 temp.allocN32Pixels(100, 100);
905 SkBitmapDevice device(temp);
906 test_huge_blur(&device, reporter);
907}
908
senorblanco3a495202014-09-29 07:57:20 -0700909DEF_TEST(MatrixConvolutionSanityTest, reporter) {
910 SkScalar kernel[1] = { 0 };
911 SkScalar gain = SK_Scalar1, bias = 0;
912 SkIPoint kernelOffset = SkIPoint::Make(1, 1);
913
914 // Check that an enormous (non-allocatable) kernel gives a NULL filter.
915 SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create(
916 SkISize::Make(1<<30, 1<<30),
917 kernel,
918 gain,
919 bias,
920 kernelOffset,
921 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
922 false));
923
924 REPORTER_ASSERT(reporter, NULL == conv.get());
925
926 // Check that a NULL kernel gives a NULL filter.
927 conv.reset(SkMatrixConvolutionImageFilter::Create(
928 SkISize::Make(1, 1),
929 NULL,
930 gain,
931 bias,
932 kernelOffset,
933 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
934 false));
935
936 REPORTER_ASSERT(reporter, NULL == conv.get());
937
938 // Check that a kernel width < 1 gives a NULL filter.
939 conv.reset(SkMatrixConvolutionImageFilter::Create(
940 SkISize::Make(0, 1),
941 kernel,
942 gain,
943 bias,
944 kernelOffset,
945 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
946 false));
947
948 REPORTER_ASSERT(reporter, NULL == conv.get());
949
950 // Check that kernel height < 1 gives a NULL filter.
951 conv.reset(SkMatrixConvolutionImageFilter::Create(
952 SkISize::Make(1, -1),
953 kernel,
954 gain,
955 bias,
956 kernelOffset,
957 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
958 false));
959
960 REPORTER_ASSERT(reporter, NULL == conv.get());
961}
962
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +0000963static void test_xfermode_cropped_input(SkBaseDevice* device, skiatest::Reporter* reporter) {
964 SkCanvas canvas(device);
965 canvas.clear(0);
966
967 SkBitmap bitmap;
968 bitmap.allocN32Pixels(1, 1);
969 bitmap.eraseARGB(255, 255, 255, 255);
970
971 SkAutoTUnref<SkColorFilter> green(
972 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
973 SkAutoTUnref<SkColorFilterImageFilter> greenFilter(
974 SkColorFilterImageFilter::Create(green.get()));
975 SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
976 SkAutoTUnref<SkColorFilterImageFilter> croppedOut(
977 SkColorFilterImageFilter::Create(green.get(), NULL, &cropRect));
978
979 // Check that an xfermode image filter whose input has been cropped out still draws the other
980 // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
981 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
982 SkAutoTUnref<SkImageFilter> xfermodeNoFg(
983 SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
984 SkAutoTUnref<SkImageFilter> xfermodeNoBg(
985 SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
986 SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
987 SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
988
989 SkPaint paint;
990 paint.setImageFilter(xfermodeNoFg);
991 canvas.drawSprite(bitmap, 0, 0, &paint);
992
993 uint32_t pixel;
994 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
995 canvas.readPixels(info, &pixel, 4, 0, 0);
996 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
997
998 paint.setImageFilter(xfermodeNoBg);
999 canvas.drawSprite(bitmap, 0, 0, &paint);
1000 canvas.readPixels(info, &pixel, 4, 0, 0);
1001 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1002
1003 paint.setImageFilter(xfermodeNoFgNoBg);
1004 canvas.drawSprite(bitmap, 0, 0, &paint);
1005 canvas.readPixels(info, &pixel, 4, 0, 0);
1006 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1007}
1008
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001009DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
1010 SkBitmap temp;
1011 temp.allocN32Pixels(50, 50);
1012 SkBitmapDevice device(temp);
1013 SkCanvas canvas(&device);
1014 canvas.clear(0x0);
1015
1016 SkBitmap bitmap;
1017 bitmap.allocN32Pixels(10, 10);
1018 bitmap.eraseColor(SK_ColorGREEN);
1019
1020 SkMatrix matrix;
1021 matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
1022 matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
1023 SkAutoTUnref<SkImageFilter> matrixFilter(
1024 SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel));
1025
1026 // Test that saveLayer() with a filter nested inside another saveLayer() applies the
1027 // correct offset to the filter matrix.
1028 SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
1029 canvas.saveLayer(&bounds1, NULL);
1030 SkPaint filterPaint;
1031 filterPaint.setImageFilter(matrixFilter);
1032 SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
1033 canvas.saveLayer(&bounds2, &filterPaint);
1034 SkPaint greenPaint;
1035 greenPaint.setColor(SK_ColorGREEN);
1036 canvas.drawRect(bounds2, greenPaint);
1037 canvas.restore();
1038 canvas.restore();
1039 SkPaint strokePaint;
1040 strokePaint.setStyle(SkPaint::kStroke_Style);
1041 strokePaint.setColor(SK_ColorRED);
1042
1043 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
1044 uint32_t pixel;
1045 canvas.readPixels(info, &pixel, 4, 25, 25);
1046 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1047
1048 // Test that drawSprite() with a filter nested inside a saveLayer() applies the
1049 // correct offset to the filter matrix.
1050 canvas.clear(0x0);
1051 canvas.readPixels(info, &pixel, 4, 25, 25);
1052 canvas.saveLayer(&bounds1, NULL);
1053 canvas.drawSprite(bitmap, 20, 20, &filterPaint);
1054 canvas.restore();
1055
1056 canvas.readPixels(info, &pixel, 4, 25, 25);
1057 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1058}
1059
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001060DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
1061 SkBitmap temp;
1062 temp.allocN32Pixels(100, 100);
1063 SkBitmapDevice device(temp);
1064 test_xfermode_cropped_input(&device, reporter);
1065}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001066
senorblanco@chromium.org58d14662014-02-03 22:36:39 +00001067#if SK_SUPPORT_GPU
reed4a8126e2014-09-22 07:29:03 -07001068const SkSurfaceProps gProps = SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
1069
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +00001070DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
1071 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001072 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1073 SkImageInfo::MakeN32Premul(100, 100),
reed4a8126e2014-09-22 07:29:03 -07001074 gProps,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001075 0));
1076 test_crop_rects(device, reporter);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +00001077}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001078
1079DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
1080 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1081 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1082 SkImageInfo::MakeN32Premul(100, 100),
reed4a8126e2014-09-22 07:29:03 -07001083 gProps,
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001084 0));
1085 test_huge_blur(device, reporter);
1086}
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001087
1088DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) {
1089 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1090 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1091 SkImageInfo::MakeN32Premul(1, 1),
reed4a8126e2014-09-22 07:29:03 -07001092 gProps,
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001093 0));
1094 test_xfermode_cropped_input(device, reporter);
1095}
senorblanco32673b92014-09-09 09:15:04 -07001096
1097DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) {
1098 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1099 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1100 SkImageInfo::MakeN32Premul(1, 1),
reed4a8126e2014-09-22 07:29:03 -07001101 gProps,
senorblanco32673b92014-09-09 09:15:04 -07001102 0));
1103 test_negative_blur_sigma(device, reporter);
1104}
senorblanco@chromium.org58d14662014-02-03 22:36:39 +00001105#endif