blob: cd6d235dc59d4e5638cbb1291b31dcb08da12d10 [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 "SkBicubicImageFilter.h"
9#include "SkBitmap.h"
10#include "SkBitmapDevice.h"
11#include "SkBitmapSource.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000012#include "SkBlurImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000013#include "SkCanvas.h"
14#include "SkColorFilterImageFilter.h"
15#include "SkColorMatrixFilter.h"
16#include "SkDeviceImageFilterProxy.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000017#include "SkDisplacementMapEffect.h"
18#include "SkDropShadowImageFilter.h"
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000019#include "SkFlattenableBuffers.h"
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +000020#include "SkFlattenableSerialization.h"
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +000021#include "SkGradientShader.h"
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +000022#include "SkLightingImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000023#include "SkMatrixConvolutionImageFilter.h"
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +000024#include "SkMatrixImageFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000025#include "SkMergeImageFilter.h"
26#include "SkMorphologyImageFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000027#include "SkOffsetImageFilter.h"
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000028#include "SkPicture.h"
senorblanco@chromium.org910702b2014-05-30 20:36:15 +000029#include "SkPictureImageFilter.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000030#include "SkPictureRecorder.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000031#include "SkRect.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000032#include "SkTileImageFilter.h"
33#include "SkXfermodeImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000034#include "Test.h"
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000035
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +000036#if SK_SUPPORT_GPU
37#include "GrContextFactory.h"
38#include "SkGpuDevice.h"
39#endif
40
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000041static const int kBitmapSize = 4;
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +000042
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000043namespace {
44
45class MatrixTestImageFilter : public SkImageFilter {
46public:
47 MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
48 : SkImageFilter(0), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
49 }
50
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000051 virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
senorblanco@chromium.org09373352014-02-05 23:04:28 +000052 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000053 REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000054 return true;
55 }
56
57 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
58
59protected:
60 explicit MatrixTestImageFilter(SkReadBuffer& buffer) : SkImageFilter(0) {
61 fReporter = static_cast<skiatest::Reporter*>(buffer.readFunctionPtr());
62 buffer.readMatrix(&fExpectedMatrix);
63 }
64
65 virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
66 buffer.writeFunctionPtr(fReporter);
67 buffer.writeMatrix(fExpectedMatrix);
68 }
69
70private:
71 skiatest::Reporter* fReporter;
72 SkMatrix fExpectedMatrix;
73};
74
75}
76
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000077static void make_small_bitmap(SkBitmap& bitmap) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000078 bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
79 SkCanvas canvas(bitmap);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000080 canvas.clear(0x00000000);
81 SkPaint darkPaint;
82 darkPaint.setColor(0xFF804020);
83 SkPaint lightPaint;
84 lightPaint.setColor(0xFF244484);
85 const int i = kBitmapSize / 4;
86 for (int y = 0; y < kBitmapSize; y += i) {
87 for (int x = 0; x < kBitmapSize; x += i) {
88 canvas.save();
89 canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
90 canvas.drawRect(SkRect::MakeXYWH(0, 0,
91 SkIntToScalar(i),
92 SkIntToScalar(i)), darkPaint);
93 canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
94 0,
95 SkIntToScalar(i),
96 SkIntToScalar(i)), lightPaint);
97 canvas.drawRect(SkRect::MakeXYWH(0,
98 SkIntToScalar(i),
99 SkIntToScalar(i),
100 SkIntToScalar(i)), lightPaint);
101 canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
102 SkIntToScalar(i),
103 SkIntToScalar(i),
104 SkIntToScalar(i)), darkPaint);
105 canvas.restore();
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +0000106 }
107 }
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000108}
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000109
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000110static SkImageFilter* make_scale(float amount, SkImageFilter* input = NULL) {
111 SkScalar s = amount;
112 SkScalar matrix[20] = { s, 0, 0, 0, 0,
113 0, s, 0, 0, 0,
114 0, 0, s, 0, 0,
115 0, 0, 0, s, 0 };
commit-bot@chromium.org727a3522014-02-21 18:46:30 +0000116 SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000117 return SkColorFilterImageFilter::Create(filter, input);
118}
119
120static SkImageFilter* make_grayscale(SkImageFilter* input = NULL, const SkImageFilter::CropRect* cropRect = NULL) {
121 SkScalar matrix[20];
122 memset(matrix, 0, 20 * sizeof(SkScalar));
123 matrix[0] = matrix[5] = matrix[10] = 0.2126f;
124 matrix[1] = matrix[6] = matrix[11] = 0.7152f;
125 matrix[2] = matrix[7] = matrix[12] = 0.0722f;
126 matrix[18] = 1.0f;
commit-bot@chromium.org727a3522014-02-21 18:46:30 +0000127 SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000128 return SkColorFilterImageFilter::Create(filter, input, cropRect);
129}
130
131DEF_TEST(ImageFilter, reporter) {
132 {
133 // Check that two non-clipping color matrices concatenate into a single filter.
134 SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
135 SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
136 REPORTER_ASSERT(reporter, NULL == quarterBrightness->getInput(0));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000137 }
138
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000139 {
140 // Check that a clipping color matrix followed by a grayscale does not concatenate into a single filter.
141 SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
142 SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
143 REPORTER_ASSERT(reporter, NULL != halfBrightness->getInput(0));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000144 }
145
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000146 {
147 // Check that a color filter image filter without a crop rect can be
148 // expressed as a color filter.
149 SkAutoTUnref<SkImageFilter> gray(make_grayscale());
150 REPORTER_ASSERT(reporter, true == gray->asColorFilter(NULL));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000151 }
152
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000153 {
154 // Check that a color filter image filter with a crop rect cannot
155 // be expressed as a color filter.
156 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
157 SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(NULL, &cropRect));
158 REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(NULL));
159 }
160
161 {
162 // Tests pass by not asserting
163 SkBitmap bitmap, result;
164 make_small_bitmap(bitmap);
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000165 result.allocN32Pixels(kBitmapSize, kBitmapSize);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000166
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000167 {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000168 // This tests for :
169 // 1 ) location at (0,0,1)
170 SkPoint3 location(0, 0, SK_Scalar1);
171 // 2 ) location and target at same value
172 SkPoint3 target(location.fX, location.fY, location.fZ);
173 // 3 ) large negative specular exponent value
174 SkScalar specularExponent = -1000;
175
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000176 SkAutoTUnref<SkImageFilter> bmSrc(SkBitmapSource::Create(bitmap));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000177 SkPaint paint;
178 paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(
179 location, target, specularExponent, 180,
180 0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
181 bmSrc))->unref();
182 SkCanvas canvas(result);
183 SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
184 SkIntToScalar(kBitmapSize));
185 canvas.drawRect(r, paint);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000186 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000187
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000188 {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000189 // This tests for scale bringing width to 0
190 SkSize scale = SkSize::Make(-0.001f, SK_Scalar1);
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000191 SkAutoTUnref<SkImageFilter> bmSrc(SkBitmapSource::Create(bitmap));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000192 SkAutoTUnref<SkBicubicImageFilter> bicubic(
193 SkBicubicImageFilter::CreateMitchell(scale, bmSrc));
194 SkBitmapDevice device(bitmap);
195 SkDeviceImageFilterProxy proxy(&device);
196 SkIPoint loc = SkIPoint::Make(0, 0);
197 // An empty input should early return and return false
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000198 SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(2));
199 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeEmpty(), cache.get());
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000200 REPORTER_ASSERT(reporter,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000201 !bicubic->filterImage(&proxy, bitmap, ctx, &result, &loc));
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +0000202 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000203 }
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000204}
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000205
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000206static void test_crop_rects(SkBaseDevice* device, skiatest::Reporter* reporter) {
207 // Check that all filters offset to their absolute crop rect,
208 // unaffected by the input crop rect.
209 // Tests pass by not asserting.
210 SkBitmap bitmap;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000211 bitmap.allocN32Pixels(100, 100);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000212 bitmap.eraseARGB(0, 0, 0, 0);
213 SkDeviceImageFilterProxy proxy(device);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000214
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000215 SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
216 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
217 SkAutoTUnref<SkImageFilter> input(make_grayscale(NULL, &inputCropRect));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000218
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000219 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
220 SkPoint3 location(0, 0, SK_Scalar1);
221 SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
222 SkScalar kernel[9] = {
223 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
224 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
225 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
226 };
227 SkISize kernelSize = SkISize::Make(3, 3);
228 SkScalar gain = SK_Scalar1, bias = 0;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000229
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000230 SkImageFilter* filters[] = {
231 SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect),
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000232 SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
233 SkDisplacementMapEffect::kB_ChannelSelectorType,
234 40.0f, input.get(), input.get(), &cropRect),
235 SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
236 SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN, input.get(), &cropRect),
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000237 SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
238 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000239 SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
240 SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect),
241 SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
242 SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
243 SkDilateImageFilter::Create(3, 2, input.get(), &cropRect),
244 SkErodeImageFilter::Create(2, 3, input.get(), &cropRect),
245 SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()),
246 SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect),
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000247 };
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000248
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000249 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
250 SkImageFilter* filter = filters[i];
251 SkBitmap result;
252 SkIPoint offset;
253 SkString str;
senorblanco@chromium.orgf4e1a762014-02-04 00:28:46 +0000254 str.printf("filter %d", static_cast<int>(i));
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000255 SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(2));
256 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), cache.get());
257 REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, ctx,
258 &result, &offset), str.c_str());
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000259 REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000260 }
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000261
262 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
263 SkSafeUnref(filters[i]);
264 }
265}
266
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000267static SkBitmap make_gradient_circle(int width, int height) {
268 SkBitmap bitmap;
269 SkScalar x = SkIntToScalar(width / 2);
270 SkScalar y = SkIntToScalar(height / 2);
271 SkScalar radius = SkMinScalar(x, y) * 0.8f;
272 bitmap.allocN32Pixels(width, height);
273 SkCanvas canvas(bitmap);
274 canvas.clear(0x00000000);
275 SkColor colors[2];
276 colors[0] = SK_ColorWHITE;
277 colors[1] = SK_ColorBLACK;
278 SkAutoTUnref<SkShader> shader(
279 SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2,
280 SkShader::kClamp_TileMode)
281 );
282 SkPaint paint;
283 paint.setShader(shader);
284 canvas.drawCircle(x, y, radius, paint);
285 return bitmap;
286}
287
288DEF_TEST(ImageFilterDrawTiled, reporter) {
289 // Check that all filters when drawn tiled (with subsequent clip rects) exactly
290 // match the same filters drawn with a single full-canvas bitmap draw.
291 // Tests pass by not asserting.
292
293 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
294 SkPoint3 location(0, 0, SK_Scalar1);
295 SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
296 SkScalar kernel[9] = {
297 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
298 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
299 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
300 };
301 SkISize kernelSize = SkISize::Make(3, 3);
302 SkScalar gain = SK_Scalar1, bias = 0;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000303 SkScalar five = SkIntToScalar(5);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000304
305 SkAutoTUnref<SkImageFilter> gradient_source(SkBitmapSource::Create(make_gradient_circle(64, 64)));
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000306 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000307 SkMatrix matrix;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000308
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000309 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
310 matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000311
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000312 SkRTreeFactory factory;
313 SkPictureRecorder recorder;
314 SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
315
316 SkPaint greenPaint;
317 greenPaint.setColor(SK_ColorGREEN);
318 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
319 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
320 SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
321
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000322 struct {
323 const char* fName;
324 SkImageFilter* fFilter;
325 } filters[] = {
326 { "color filter", SkColorFilterImageFilter::Create(cf.get()) },
327 { "displacement map", SkDisplacementMapEffect::Create(
328 SkDisplacementMapEffect::kR_ChannelSelectorType,
329 SkDisplacementMapEffect::kB_ChannelSelectorType,
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000330 20.0f, gradient_source.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000331 { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
332 { "drop shadow", SkDropShadowImageFilter::Create(
333 SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN) },
334 { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
335 location, SK_ColorGREEN, 0, 0) },
336 { "specular lighting",
337 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
338 { "matrix convolution",
339 SkMatrixConvolutionImageFilter::Create(
340 kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
341 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
342 { "merge", SkMergeImageFilter::Create(NULL, NULL, SkXfermode::kSrcOver_Mode) },
343 { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
344 { "dilate", SkDilateImageFilter::Create(3, 2) },
345 { "erode", SkErodeImageFilter::Create(2, 3) },
346 { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
347 SkRect::MakeXYWH(0, 0, 100, 100), NULL) },
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000348 { "matrix", SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel) },
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000349 { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000350 { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000351 };
352
353 SkBitmap untiledResult, tiledResult;
354 int width = 64, height = 64;
355 untiledResult.allocN32Pixels(width, height);
356 tiledResult.allocN32Pixels(width, height);
357 SkCanvas tiledCanvas(tiledResult);
358 SkCanvas untiledCanvas(untiledResult);
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000359 int tileSize = 8;
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000360
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000361 for (int scale = 1; scale <= 2; ++scale) {
362 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
363 tiledCanvas.clear(0);
364 untiledCanvas.clear(0);
365 SkPaint paint;
366 paint.setImageFilter(filters[i].fFilter);
367 paint.setTextSize(SkIntToScalar(height));
368 paint.setColor(SK_ColorWHITE);
369 SkString str;
370 const char* text = "ABC";
371 SkScalar ypos = SkIntToScalar(height);
372 untiledCanvas.save();
373 untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
374 untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
375 untiledCanvas.restore();
376 for (int y = 0; y < height; y += tileSize) {
377 for (int x = 0; x < width; x += tileSize) {
378 tiledCanvas.save();
379 tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
380 tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
381 tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
382 tiledCanvas.restore();
383 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000384 }
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000385 untiledCanvas.flush();
386 tiledCanvas.flush();
387 for (int y = 0; y < height; y++) {
388 int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
389 REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
390 if (diffs) {
391 break;
392 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000393 }
394 }
395 }
396
397 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
398 SkSafeUnref(filters[i].fFilter);
399 }
400}
401
senorblanco@chromium.org91957432014-05-01 14:03:41 +0000402DEF_TEST(ImageFilterMatrixConvolution, reporter) {
403 // Check that a 1x3 filter does not cause a spurious assert.
404 SkScalar kernel[3] = {
405 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
406 };
407 SkISize kernelSize = SkISize::Make(1, 3);
408 SkScalar gain = SK_Scalar1, bias = 0;
409 SkIPoint kernelOffset = SkIPoint::Make(0, 0);
410
411 SkAutoTUnref<SkImageFilter> filter(
412 SkMatrixConvolutionImageFilter::Create(
413 kernelSize, kernel, gain, bias, kernelOffset,
414 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
415
416 SkBitmap result;
417 int width = 16, height = 16;
418 result.allocN32Pixels(width, height);
419 SkCanvas canvas(result);
420 canvas.clear(0);
421
422 SkPaint paint;
423 paint.setImageFilter(filter);
424 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
425 canvas.drawRect(rect, paint);
426}
427
senorblanco@chromium.org8c7372b2014-05-02 19:13:11 +0000428DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
429 // Check that a filter with borders outside the target bounds
430 // does not crash.
431 SkScalar kernel[3] = {
432 0, 0, 0,
433 };
434 SkISize kernelSize = SkISize::Make(3, 1);
435 SkScalar gain = SK_Scalar1, bias = 0;
436 SkIPoint kernelOffset = SkIPoint::Make(2, 0);
437
438 SkAutoTUnref<SkImageFilter> filter(
439 SkMatrixConvolutionImageFilter::Create(
440 kernelSize, kernel, gain, bias, kernelOffset,
441 SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
442
443 SkBitmap result;
444
445 int width = 10, height = 10;
446 result.allocN32Pixels(width, height);
447 SkCanvas canvas(result);
448 canvas.clear(0);
449
450 SkPaint filterPaint;
451 filterPaint.setImageFilter(filter);
452 SkRect bounds = SkRect::MakeWH(1, 10);
453 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
454 SkPaint rectPaint;
455 canvas.saveLayer(&bounds, &filterPaint);
456 canvas.drawRect(rect, rectPaint);
457 canvas.restore();
458}
459
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000460DEF_TEST(ImageFilterCropRect, reporter) {
461 SkBitmap temp;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000462 temp.allocN32Pixels(100, 100);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000463 SkBitmapDevice device(temp);
464 test_crop_rects(&device, reporter);
465}
466
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000467DEF_TEST(ImageFilterMatrixTest, reporter) {
468 SkBitmap temp;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000469 temp.allocN32Pixels(100, 100);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000470 SkBitmapDevice device(temp);
471 SkCanvas canvas(&device);
472 canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
473
474 SkMatrix expectedMatrix = canvas.getTotalMatrix();
475
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000476 SkRTreeFactory factory;
477 SkPictureRecorder recorder;
478 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000479
480 SkPaint paint;
481 SkAutoTUnref<MatrixTestImageFilter> imageFilter(
482 new MatrixTestImageFilter(reporter, expectedMatrix));
483 paint.setImageFilter(imageFilter.get());
commit-bot@chromium.org091a5942014-04-18 14:19:31 +0000484 recordingCanvas->saveLayer(NULL, &paint);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000485 SkPaint solidPaint;
486 solidPaint.setColor(0xFFFFFFFF);
487 recordingCanvas->save();
488 recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
489 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
490 recordingCanvas->restore(); // scale
491 recordingCanvas->restore(); // saveLayer
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000492 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000493
robertphillips9b14f262014-06-04 05:40:44 -0700494 canvas.drawPicture(picture);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000495}
496
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000497DEF_TEST(ImageFilterPictureImageFilterTest, reporter) {
498
499 SkRTreeFactory factory;
500 SkPictureRecorder recorder;
501 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
502
503 // Create an SkPicture which simply draws a green 1x1 rectangle.
504 SkPaint greenPaint;
505 greenPaint.setColor(SK_ColorGREEN);
506 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
507 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
508
509 // Wrap that SkPicture in an SkPictureImageFilter.
510 SkAutoTUnref<SkImageFilter> imageFilter(
511 SkPictureImageFilter::Create(picture.get()));
512
513 // Check that SkPictureImageFilter successfully serializes its contained
514 // SkPicture when not in cross-process mode.
515 SkPaint paint;
516 paint.setImageFilter(imageFilter.get());
517 SkPictureRecorder outerRecorder;
518 SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
519 SkPaint redPaintWithFilter;
520 redPaintWithFilter.setColor(SK_ColorRED);
521 redPaintWithFilter.setImageFilter(imageFilter.get());
522 outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
523 SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
524
525 SkBitmap bitmap;
526 bitmap.allocN32Pixels(1, 1);
527 SkBitmapDevice device(bitmap);
528 SkCanvas canvas(&device);
529
530 // The result here should be green, since the filter replaces the primitive's red interior.
531 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700532 canvas.drawPicture(outerPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000533 uint32_t pixel = *bitmap.getAddr32(0, 0);
534 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
535
536 // Check that, for now, SkPictureImageFilter does not serialize or
537 // deserialize its contained picture when the filter is serialized
538 // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
539 SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
540 SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
541 data->data(), data->size(), SkImageFilter::GetFlattenableType()));
542 SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
543
544 redPaintWithFilter.setImageFilter(unflattenedFilter);
545 SkPictureRecorder crossProcessRecorder;
546 SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
547 crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
548 SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
549
550 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700551 canvas.drawPicture(crossProcessPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000552 pixel = *bitmap.getAddr32(0, 0);
553 // The result here should not be green, since the filter draws nothing.
554 REPORTER_ASSERT(reporter, pixel != SK_ColorGREEN);
555}
556
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000557DEF_TEST(ImageFilterEmptySaveLayerTest, reporter) {
558
559 // Even when there's an empty saveLayer()/restore(), ensure that an image
560 // filter or color filter which affects transparent black still draws.
561
562 SkBitmap bitmap;
563 bitmap.allocN32Pixels(10, 10);
564 SkBitmapDevice device(bitmap);
565 SkCanvas canvas(&device);
566
567 SkRTreeFactory factory;
568 SkPictureRecorder recorder;
569
570 SkAutoTUnref<SkColorFilter> green(
571 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
572 SkAutoTUnref<SkColorFilterImageFilter> imageFilter(
573 SkColorFilterImageFilter::Create(green.get()));
574 SkPaint imageFilterPaint;
575 imageFilterPaint.setImageFilter(imageFilter.get());
576 SkPaint colorFilterPaint;
577 colorFilterPaint.setColorFilter(green.get());
578
579 SkRect bounds = SkRect::MakeWH(10, 10);
580
581 SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
582 recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
583 recordingCanvas->restore();
584 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
585
586 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700587 canvas.drawPicture(picture);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000588 uint32_t pixel = *bitmap.getAddr32(0, 0);
589 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
590
591 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
592 recordingCanvas->saveLayer(NULL, &imageFilterPaint);
593 recordingCanvas->restore();
594 SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
595
596 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700597 canvas.drawPicture(picture2);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000598 pixel = *bitmap.getAddr32(0, 0);
599 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
600
601 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
602 recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
603 recordingCanvas->restore();
604 SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
605
606 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700607 canvas.drawPicture(picture3);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000608 pixel = *bitmap.getAddr32(0, 0);
609 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
610}
611
senorblanco@chromium.org28ae55d2014-03-24 21:32:28 +0000612static void test_huge_blur(SkBaseDevice* device, skiatest::Reporter* reporter) {
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000613 SkCanvas canvas(device);
614
615 SkBitmap bitmap;
616 bitmap.allocN32Pixels(100, 100);
617 bitmap.eraseARGB(0, 0, 0, 0);
618
619 // Check that a blur with an insane radius does not crash or assert.
620 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
621
622 SkPaint paint;
623 paint.setImageFilter(blur);
624 canvas.drawSprite(bitmap, 0, 0, &paint);
625}
626
627DEF_TEST(HugeBlurImageFilter, reporter) {
628 SkBitmap temp;
629 temp.allocN32Pixels(100, 100);
630 SkBitmapDevice device(temp);
631 test_huge_blur(&device, reporter);
632}
633
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +0000634static void test_xfermode_cropped_input(SkBaseDevice* device, skiatest::Reporter* reporter) {
635 SkCanvas canvas(device);
636 canvas.clear(0);
637
638 SkBitmap bitmap;
639 bitmap.allocN32Pixels(1, 1);
640 bitmap.eraseARGB(255, 255, 255, 255);
641
642 SkAutoTUnref<SkColorFilter> green(
643 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
644 SkAutoTUnref<SkColorFilterImageFilter> greenFilter(
645 SkColorFilterImageFilter::Create(green.get()));
646 SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
647 SkAutoTUnref<SkColorFilterImageFilter> croppedOut(
648 SkColorFilterImageFilter::Create(green.get(), NULL, &cropRect));
649
650 // Check that an xfermode image filter whose input has been cropped out still draws the other
651 // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
652 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
653 SkAutoTUnref<SkImageFilter> xfermodeNoFg(
654 SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
655 SkAutoTUnref<SkImageFilter> xfermodeNoBg(
656 SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
657 SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
658 SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
659
660 SkPaint paint;
661 paint.setImageFilter(xfermodeNoFg);
662 canvas.drawSprite(bitmap, 0, 0, &paint);
663
664 uint32_t pixel;
665 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
666 canvas.readPixels(info, &pixel, 4, 0, 0);
667 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
668
669 paint.setImageFilter(xfermodeNoBg);
670 canvas.drawSprite(bitmap, 0, 0, &paint);
671 canvas.readPixels(info, &pixel, 4, 0, 0);
672 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
673
674 paint.setImageFilter(xfermodeNoFgNoBg);
675 canvas.drawSprite(bitmap, 0, 0, &paint);
676 canvas.readPixels(info, &pixel, 4, 0, 0);
677 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
678}
679
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +0000680DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
681 SkBitmap temp;
682 temp.allocN32Pixels(50, 50);
683 SkBitmapDevice device(temp);
684 SkCanvas canvas(&device);
685 canvas.clear(0x0);
686
687 SkBitmap bitmap;
688 bitmap.allocN32Pixels(10, 10);
689 bitmap.eraseColor(SK_ColorGREEN);
690
691 SkMatrix matrix;
692 matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
693 matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
694 SkAutoTUnref<SkImageFilter> matrixFilter(
695 SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel));
696
697 // Test that saveLayer() with a filter nested inside another saveLayer() applies the
698 // correct offset to the filter matrix.
699 SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
700 canvas.saveLayer(&bounds1, NULL);
701 SkPaint filterPaint;
702 filterPaint.setImageFilter(matrixFilter);
703 SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
704 canvas.saveLayer(&bounds2, &filterPaint);
705 SkPaint greenPaint;
706 greenPaint.setColor(SK_ColorGREEN);
707 canvas.drawRect(bounds2, greenPaint);
708 canvas.restore();
709 canvas.restore();
710 SkPaint strokePaint;
711 strokePaint.setStyle(SkPaint::kStroke_Style);
712 strokePaint.setColor(SK_ColorRED);
713
714 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
715 uint32_t pixel;
716 canvas.readPixels(info, &pixel, 4, 25, 25);
717 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
718
719 // Test that drawSprite() with a filter nested inside a saveLayer() applies the
720 // correct offset to the filter matrix.
721 canvas.clear(0x0);
722 canvas.readPixels(info, &pixel, 4, 25, 25);
723 canvas.saveLayer(&bounds1, NULL);
724 canvas.drawSprite(bitmap, 20, 20, &filterPaint);
725 canvas.restore();
726
727 canvas.readPixels(info, &pixel, 4, 25, 25);
728 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
729}
730
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +0000731DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
732 SkBitmap temp;
733 temp.allocN32Pixels(100, 100);
734 SkBitmapDevice device(temp);
735 test_xfermode_cropped_input(&device, reporter);
736}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000737
senorblanco@chromium.org58d14662014-02-03 22:36:39 +0000738#if SK_SUPPORT_GPU
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000739DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
740 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000741 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
742 SkImageInfo::MakeN32Premul(100, 100),
743 0));
744 test_crop_rects(device, reporter);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000745}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000746
747DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
748 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
749 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
750 SkImageInfo::MakeN32Premul(100, 100),
751 0));
752 test_huge_blur(device, reporter);
753}
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +0000754
755DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) {
756 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
757 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
758 SkImageInfo::MakeN32Premul(1, 1),
759 0));
760 test_xfermode_cropped_input(device, reporter);
761}
senorblanco@chromium.org58d14662014-02-03 22:36:39 +0000762#endif