blob: 2619c6ff10200199354b8674d4638b755379b835 [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"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000010#include "SkBlurImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000011#include "SkCanvas.h"
12#include "SkColorFilterImageFilter.h"
13#include "SkColorMatrixFilter.h"
ajuma5788faa2015-02-13 09:05:47 -080014#include "SkComposeImageFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000015#include "SkDisplacementMapEffect.h"
16#include "SkDropShadowImageFilter.h"
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +000017#include "SkFlattenableSerialization.h"
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +000018#include "SkGradientShader.h"
fmalita5598b632015-09-15 11:26:13 -070019#include "SkImage.h"
fmalitacd56f812015-09-14 13:31:18 -070020#include "SkImageSource.h"
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +000021#include "SkLightingImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000022#include "SkMatrixConvolutionImageFilter.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"
robertphillips3d32d762015-07-13 13:16:44 -070030#include "SkPoint3.h"
halcanary97d2c0a2014-08-19 06:27:53 -070031#include "SkReadBuffer.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000032#include "SkRect.h"
senorblanco8f3937d2014-10-29 12:36:32 -070033#include "SkRectShaderImageFilter.h"
fmalitacd56f812015-09-14 13:31:18 -070034#include "SkSurface.h"
senorblanco0abdf762015-08-20 11:10:41 -070035#include "SkTableColorFilter.h"
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000036#include "SkTileImageFilter.h"
37#include "SkXfermodeImageFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000038#include "Test.h"
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000039
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +000040#if SK_SUPPORT_GPU
kkinnunen15302832015-12-01 04:35:26 -080041#include "GrContext.h"
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +000042#include "SkGpuDevice.h"
43#endif
44
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000045static const int kBitmapSize = 4;
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +000046
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000047namespace {
48
49class MatrixTestImageFilter : public SkImageFilter {
50public:
51 MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -070052 : SkImageFilter(0, nullptr), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000053 }
54
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000055 virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
mtklein36352bf2015-03-25 18:17:31 -070056 SkBitmap* result, SkIPoint* offset) const override {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000057 REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000058 return true;
59 }
60
robertphillipsf3f5bad2014-12-19 13:49:15 -080061 SK_TO_STRING_OVERRIDE()
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000062 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
63
64protected:
mtklein36352bf2015-03-25 18:17:31 -070065 void flatten(SkWriteBuffer& buffer) const override {
reed9fa60da2014-08-21 07:59:51 -070066 this->INHERITED::flatten(buffer);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000067 buffer.writeFunctionPtr(fReporter);
68 buffer.writeMatrix(fExpectedMatrix);
69 }
70
71private:
72 skiatest::Reporter* fReporter;
73 SkMatrix fExpectedMatrix;
mtklein3f3b3d02014-12-01 11:47:08 -080074
reed9fa60da2014-08-21 07:59:51 -070075 typedef SkImageFilter INHERITED;
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +000076};
77
78}
79
reed9fa60da2014-08-21 07:59:51 -070080SkFlattenable* MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
81 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
82 skiatest::Reporter* reporter = (skiatest::Reporter*)buffer.readFunctionPtr();
83 SkMatrix matrix;
84 buffer.readMatrix(&matrix);
halcanary385fe4d2015-08-26 13:07:48 -070085 return new MatrixTestImageFilter(reporter, matrix);
reed9fa60da2014-08-21 07:59:51 -070086}
87
robertphillipsf3f5bad2014-12-19 13:49:15 -080088#ifndef SK_IGNORE_TO_STRING
89void MatrixTestImageFilter::toString(SkString* str) const {
90 str->appendf("MatrixTestImageFilter: (");
91 str->append(")");
92}
93#endif
94
fmalita5598b632015-09-15 11:26:13 -070095static SkImage* make_small_image() {
96 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(kBitmapSize, kBitmapSize));
97 SkCanvas* canvas = surface->getCanvas();
98 canvas->clear(0x00000000);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +000099 SkPaint darkPaint;
100 darkPaint.setColor(0xFF804020);
101 SkPaint lightPaint;
102 lightPaint.setColor(0xFF244484);
103 const int i = kBitmapSize / 4;
104 for (int y = 0; y < kBitmapSize; y += i) {
105 for (int x = 0; x < kBitmapSize; x += i) {
fmalita5598b632015-09-15 11:26:13 -0700106 canvas->save();
107 canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
108 canvas->drawRect(SkRect::MakeXYWH(0, 0,
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000109 SkIntToScalar(i),
110 SkIntToScalar(i)), darkPaint);
fmalita5598b632015-09-15 11:26:13 -0700111 canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000112 0,
113 SkIntToScalar(i),
114 SkIntToScalar(i)), lightPaint);
fmalita5598b632015-09-15 11:26:13 -0700115 canvas->drawRect(SkRect::MakeXYWH(0,
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000116 SkIntToScalar(i),
117 SkIntToScalar(i),
118 SkIntToScalar(i)), lightPaint);
fmalita5598b632015-09-15 11:26:13 -0700119 canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000120 SkIntToScalar(i),
121 SkIntToScalar(i),
122 SkIntToScalar(i)), darkPaint);
fmalita5598b632015-09-15 11:26:13 -0700123 canvas->restore();
commit-bot@chromium.org4b681bc2013-09-13 12:40:02 +0000124 }
125 }
fmalita5598b632015-09-15 11:26:13 -0700126
127 return surface->newImageSnapshot();
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000128}
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000129
halcanary96fcdcc2015-08-27 07:41:13 -0700130static SkImageFilter* make_scale(float amount, SkImageFilter* input = nullptr) {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000131 SkScalar s = amount;
132 SkScalar matrix[20] = { s, 0, 0, 0, 0,
133 0, s, 0, 0, 0,
134 0, 0, s, 0, 0,
135 0, 0, 0, s, 0 };
commit-bot@chromium.org727a3522014-02-21 18:46:30 +0000136 SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000137 return SkColorFilterImageFilter::Create(filter, input);
138}
139
reedcedc36f2015-03-08 04:42:52 -0700140static SkImageFilter* make_grayscale(SkImageFilter* input, const SkImageFilter::CropRect* cropRect) {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000141 SkScalar matrix[20];
142 memset(matrix, 0, 20 * sizeof(SkScalar));
143 matrix[0] = matrix[5] = matrix[10] = 0.2126f;
144 matrix[1] = matrix[6] = matrix[11] = 0.7152f;
145 matrix[2] = matrix[7] = matrix[12] = 0.0722f;
146 matrix[18] = 1.0f;
commit-bot@chromium.org727a3522014-02-21 18:46:30 +0000147 SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000148 return SkColorFilterImageFilter::Create(filter, input, cropRect);
149}
150
reedcedc36f2015-03-08 04:42:52 -0700151static SkImageFilter* make_blue(SkImageFilter* input, const SkImageFilter::CropRect* cropRect) {
152 SkAutoTUnref<SkColorFilter> filter(SkColorFilter::CreateModeFilter(SK_ColorBLUE,
153 SkXfermode::kSrcIn_Mode));
154 return SkColorFilterImageFilter::Create(filter, input, cropRect);
155}
156
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000157DEF_TEST(ImageFilter, reporter) {
158 {
reedcedc36f2015-03-08 04:42:52 -0700159 // Check that two non-clipping color-matrice-filters concatenate into a single filter.
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000160 SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
161 SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
halcanary96fcdcc2015-08-27 07:41:13 -0700162 REPORTER_ASSERT(reporter, nullptr == quarterBrightness->getInput(0));
reedcedc36f2015-03-08 04:42:52 -0700163 SkColorFilter* cf;
164 REPORTER_ASSERT(reporter, quarterBrightness->asColorFilter(&cf));
halcanary96fcdcc2015-08-27 07:41:13 -0700165 REPORTER_ASSERT(reporter, cf->asColorMatrix(nullptr));
reedcedc36f2015-03-08 04:42:52 -0700166 cf->unref();
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000167 }
168
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000169 {
reedcedc36f2015-03-08 04:42:52 -0700170 // Check that a clipping color-matrice-filter followed by a color-matrice-filters
171 // concatenates into a single filter, but not a matrixfilter (due to clamping).
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000172 SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
173 SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
halcanary96fcdcc2015-08-27 07:41:13 -0700174 REPORTER_ASSERT(reporter, nullptr == halfBrightness->getInput(0));
reedcedc36f2015-03-08 04:42:52 -0700175 SkColorFilter* cf;
176 REPORTER_ASSERT(reporter, halfBrightness->asColorFilter(&cf));
halcanary96fcdcc2015-08-27 07:41:13 -0700177 REPORTER_ASSERT(reporter, !cf->asColorMatrix(nullptr));
reedcedc36f2015-03-08 04:42:52 -0700178 cf->unref();
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000179 }
180
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000181 {
182 // Check that a color filter image filter without a crop rect can be
183 // expressed as a color filter.
halcanary96fcdcc2015-08-27 07:41:13 -0700184 SkAutoTUnref<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
185 REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000186 }
reedcedc36f2015-03-08 04:42:52 -0700187
188 {
189 // Check that a colorfilterimage filter without a crop rect but with an input
190 // that is another colorfilterimage can be expressed as a colorfilter (composed).
halcanary96fcdcc2015-08-27 07:41:13 -0700191 SkAutoTUnref<SkImageFilter> mode(make_blue(nullptr, nullptr));
192 SkAutoTUnref<SkImageFilter> gray(make_grayscale(mode, nullptr));
193 REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
reedcedc36f2015-03-08 04:42:52 -0700194 }
195
196 {
197 // Test that if we exceed the limit of what ComposeColorFilter can combine, we still
198 // can build the DAG and won't assert if we call asColorFilter.
halcanary96fcdcc2015-08-27 07:41:13 -0700199 SkAutoTUnref<SkImageFilter> filter(make_blue(nullptr, nullptr));
reedcedc36f2015-03-08 04:42:52 -0700200 const int kWayTooManyForComposeColorFilter = 100;
201 for (int i = 0; i < kWayTooManyForComposeColorFilter; ++i) {
halcanary96fcdcc2015-08-27 07:41:13 -0700202 filter.reset(make_blue(filter, nullptr));
reedcedc36f2015-03-08 04:42:52 -0700203 // the first few of these will succeed, but after we hit the internal limit,
204 // it will then return false.
halcanary96fcdcc2015-08-27 07:41:13 -0700205 (void)filter->asColorFilter(nullptr);
reedcedc36f2015-03-08 04:42:52 -0700206 }
207 }
reed5c518a82015-03-05 14:47:29 -0800208
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000209 {
210 // Check that a color filter image filter with a crop rect cannot
211 // be expressed as a color filter.
212 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
halcanary96fcdcc2015-08-27 07:41:13 -0700213 SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect));
214 REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(nullptr));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000215 }
216
217 {
senorblanco3df05012014-07-03 11:13:09 -0700218 // Check that two non-commutative matrices are concatenated in
219 // the correct order.
220 SkScalar blueToRedMatrix[20] = { 0 };
221 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
222 SkScalar redToGreenMatrix[20] = { 0 };
223 redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
224 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
225 SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get()));
226 SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix));
227 SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get()));
228
229 SkBitmap result;
230 result.allocN32Pixels(kBitmapSize, kBitmapSize);
231
232 SkPaint paint;
233 paint.setColor(SK_ColorBLUE);
234 paint.setImageFilter(filter2.get());
235 SkCanvas canvas(result);
236 canvas.clear(0x0);
237 SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
238 canvas.drawRect(rect, paint);
239 uint32_t pixel = *result.getAddr32(0, 0);
240 // The result here should be green, since we have effectively shifted blue to green.
241 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
242 }
243
244 {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000245 // Tests pass by not asserting
fmalita5598b632015-09-15 11:26:13 -0700246 SkAutoTUnref<SkImage> image(make_small_image());
247 SkBitmap result;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000248 result.allocN32Pixels(kBitmapSize, kBitmapSize);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000249
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000250 {
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000251 // This tests for :
252 // 1 ) location at (0,0,1)
robertphillips3d32d762015-07-13 13:16:44 -0700253 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000254 // 2 ) location and target at same value
robertphillips3d32d762015-07-13 13:16:44 -0700255 SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000256 // 3 ) large negative specular exponent value
257 SkScalar specularExponent = -1000;
258
fmalita5598b632015-09-15 11:26:13 -0700259 SkAutoTUnref<SkImageFilter> bmSrc(SkImageSource::Create(image));
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +0000260 SkPaint paint;
261 paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(
262 location, target, specularExponent, 180,
263 0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
264 bmSrc))->unref();
265 SkCanvas canvas(result);
266 SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
267 SkIntToScalar(kBitmapSize));
268 canvas.drawRect(r, paint);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000269 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000270 }
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000271}
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000272
robertphillips9a53fd72015-06-22 09:46:59 -0700273static void test_crop_rects(SkImageFilter::Proxy* proxy, skiatest::Reporter* reporter) {
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000274 // Check that all filters offset to their absolute crop rect,
275 // unaffected by the input crop rect.
276 // Tests pass by not asserting.
277 SkBitmap bitmap;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000278 bitmap.allocN32Pixels(100, 100);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000279 bitmap.eraseARGB(0, 0, 0, 0);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000280
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000281 SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
282 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
halcanary96fcdcc2015-08-27 07:41:13 -0700283 SkAutoTUnref<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000284
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000285 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
robertphillips3d32d762015-07-13 13:16:44 -0700286 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000287 SkScalar kernel[9] = {
288 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
289 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
290 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
291 };
292 SkISize kernelSize = SkISize::Make(3, 3);
293 SkScalar gain = SK_Scalar1, bias = 0;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000294
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000295 SkImageFilter* filters[] = {
296 SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect),
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000297 SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
298 SkDisplacementMapEffect::kB_ChannelSelectorType,
299 40.0f, input.get(), input.get(), &cropRect),
300 SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
sugoi234f0362014-10-23 13:59:52 -0700301 SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1,
302 SK_ColorGREEN, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
senorblanco24e06d52015-03-18 12:11:33 -0700303 input.get(), &cropRect),
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000304 SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
305 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
commit-bot@chromium.orgcac5fd52014-03-10 10:51:58 +0000306 SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
307 SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect),
308 SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
309 SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
310 SkDilateImageFilter::Create(3, 2, input.get(), &cropRect),
311 SkErodeImageFilter::Create(2, 3, input.get(), &cropRect),
312 SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()),
313 SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect),
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000314 };
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000315
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000316 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
317 SkImageFilter* filter = filters[i];
318 SkBitmap result;
319 SkIPoint offset;
320 SkString str;
senorblanco@chromium.orgf4e1a762014-02-04 00:28:46 +0000321 str.printf("filter %d", static_cast<int>(i));
senorblanco84449132015-11-25 10:32:18 -0800322 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
robertphillips9a53fd72015-06-22 09:46:59 -0700323 REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(proxy, bitmap, ctx,
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000324 &result, &offset), str.c_str());
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000325 REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000326 }
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000327
328 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
329 SkSafeUnref(filters[i]);
330 }
331}
332
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000333static SkBitmap make_gradient_circle(int width, int height) {
334 SkBitmap bitmap;
335 SkScalar x = SkIntToScalar(width / 2);
336 SkScalar y = SkIntToScalar(height / 2);
337 SkScalar radius = SkMinScalar(x, y) * 0.8f;
338 bitmap.allocN32Pixels(width, height);
339 SkCanvas canvas(bitmap);
340 canvas.clear(0x00000000);
341 SkColor colors[2];
342 colors[0] = SK_ColorWHITE;
343 colors[1] = SK_ColorBLACK;
344 SkAutoTUnref<SkShader> shader(
halcanary96fcdcc2015-08-27 07:41:13 -0700345 SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000346 SkShader::kClamp_TileMode)
347 );
348 SkPaint paint;
349 paint.setShader(shader);
350 canvas.drawCircle(x, y, radius, paint);
351 return bitmap;
352}
353
robertphillips9a53fd72015-06-22 09:46:59 -0700354static void test_negative_blur_sigma(SkImageFilter::Proxy* proxy, skiatest::Reporter* reporter) {
senorblanco32673b92014-09-09 09:15:04 -0700355 // Check that SkBlurImageFilter will accept a negative sigma, either in
356 // the given arguments or after CTM application.
reed5ea95df2015-10-06 14:05:32 -0700357 const int width = 32, height = 32;
358 const SkScalar five = SkIntToScalar(5);
senorblanco32673b92014-09-09 09:15:04 -0700359
reed5ea95df2015-10-06 14:05:32 -0700360 SkAutoTUnref<SkImageFilter> positiveFilter(SkBlurImageFilter::Create(five, five));
361 SkAutoTUnref<SkImageFilter> negativeFilter(SkBlurImageFilter::Create(-five, five));
senorblanco32673b92014-09-09 09:15:04 -0700362
363 SkBitmap gradient = make_gradient_circle(width, height);
364 SkBitmap positiveResult1, negativeResult1;
365 SkBitmap positiveResult2, negativeResult2;
366 SkIPoint offset;
senorblanco84449132015-11-25 10:32:18 -0800367 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, SkImageFilter::kApprox_SizeConstraint);
368 REPORTER_ASSERT(reporter, positiveFilter->filterImage(proxy, gradient, ctx, &positiveResult1, &offset));
369 REPORTER_ASSERT(reporter, negativeFilter->filterImage(proxy, gradient, ctx, &negativeResult1, &offset));
senorblanco32673b92014-09-09 09:15:04 -0700370 SkMatrix negativeScale;
371 negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
senorblanco84449132015-11-25 10:32:18 -0800372 SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeWH(32, 32), nullptr, SkImageFilter::kApprox_SizeConstraint);
373 REPORTER_ASSERT(reporter, positiveFilter->filterImage(proxy, gradient, negativeCTX, &negativeResult2, &offset));
374 REPORTER_ASSERT(reporter, negativeFilter->filterImage(proxy, gradient, negativeCTX, &positiveResult2, &offset));
senorblanco32673b92014-09-09 09:15:04 -0700375 SkAutoLockPixels lockP1(positiveResult1);
376 SkAutoLockPixels lockP2(positiveResult2);
377 SkAutoLockPixels lockN1(negativeResult1);
378 SkAutoLockPixels lockN2(negativeResult2);
379 for (int y = 0; y < height; y++) {
380 int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes());
381 REPORTER_ASSERT(reporter, !diffs);
382 if (diffs) {
383 break;
384 }
385 diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes());
386 REPORTER_ASSERT(reporter, !diffs);
387 if (diffs) {
388 break;
389 }
390 diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes());
391 REPORTER_ASSERT(reporter, !diffs);
392 if (diffs) {
393 break;
394 }
395 }
396}
397
398DEF_TEST(TestNegativeBlurSigma, reporter) {
robertphillips9a53fd72015-06-22 09:46:59 -0700399 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
400 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
401
402 SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
reed88d064d2015-10-12 11:30:02 -0700403 SkImageFilter::DeviceProxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -0700404
405 test_negative_blur_sigma(&proxy, reporter);
senorblanco32673b92014-09-09 09:15:04 -0700406}
407
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000408DEF_TEST(ImageFilterDrawTiled, reporter) {
409 // Check that all filters when drawn tiled (with subsequent clip rects) exactly
410 // match the same filters drawn with a single full-canvas bitmap draw.
411 // Tests pass by not asserting.
412
413 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
robertphillips3d32d762015-07-13 13:16:44 -0700414 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000415 SkScalar kernel[9] = {
416 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
417 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
418 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
419 };
reed5ea95df2015-10-06 14:05:32 -0700420 const SkISize kernelSize = SkISize::Make(3, 3);
421 const SkScalar gain = SK_Scalar1, bias = 0;
422 const SkScalar five = SkIntToScalar(5);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000423
fmalita5598b632015-09-15 11:26:13 -0700424 SkAutoTUnref<SkImage> gradientImage(SkImage::NewFromBitmap(make_gradient_circle(64, 64)));
425 SkAutoTUnref<SkImageFilter> gradientSource(SkImageSource::Create(gradientImage));
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000426 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000427 SkMatrix matrix;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000428
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000429 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
430 matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000431
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000432 SkRTreeFactory factory;
433 SkPictureRecorder recorder;
434 SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
435
436 SkPaint greenPaint;
437 greenPaint.setColor(SK_ColorGREEN);
438 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
439 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
440 SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
senorblanco8f3937d2014-10-29 12:36:32 -0700441 SkAutoTUnref<SkShader> shader(SkPerlinNoiseShader::CreateTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
442
443 SkAutoTUnref<SkImageFilter> rectShaderFilter(SkRectShaderImageFilter::Create(shader.get()));
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000444
fsd8b57712015-05-20 00:52:17 -0700445 SkAutoTUnref<SkShader> greenColorShader(SkShader::CreateColorShader(SK_ColorGREEN));
446 SkImageFilter::CropRect leftSideCropRect(SkRect::MakeXYWH(0, 0, 32, 64));
447 SkAutoTUnref<SkImageFilter> rectShaderFilterLeft(SkRectShaderImageFilter::Create(greenColorShader.get(), &leftSideCropRect));
448 SkImageFilter::CropRect rightSideCropRect(SkRect::MakeXYWH(32, 0, 32, 64));
449 SkAutoTUnref<SkImageFilter> rectShaderFilterRight(SkRectShaderImageFilter::Create(greenColorShader.get(), &rightSideCropRect));
450
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000451 struct {
452 const char* fName;
453 SkImageFilter* fFilter;
454 } filters[] = {
455 { "color filter", SkColorFilterImageFilter::Create(cf.get()) },
456 { "displacement map", SkDisplacementMapEffect::Create(
457 SkDisplacementMapEffect::kR_ChannelSelectorType,
458 SkDisplacementMapEffect::kB_ChannelSelectorType,
fmalita5598b632015-09-15 11:26:13 -0700459 20.0f, gradientSource.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000460 { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
461 { "drop shadow", SkDropShadowImageFilter::Create(
sugoi234f0362014-10-23 13:59:52 -0700462 SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
463 SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000464 { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
465 location, SK_ColorGREEN, 0, 0) },
466 { "specular lighting",
467 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
468 { "matrix convolution",
469 SkMatrixConvolutionImageFilter::Create(
470 kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
471 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
halcanary96fcdcc2015-08-27 07:41:13 -0700472 { "merge", SkMergeImageFilter::Create(nullptr, nullptr, SkXfermode::kSrcOver_Mode) },
fsd8b57712015-05-20 00:52:17 -0700473 { "merge with disjoint inputs", SkMergeImageFilter::Create(
474 rectShaderFilterLeft, rectShaderFilterRight, SkXfermode::kSrcOver_Mode) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000475 { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
476 { "dilate", SkDilateImageFilter::Create(3, 2) },
477 { "erode", SkErodeImageFilter::Create(2, 3) },
478 { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
halcanary96fcdcc2015-08-27 07:41:13 -0700479 SkRect::MakeXYWH(0, 0, 100, 100), nullptr) },
senorblanco8c874ee2015-03-20 06:38:17 -0700480 { "matrix", SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality) },
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000481 { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000482 { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
senorblanco8f3937d2014-10-29 12:36:32 -0700483 { "rect shader and blur", SkBlurImageFilter::Create(five, five, rectShaderFilter.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000484 };
485
486 SkBitmap untiledResult, tiledResult;
reed5ea95df2015-10-06 14:05:32 -0700487 const int width = 64, height = 64;
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000488 untiledResult.allocN32Pixels(width, height);
489 tiledResult.allocN32Pixels(width, height);
490 SkCanvas tiledCanvas(tiledResult);
491 SkCanvas untiledCanvas(untiledResult);
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000492 int tileSize = 8;
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000493
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000494 for (int scale = 1; scale <= 2; ++scale) {
495 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
496 tiledCanvas.clear(0);
497 untiledCanvas.clear(0);
498 SkPaint paint;
499 paint.setImageFilter(filters[i].fFilter);
500 paint.setTextSize(SkIntToScalar(height));
501 paint.setColor(SK_ColorWHITE);
502 SkString str;
503 const char* text = "ABC";
504 SkScalar ypos = SkIntToScalar(height);
505 untiledCanvas.save();
506 untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
507 untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
508 untiledCanvas.restore();
509 for (int y = 0; y < height; y += tileSize) {
510 for (int x = 0; x < width; x += tileSize) {
511 tiledCanvas.save();
512 tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
513 tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
514 tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
515 tiledCanvas.restore();
516 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000517 }
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000518 untiledCanvas.flush();
519 tiledCanvas.flush();
520 for (int y = 0; y < height; y++) {
521 int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
522 REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
523 if (diffs) {
524 break;
525 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000526 }
527 }
528 }
529
530 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
531 SkSafeUnref(filters[i].fFilter);
532 }
533}
534
mtklein3f3b3d02014-12-01 11:47:08 -0800535static void draw_saveLayer_picture(int width, int height, int tileSize,
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700536 SkBBHFactory* factory, SkBitmap* result) {
mtkleind910f542014-08-22 09:06:34 -0700537
538 SkMatrix matrix;
539 matrix.setTranslate(SkIntToScalar(50), 0);
540
541 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
542 SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
senorblanco8c874ee2015-03-20 06:38:17 -0700543 SkAutoTUnref<SkImageFilter> imageFilter(SkImageFilter::CreateMatrixFilter(matrix, kNone_SkFilterQuality, cfif.get()));
mtkleind910f542014-08-22 09:06:34 -0700544
545 SkPaint paint;
546 paint.setImageFilter(imageFilter.get());
547 SkPictureRecorder recorder;
548 SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
mtklein3f3b3d02014-12-01 11:47:08 -0800549 SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
550 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700551 factory, 0);
mtkleind910f542014-08-22 09:06:34 -0700552 recordingCanvas->translate(-55, 0);
553 recordingCanvas->saveLayer(&bounds, &paint);
554 recordingCanvas->restore();
555 SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
556
557 result->allocN32Pixels(width, height);
558 SkCanvas canvas(*result);
559 canvas.clear(0);
560 canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
561 canvas.drawPicture(picture1.get());
562}
563
564DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
565 // Check that matrix filter when drawn tiled with BBH exactly
566 // matches the same thing drawn without BBH.
567 // Tests pass by not asserting.
568
569 const int width = 200, height = 200;
570 const int tileSize = 100;
571 SkBitmap result1, result2;
572 SkRTreeFactory factory;
573
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700574 draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
halcanary96fcdcc2015-08-27 07:41:13 -0700575 draw_saveLayer_picture(width, height, tileSize, nullptr, &result2);
mtkleind910f542014-08-22 09:06:34 -0700576
577 for (int y = 0; y < height; y++) {
578 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
579 REPORTER_ASSERT(reporter, !diffs);
580 if (diffs) {
581 break;
582 }
583 }
584}
585
halcanary96fcdcc2015-08-27 07:41:13 -0700586static SkImageFilter* makeBlur(SkImageFilter* input = nullptr) {
senorblanco1150a6d2014-08-25 12:46:58 -0700587 return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input);
588}
589
halcanary96fcdcc2015-08-27 07:41:13 -0700590static SkImageFilter* makeDropShadow(SkImageFilter* input = nullptr) {
senorblanco1150a6d2014-08-25 12:46:58 -0700591 return SkDropShadowImageFilter::Create(
592 SkIntToScalar(100), SkIntToScalar(100),
593 SkIntToScalar(10), SkIntToScalar(10),
sugoi234f0362014-10-23 13:59:52 -0700594 SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
halcanary96fcdcc2015-08-27 07:41:13 -0700595 input, nullptr);
senorblanco1150a6d2014-08-25 12:46:58 -0700596}
597
598DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
599 SkAutoTUnref<SkImageFilter> filter1(makeBlur());
600 SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
601
602 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
603 SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
604 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
605
606 REPORTER_ASSERT(reporter, bounds == expectedBounds);
607}
608
609DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
610 SkAutoTUnref<SkImageFilter> filter1(makeDropShadow());
611 SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get()));
612
613 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
614 SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
615 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
616
617 REPORTER_ASSERT(reporter, bounds == expectedBounds);
618}
619
620DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
621 SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2));
622 SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
623
624 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
625 SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
626 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
627
628 REPORTER_ASSERT(reporter, bounds == expectedBounds);
629}
630
ajuma5788faa2015-02-13 09:05:47 -0800631DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
632 SkAutoTUnref<SkImageFilter> filter1(makeBlur());
633 SkAutoTUnref<SkImageFilter> filter2(makeBlur());
634 SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(filter1.get(), filter2.get()));
635
636 SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
637 SkRect expectedBounds = SkRect::MakeXYWH(
638 SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
639 SkRect boundsDst = SkRect::MakeEmpty();
640 composedFilter->computeFastBounds(boundsSrc, &boundsDst);
641
642 REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
643}
644
senorblanco4a243982015-11-25 07:06:55 -0800645DEF_TEST(ImageFilterMergeResultSize, reporter) {
646 SkBitmap greenBM;
647 greenBM.allocN32Pixels(20, 20);
648 greenBM.eraseColor(SK_ColorGREEN);
649 SkAutoTUnref<SkImage> greenImage(SkImage::NewFromBitmap(greenBM));
650 SkAutoTUnref<SkImageFilter> source(SkImageSource::Create(greenImage.get()));
651 SkAutoTUnref<SkImageFilter> merge(SkMergeImageFilter::Create(source.get(), source.get()));
652
653 SkBitmap bitmap;
654 bitmap.allocN32Pixels(1, 1);
655 bitmap.eraseColor(0);
656 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
657 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
658 SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
659 SkImageFilter::DeviceProxy proxy(device);
660 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr,
661 SkImageFilter::kApprox_SizeConstraint);
662 SkBitmap result;
663 SkIPoint offset;
664 REPORTER_ASSERT(reporter, merge->filterImage(&proxy, bitmap, ctx, &result, &offset));
665 REPORTER_ASSERT(reporter, result.width() == 20 && result.height() == 20);
666}
667
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700668static void draw_blurred_rect(SkCanvas* canvas) {
senorblanco837f5322014-07-14 10:19:54 -0700669 SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
670 SkPaint filterPaint;
671 filterPaint.setColor(SK_ColorWHITE);
672 filterPaint.setImageFilter(filter);
halcanary96fcdcc2015-08-27 07:41:13 -0700673 canvas->saveLayer(nullptr, &filterPaint);
senorblanco837f5322014-07-14 10:19:54 -0700674 SkPaint whitePaint;
675 whitePaint.setColor(SK_ColorWHITE);
676 canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
677 canvas->restore();
678}
679
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700680static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
senorblanco837f5322014-07-14 10:19:54 -0700681 canvas->save();
682 canvas->clipRect(clipRect);
683 canvas->drawPicture(picture);
684 canvas->restore();
685}
686
687DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
688 // Check that the blur filter when recorded with RTree acceleration,
689 // and drawn tiled (with subsequent clip rects) exactly
690 // matches the same filter drawn with without RTree acceleration.
691 // This tests that the "bleed" from the blur into the otherwise-blank
692 // tiles is correctly rendered.
693 // Tests pass by not asserting.
694
695 int width = 16, height = 8;
696 SkBitmap result1, result2;
697 result1.allocN32Pixels(width, height);
698 result2.allocN32Pixels(width, height);
699 SkCanvas canvas1(result1);
700 SkCanvas canvas2(result2);
701 int tileSize = 8;
702
703 canvas1.clear(0);
704 canvas2.clear(0);
705
706 SkRTreeFactory factory;
707
708 SkPictureRecorder recorder1, recorder2;
709 // The only difference between these two pictures is that one has RTree aceleration.
mtklein3f3b3d02014-12-01 11:47:08 -0800710 SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
711 SkIntToScalar(height),
halcanary96fcdcc2015-08-27 07:41:13 -0700712 nullptr, 0);
mtklein3f3b3d02014-12-01 11:47:08 -0800713 SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
714 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700715 &factory, 0);
716 draw_blurred_rect(recordingCanvas1);
717 draw_blurred_rect(recordingCanvas2);
senorblanco837f5322014-07-14 10:19:54 -0700718 SkAutoTUnref<SkPicture> picture1(recorder1.endRecording());
719 SkAutoTUnref<SkPicture> picture2(recorder2.endRecording());
720 for (int y = 0; y < height; y += tileSize) {
721 for (int x = 0; x < width; x += tileSize) {
722 SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700723 draw_picture_clipped(&canvas1, tileRect, picture1);
724 draw_picture_clipped(&canvas2, tileRect, picture2);
senorblanco837f5322014-07-14 10:19:54 -0700725 }
726 }
727 for (int y = 0; y < height; y++) {
728 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
729 REPORTER_ASSERT(reporter, !diffs);
730 if (diffs) {
731 break;
732 }
733 }
734}
735
senorblanco@chromium.org91957432014-05-01 14:03:41 +0000736DEF_TEST(ImageFilterMatrixConvolution, reporter) {
737 // Check that a 1x3 filter does not cause a spurious assert.
738 SkScalar kernel[3] = {
739 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
740 };
741 SkISize kernelSize = SkISize::Make(1, 3);
742 SkScalar gain = SK_Scalar1, bias = 0;
743 SkIPoint kernelOffset = SkIPoint::Make(0, 0);
744
745 SkAutoTUnref<SkImageFilter> filter(
746 SkMatrixConvolutionImageFilter::Create(
747 kernelSize, kernel, gain, bias, kernelOffset,
748 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
749
750 SkBitmap result;
751 int width = 16, height = 16;
752 result.allocN32Pixels(width, height);
753 SkCanvas canvas(result);
754 canvas.clear(0);
755
756 SkPaint paint;
757 paint.setImageFilter(filter);
758 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
759 canvas.drawRect(rect, paint);
760}
761
senorblanco@chromium.org8c7372b2014-05-02 19:13:11 +0000762DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
763 // Check that a filter with borders outside the target bounds
764 // does not crash.
765 SkScalar kernel[3] = {
766 0, 0, 0,
767 };
768 SkISize kernelSize = SkISize::Make(3, 1);
769 SkScalar gain = SK_Scalar1, bias = 0;
770 SkIPoint kernelOffset = SkIPoint::Make(2, 0);
771
772 SkAutoTUnref<SkImageFilter> filter(
773 SkMatrixConvolutionImageFilter::Create(
774 kernelSize, kernel, gain, bias, kernelOffset,
775 SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
776
777 SkBitmap result;
778
779 int width = 10, height = 10;
780 result.allocN32Pixels(width, height);
781 SkCanvas canvas(result);
782 canvas.clear(0);
783
784 SkPaint filterPaint;
785 filterPaint.setImageFilter(filter);
786 SkRect bounds = SkRect::MakeWH(1, 10);
787 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
788 SkPaint rectPaint;
789 canvas.saveLayer(&bounds, &filterPaint);
790 canvas.drawRect(rect, rectPaint);
791 canvas.restore();
792}
793
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000794DEF_TEST(ImageFilterCropRect, reporter) {
robertphillips9a53fd72015-06-22 09:46:59 -0700795 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
796 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
797
798 SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
reed88d064d2015-10-12 11:30:02 -0700799 SkImageFilter::DeviceProxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -0700800
801 test_crop_rects(&proxy, reporter);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000802}
803
tfarina9ea53f92014-06-24 06:50:39 -0700804DEF_TEST(ImageFilterMatrix, reporter) {
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000805 SkBitmap temp;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000806 temp.allocN32Pixels(100, 100);
robertphillips9a53fd72015-06-22 09:46:59 -0700807 SkCanvas canvas(temp);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000808 canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
809
810 SkMatrix expectedMatrix = canvas.getTotalMatrix();
811
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000812 SkRTreeFactory factory;
813 SkPictureRecorder recorder;
814 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000815
816 SkPaint paint;
817 SkAutoTUnref<MatrixTestImageFilter> imageFilter(
818 new MatrixTestImageFilter(reporter, expectedMatrix));
819 paint.setImageFilter(imageFilter.get());
halcanary96fcdcc2015-08-27 07:41:13 -0700820 recordingCanvas->saveLayer(nullptr, &paint);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000821 SkPaint solidPaint;
822 solidPaint.setColor(0xFFFFFFFF);
823 recordingCanvas->save();
824 recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
825 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
826 recordingCanvas->restore(); // scale
827 recordingCanvas->restore(); // saveLayer
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000828 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000829
robertphillips9b14f262014-06-04 05:40:44 -0700830 canvas.drawPicture(picture);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000831}
832
senorblanco3d822c22014-07-30 14:49:31 -0700833DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000834 SkRTreeFactory factory;
835 SkPictureRecorder recorder;
836 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
837
838 // Create an SkPicture which simply draws a green 1x1 rectangle.
839 SkPaint greenPaint;
840 greenPaint.setColor(SK_ColorGREEN);
841 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
842 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
843
844 // Wrap that SkPicture in an SkPictureImageFilter.
845 SkAutoTUnref<SkImageFilter> imageFilter(
846 SkPictureImageFilter::Create(picture.get()));
847
848 // Check that SkPictureImageFilter successfully serializes its contained
849 // SkPicture when not in cross-process mode.
850 SkPaint paint;
851 paint.setImageFilter(imageFilter.get());
852 SkPictureRecorder outerRecorder;
853 SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
854 SkPaint redPaintWithFilter;
855 redPaintWithFilter.setColor(SK_ColorRED);
856 redPaintWithFilter.setImageFilter(imageFilter.get());
857 outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
858 SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
859
860 SkBitmap bitmap;
861 bitmap.allocN32Pixels(1, 1);
robertphillips9a53fd72015-06-22 09:46:59 -0700862 SkCanvas canvas(bitmap);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000863
864 // The result here should be green, since the filter replaces the primitive's red interior.
865 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700866 canvas.drawPicture(outerPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000867 uint32_t pixel = *bitmap.getAddr32(0, 0);
868 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
869
870 // Check that, for now, SkPictureImageFilter does not serialize or
871 // deserialize its contained picture when the filter is serialized
872 // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
873 SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
874 SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
875 data->data(), data->size(), SkImageFilter::GetFlattenableType()));
876 SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
877
878 redPaintWithFilter.setImageFilter(unflattenedFilter);
879 SkPictureRecorder crossProcessRecorder;
880 SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
881 crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
882 SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
883
884 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700885 canvas.drawPicture(crossProcessPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000886 pixel = *bitmap.getAddr32(0, 0);
hendrikw446ee672015-06-16 09:28:37 -0700887 // If the security precautions are enabled, the result here should not be green, since the
888 // filter draws nothing.
889 REPORTER_ASSERT(reporter, SkPicture::PictureIOSecurityPrecautionsEnabled()
890 ? pixel != SK_ColorGREEN : pixel == SK_ColorGREEN);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000891}
892
senorblanco3d822c22014-07-30 14:49:31 -0700893DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
894 SkRTreeFactory factory;
895 SkPictureRecorder recorder;
896 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
897
898 // Create an SkPicture which simply draws a green 1x1 rectangle.
899 SkPaint greenPaint;
900 greenPaint.setColor(SK_ColorGREEN);
901 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
902 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
903
robertphillips9a53fd72015-06-22 09:46:59 -0700904 SkAutoTUnref<SkImageFilter> imageFilter(SkPictureImageFilter::Create(picture.get()));
senorblanco3d822c22014-07-30 14:49:31 -0700905
906 SkBitmap result;
907 SkIPoint offset;
reedc9b5f8b2015-10-22 13:20:20 -0700908 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr, SkImageFilter::kApprox_SizeConstraint);
senorblanco3d822c22014-07-30 14:49:31 -0700909 SkBitmap bitmap;
910 bitmap.allocN32Pixels(2, 2);
robertphillipsefbffed2015-06-22 12:06:08 -0700911 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
912 SkBitmapDevice device(bitmap, props);
reed88d064d2015-10-12 11:30:02 -0700913 SkImageFilter::DeviceProxy proxy(&device);
senorblanco3d822c22014-07-30 14:49:31 -0700914 REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
915}
916
tfarina9ea53f92014-06-24 06:50:39 -0700917DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000918 // Even when there's an empty saveLayer()/restore(), ensure that an image
919 // filter or color filter which affects transparent black still draws.
920
921 SkBitmap bitmap;
922 bitmap.allocN32Pixels(10, 10);
robertphillips9a53fd72015-06-22 09:46:59 -0700923 SkCanvas canvas(bitmap);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000924
925 SkRTreeFactory factory;
926 SkPictureRecorder recorder;
927
928 SkAutoTUnref<SkColorFilter> green(
929 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
reedf0280032015-10-12 11:10:10 -0700930 SkAutoTUnref<SkImageFilter> imageFilter(
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000931 SkColorFilterImageFilter::Create(green.get()));
932 SkPaint imageFilterPaint;
933 imageFilterPaint.setImageFilter(imageFilter.get());
934 SkPaint colorFilterPaint;
935 colorFilterPaint.setColorFilter(green.get());
936
937 SkRect bounds = SkRect::MakeWH(10, 10);
938
939 SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
940 recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
941 recordingCanvas->restore();
942 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
943
944 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700945 canvas.drawPicture(picture);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000946 uint32_t pixel = *bitmap.getAddr32(0, 0);
947 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
948
949 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700950 recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000951 recordingCanvas->restore();
952 SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
953
954 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700955 canvas.drawPicture(picture2);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000956 pixel = *bitmap.getAddr32(0, 0);
957 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
958
959 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
960 recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
961 recordingCanvas->restore();
962 SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
963
964 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700965 canvas.drawPicture(picture3);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000966 pixel = *bitmap.getAddr32(0, 0);
967 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
968}
969
robertphillips9a53fd72015-06-22 09:46:59 -0700970static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) {
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000971 SkBitmap bitmap;
972 bitmap.allocN32Pixels(100, 100);
973 bitmap.eraseARGB(0, 0, 0, 0);
974
975 // Check that a blur with an insane radius does not crash or assert.
976 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
977
978 SkPaint paint;
979 paint.setImageFilter(blur);
robertphillips9a53fd72015-06-22 09:46:59 -0700980 canvas->drawSprite(bitmap, 0, 0, &paint);
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000981}
982
983DEF_TEST(HugeBlurImageFilter, reporter) {
984 SkBitmap temp;
985 temp.allocN32Pixels(100, 100);
robertphillips9a53fd72015-06-22 09:46:59 -0700986 SkCanvas canvas(temp);
987 test_huge_blur(&canvas, reporter);
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000988}
989
senorblanco3a495202014-09-29 07:57:20 -0700990DEF_TEST(MatrixConvolutionSanityTest, reporter) {
991 SkScalar kernel[1] = { 0 };
992 SkScalar gain = SK_Scalar1, bias = 0;
993 SkIPoint kernelOffset = SkIPoint::Make(1, 1);
994
halcanary96fcdcc2015-08-27 07:41:13 -0700995 // Check that an enormous (non-allocatable) kernel gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -0700996 SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create(
997 SkISize::Make(1<<30, 1<<30),
998 kernel,
999 gain,
1000 bias,
1001 kernelOffset,
1002 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
1003 false));
1004
halcanary96fcdcc2015-08-27 07:41:13 -07001005 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001006
halcanary96fcdcc2015-08-27 07:41:13 -07001007 // Check that a nullptr kernel gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -07001008 conv.reset(SkMatrixConvolutionImageFilter::Create(
1009 SkISize::Make(1, 1),
halcanary96fcdcc2015-08-27 07:41:13 -07001010 nullptr,
senorblanco3a495202014-09-29 07:57:20 -07001011 gain,
1012 bias,
1013 kernelOffset,
1014 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
1015 false));
1016
halcanary96fcdcc2015-08-27 07:41:13 -07001017 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001018
halcanary96fcdcc2015-08-27 07:41:13 -07001019 // Check that a kernel width < 1 gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -07001020 conv.reset(SkMatrixConvolutionImageFilter::Create(
1021 SkISize::Make(0, 1),
1022 kernel,
1023 gain,
1024 bias,
1025 kernelOffset,
1026 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
1027 false));
1028
halcanary96fcdcc2015-08-27 07:41:13 -07001029 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001030
halcanary96fcdcc2015-08-27 07:41:13 -07001031 // Check that kernel height < 1 gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -07001032 conv.reset(SkMatrixConvolutionImageFilter::Create(
1033 SkISize::Make(1, -1),
1034 kernel,
1035 gain,
1036 bias,
1037 kernelOffset,
1038 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
1039 false));
1040
halcanary96fcdcc2015-08-27 07:41:13 -07001041 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001042}
1043
robertphillips9a53fd72015-06-22 09:46:59 -07001044static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* reporter) {
1045 canvas->clear(0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001046
1047 SkBitmap bitmap;
1048 bitmap.allocN32Pixels(1, 1);
1049 bitmap.eraseARGB(255, 255, 255, 255);
1050
1051 SkAutoTUnref<SkColorFilter> green(
1052 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
reedf0280032015-10-12 11:10:10 -07001053 SkAutoTUnref<SkImageFilter> greenFilter(SkColorFilterImageFilter::Create(green.get()));
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001054 SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
reedf0280032015-10-12 11:10:10 -07001055 SkAutoTUnref<SkImageFilter> croppedOut(
halcanary96fcdcc2015-08-27 07:41:13 -07001056 SkColorFilterImageFilter::Create(green.get(), nullptr, &cropRect));
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001057
1058 // Check that an xfermode image filter whose input has been cropped out still draws the other
1059 // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
1060 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
1061 SkAutoTUnref<SkImageFilter> xfermodeNoFg(
1062 SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
1063 SkAutoTUnref<SkImageFilter> xfermodeNoBg(
1064 SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
1065 SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
1066 SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
1067
1068 SkPaint paint;
1069 paint.setImageFilter(xfermodeNoFg);
robertphillips9a53fd72015-06-22 09:46:59 -07001070 canvas->drawSprite(bitmap, 0, 0, &paint);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001071
1072 uint32_t pixel;
kkinnunena9d9a392015-03-06 07:16:00 -08001073 SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
robertphillips9a53fd72015-06-22 09:46:59 -07001074 canvas->readPixels(info, &pixel, 4, 0, 0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001075 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1076
1077 paint.setImageFilter(xfermodeNoBg);
robertphillips9a53fd72015-06-22 09:46:59 -07001078 canvas->drawSprite(bitmap, 0, 0, &paint);
1079 canvas->readPixels(info, &pixel, 4, 0, 0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001080 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1081
1082 paint.setImageFilter(xfermodeNoFgNoBg);
robertphillips9a53fd72015-06-22 09:46:59 -07001083 canvas->drawSprite(bitmap, 0, 0, &paint);
1084 canvas->readPixels(info, &pixel, 4, 0, 0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001085 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1086}
1087
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001088DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
1089 SkBitmap temp;
1090 temp.allocN32Pixels(50, 50);
robertphillips9a53fd72015-06-22 09:46:59 -07001091 SkCanvas canvas(temp);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001092 canvas.clear(0x0);
1093
1094 SkBitmap bitmap;
1095 bitmap.allocN32Pixels(10, 10);
1096 bitmap.eraseColor(SK_ColorGREEN);
1097
1098 SkMatrix matrix;
1099 matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
1100 matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
1101 SkAutoTUnref<SkImageFilter> matrixFilter(
senorblanco8c874ee2015-03-20 06:38:17 -07001102 SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality));
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001103
1104 // Test that saveLayer() with a filter nested inside another saveLayer() applies the
1105 // correct offset to the filter matrix.
1106 SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
halcanary96fcdcc2015-08-27 07:41:13 -07001107 canvas.saveLayer(&bounds1, nullptr);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001108 SkPaint filterPaint;
1109 filterPaint.setImageFilter(matrixFilter);
1110 SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
1111 canvas.saveLayer(&bounds2, &filterPaint);
1112 SkPaint greenPaint;
1113 greenPaint.setColor(SK_ColorGREEN);
1114 canvas.drawRect(bounds2, greenPaint);
1115 canvas.restore();
1116 canvas.restore();
1117 SkPaint strokePaint;
1118 strokePaint.setStyle(SkPaint::kStroke_Style);
1119 strokePaint.setColor(SK_ColorRED);
1120
kkinnunena9d9a392015-03-06 07:16:00 -08001121 SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001122 uint32_t pixel;
1123 canvas.readPixels(info, &pixel, 4, 25, 25);
1124 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1125
1126 // Test that drawSprite() with a filter nested inside a saveLayer() applies the
1127 // correct offset to the filter matrix.
1128 canvas.clear(0x0);
1129 canvas.readPixels(info, &pixel, 4, 25, 25);
halcanary96fcdcc2015-08-27 07:41:13 -07001130 canvas.saveLayer(&bounds1, nullptr);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001131 canvas.drawSprite(bitmap, 20, 20, &filterPaint);
1132 canvas.restore();
1133
1134 canvas.readPixels(info, &pixel, 4, 25, 25);
1135 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1136}
1137
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001138DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
1139 SkBitmap temp;
1140 temp.allocN32Pixels(100, 100);
robertphillips9a53fd72015-06-22 09:46:59 -07001141 SkCanvas canvas(temp);
1142 test_xfermode_cropped_input(&canvas, reporter);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001143}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001144
ajuma5788faa2015-02-13 09:05:47 -08001145DEF_TEST(ComposedImageFilterOffset, reporter) {
1146 SkBitmap bitmap;
1147 bitmap.allocN32Pixels(100, 100);
1148 bitmap.eraseARGB(0, 0, 0, 0);
robertphillipsefbffed2015-06-22 12:06:08 -07001149 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1150 SkBitmapDevice device(bitmap, props);
reed88d064d2015-10-12 11:30:02 -07001151 SkImageFilter::DeviceProxy proxy(&device);
ajuma5788faa2015-02-13 09:05:47 -08001152
1153 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
halcanary96fcdcc2015-08-27 07:41:13 -07001154 SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect));
ajuma8e8c9402015-02-13 10:15:46 -08001155 SkAutoTUnref<SkImageFilter> blurFilter(makeBlur());
1156 SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter, offsetFilter.get()));
ajuma5788faa2015-02-13 09:05:47 -08001157 SkBitmap result;
1158 SkIPoint offset;
senorblanco84449132015-11-25 10:32:18 -08001159 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
ajuma5788faa2015-02-13 09:05:47 -08001160 REPORTER_ASSERT(reporter, composedFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
1161 REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
1162}
1163
senorblanco24d2a7b2015-07-13 10:27:05 -07001164DEF_TEST(PartialCropRect, reporter) {
1165 SkBitmap bitmap;
1166 bitmap.allocN32Pixels(100, 100);
1167 bitmap.eraseARGB(0, 0, 0, 0);
1168 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1169 SkBitmapDevice device(bitmap, props);
reed88d064d2015-10-12 11:30:02 -07001170 SkImageFilter::DeviceProxy proxy(&device);
senorblanco24d2a7b2015-07-13 10:27:05 -07001171
1172 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30),
senorblancoed7cf272015-07-16 15:19:11 -07001173 SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge);
halcanary96fcdcc2015-08-27 07:41:13 -07001174 SkAutoTUnref<SkImageFilter> filter(make_grayscale(nullptr, &cropRect));
senorblanco24d2a7b2015-07-13 10:27:05 -07001175 SkBitmap result;
1176 SkIPoint offset;
senorblanco84449132015-11-25 10:32:18 -08001177 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
senorblanco24d2a7b2015-07-13 10:27:05 -07001178 REPORTER_ASSERT(reporter, filter->filterImage(&proxy, bitmap, ctx, &result, &offset));
1179 REPORTER_ASSERT(reporter, offset.fX == 0);
1180 REPORTER_ASSERT(reporter, offset.fY == 0);
1181 REPORTER_ASSERT(reporter, result.width() == 20);
1182 REPORTER_ASSERT(reporter, result.height() == 30);
1183}
1184
senorblanco0abdf762015-08-20 11:10:41 -07001185DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
1186
1187 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
1188 SkAutoTUnref<SkImageFilter> lighting(SkLightingImageFilter::CreatePointLitDiffuse(
1189 location, SK_ColorGREEN, 0, 0));
1190 REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
1191
1192 SkAutoTUnref<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
1193 REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
1194 {
1195 SkColorFilter* grayCF;
1196 REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
1197 REPORTER_ASSERT(reporter, !grayCF->affectsTransparentBlack());
1198 grayCF->unref();
1199 }
1200 REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
1201
1202 SkAutoTUnref<SkImageFilter> grayBlur(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, gray.get()));
1203 REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
1204
1205 SkScalar greenMatrix[20] = { 0, 0, 0, 0, 0,
1206 0, 0, 0, 0, 1,
1207 0, 0, 0, 0, 0,
1208 0, 0, 0, 0, 1 };
1209 SkAutoTUnref<SkColorFilter> greenCF(SkColorMatrixFilter::Create(greenMatrix));
1210 SkAutoTUnref<SkImageFilter> green(SkColorFilterImageFilter::Create(greenCF));
1211
1212 REPORTER_ASSERT(reporter, greenCF->affectsTransparentBlack());
1213 REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
1214
1215 SkAutoTUnref<SkImageFilter> greenBlur(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, green.get()));
1216 REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
1217
1218 uint8_t allOne[256], identity[256];
1219 for (int i = 0; i < 256; ++i) {
1220 identity[i] = i;
1221 allOne[i] = 255;
1222 }
1223
1224 SkAutoTUnref<SkColorFilter> identityCF(
1225 SkTableColorFilter::CreateARGB(identity, identity, identity, allOne));
1226 SkAutoTUnref<SkImageFilter> identityFilter(SkColorFilterImageFilter::Create(identityCF.get()));
1227 REPORTER_ASSERT(reporter, !identityCF->affectsTransparentBlack());
1228 REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
1229
1230 SkAutoTUnref<SkColorFilter> forceOpaqueCF(
1231 SkTableColorFilter::CreateARGB(allOne, identity, identity, identity));
1232 SkAutoTUnref<SkImageFilter> forceOpaque(SkColorFilterImageFilter::Create(forceOpaqueCF.get()));
1233 REPORTER_ASSERT(reporter, forceOpaqueCF->affectsTransparentBlack());
1234 REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
1235}
1236
fmalitacd56f812015-09-14 13:31:18 -07001237// Verify that SkImageSource survives serialization
1238DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
1239 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10));
1240 surface->getCanvas()->clear(SK_ColorGREEN);
1241 SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
1242 SkAutoTUnref<SkImageFilter> filter(SkImageSource::Create(image));
1243
1244 SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(filter));
1245 SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
1246 data->data(), data->size(), SkImageFilter::GetFlattenableType()));
1247 SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
1248 REPORTER_ASSERT(reporter, unflattenedFilter);
1249
1250 SkBitmap bm;
1251 bm.allocN32Pixels(10, 10);
fmalita23cb88c2015-09-15 06:56:23 -07001252 bm.eraseColor(SK_ColorBLUE);
fmalitacd56f812015-09-14 13:31:18 -07001253 SkPaint paint;
1254 paint.setColor(SK_ColorRED);
1255 paint.setImageFilter(unflattenedFilter);
1256
1257 SkCanvas canvas(bm);
1258 canvas.drawRect(SkRect::MakeWH(10, 10), paint);
1259 REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
1260}
1261
senorblanco@chromium.org58d14662014-02-03 22:36:39 +00001262#if SK_SUPPORT_GPU
reed4a8126e2014-09-22 07:29:03 -07001263
kkinnunen15302832015-12-01 04:35:26 -08001264DEF_GPUTEST_FOR_NATIVE_CONTEXT(ImageFilterCropRect_Gpu, reporter, context) {
robertphillipsefbffed2015-06-22 12:06:08 -07001265 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1266
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001267 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001268 SkSurface::kNo_Budgeted,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001269 SkImageInfo::MakeN32Premul(100, 100),
bsalomonafe30052015-01-16 07:32:33 -08001270 0,
bsalomon74f681d2015-06-23 14:38:48 -07001271 &props,
1272 SkGpuDevice::kUninit_InitContents));
reed88d064d2015-10-12 11:30:02 -07001273 SkImageFilter::DeviceProxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -07001274
1275 test_crop_rects(&proxy, reporter);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +00001276}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001277
kkinnunen15302832015-12-01 04:35:26 -08001278DEF_GPUTEST_FOR_NATIVE_CONTEXT(HugeBlurImageFilter_Gpu, reporter, context) {
robertphillipsefbffed2015-06-22 12:06:08 -07001279 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1280
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001281 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001282 SkSurface::kNo_Budgeted,
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001283 SkImageInfo::MakeN32Premul(100, 100),
bsalomonafe30052015-01-16 07:32:33 -08001284 0,
bsalomon74f681d2015-06-23 14:38:48 -07001285 &props,
1286 SkGpuDevice::kUninit_InitContents));
robertphillips9a53fd72015-06-22 09:46:59 -07001287 SkCanvas canvas(device);
1288
1289 test_huge_blur(&canvas, reporter);
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001290}
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001291
kkinnunen15302832015-12-01 04:35:26 -08001292DEF_GPUTEST_FOR_NATIVE_CONTEXT(XfermodeImageFilterCroppedInput_Gpu, reporter, context) {
robertphillipsefbffed2015-06-22 12:06:08 -07001293 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1294
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001295 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001296 SkSurface::kNo_Budgeted,
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001297 SkImageInfo::MakeN32Premul(1, 1),
bsalomonafe30052015-01-16 07:32:33 -08001298 0,
bsalomon74f681d2015-06-23 14:38:48 -07001299 &props,
1300 SkGpuDevice::kUninit_InitContents));
robertphillips9a53fd72015-06-22 09:46:59 -07001301 SkCanvas canvas(device);
1302
1303 test_xfermode_cropped_input(&canvas, reporter);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001304}
senorblanco32673b92014-09-09 09:15:04 -07001305
kkinnunen15302832015-12-01 04:35:26 -08001306DEF_GPUTEST_FOR_NATIVE_CONTEXT(TestNegativeBlurSigma_Gpu, reporter, context) {
robertphillipsefbffed2015-06-22 12:06:08 -07001307 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1308
senorblanco32673b92014-09-09 09:15:04 -07001309 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001310 SkSurface::kNo_Budgeted,
senorblanco32673b92014-09-09 09:15:04 -07001311 SkImageInfo::MakeN32Premul(1, 1),
bsalomonafe30052015-01-16 07:32:33 -08001312 0,
bsalomon74f681d2015-06-23 14:38:48 -07001313 &props,
1314 SkGpuDevice::kUninit_InitContents));
reed88d064d2015-10-12 11:30:02 -07001315 SkImageFilter::DeviceProxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -07001316
1317 test_negative_blur_sigma(&proxy, reporter);
senorblanco32673b92014-09-09 09:15:04 -07001318}
senorblanco@chromium.org58d14662014-02-03 22:36:39 +00001319#endif