blob: b1a017cc52c7b20c9400080c725a08aad81b9d24 [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
41#include "GrContextFactory.h"
42#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));
halcanary96fcdcc2015-08-27 07:41:13 -0700322 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), nullptr);
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.
357 int width = 32, height = 32;
senorblanco32673b92014-09-09 09:15:04 -0700358 SkScalar five = SkIntToScalar(5);
359
360 SkAutoTUnref<SkBlurImageFilter> positiveFilter(
361 SkBlurImageFilter::Create(five, five)
362 );
363
364 SkAutoTUnref<SkBlurImageFilter> negativeFilter(
365 SkBlurImageFilter::Create(-five, five)
366 );
367
368 SkBitmap gradient = make_gradient_circle(width, height);
369 SkBitmap positiveResult1, negativeResult1;
370 SkBitmap positiveResult2, negativeResult2;
371 SkIPoint offset;
halcanary96fcdcc2015-08-27 07:41:13 -0700372 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), nullptr);
robertphillips9a53fd72015-06-22 09:46:59 -0700373 positiveFilter->filterImage(proxy, gradient, ctx, &positiveResult1, &offset);
374 negativeFilter->filterImage(proxy, gradient, ctx, &negativeResult1, &offset);
senorblanco32673b92014-09-09 09:15:04 -0700375 SkMatrix negativeScale;
376 negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
halcanary96fcdcc2015-08-27 07:41:13 -0700377 SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeLargest(), nullptr);
robertphillips9a53fd72015-06-22 09:46:59 -0700378 positiveFilter->filterImage(proxy, gradient, negativeCTX, &negativeResult2, &offset);
379 negativeFilter->filterImage(proxy, gradient, negativeCTX, &positiveResult2, &offset);
senorblanco32673b92014-09-09 09:15:04 -0700380 SkAutoLockPixels lockP1(positiveResult1);
381 SkAutoLockPixels lockP2(positiveResult2);
382 SkAutoLockPixels lockN1(negativeResult1);
383 SkAutoLockPixels lockN2(negativeResult2);
384 for (int y = 0; y < height; y++) {
385 int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes());
386 REPORTER_ASSERT(reporter, !diffs);
387 if (diffs) {
388 break;
389 }
390 diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes());
391 REPORTER_ASSERT(reporter, !diffs);
392 if (diffs) {
393 break;
394 }
395 diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes());
396 REPORTER_ASSERT(reporter, !diffs);
397 if (diffs) {
398 break;
399 }
400 }
401}
402
403DEF_TEST(TestNegativeBlurSigma, reporter) {
robertphillips9a53fd72015-06-22 09:46:59 -0700404 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
405 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
406
407 SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
robertphillipsefbffed2015-06-22 12:06:08 -0700408 SkImageFilter::Proxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -0700409
410 test_negative_blur_sigma(&proxy, reporter);
senorblanco32673b92014-09-09 09:15:04 -0700411}
412
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000413DEF_TEST(ImageFilterDrawTiled, reporter) {
414 // Check that all filters when drawn tiled (with subsequent clip rects) exactly
415 // match the same filters drawn with a single full-canvas bitmap draw.
416 // Tests pass by not asserting.
417
418 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
robertphillips3d32d762015-07-13 13:16:44 -0700419 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000420 SkScalar kernel[9] = {
421 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
422 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
423 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
424 };
425 SkISize kernelSize = SkISize::Make(3, 3);
426 SkScalar gain = SK_Scalar1, bias = 0;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000427 SkScalar five = SkIntToScalar(5);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000428
fmalita5598b632015-09-15 11:26:13 -0700429 SkAutoTUnref<SkImage> gradientImage(SkImage::NewFromBitmap(make_gradient_circle(64, 64)));
430 SkAutoTUnref<SkImageFilter> gradientSource(SkImageSource::Create(gradientImage));
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000431 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000432 SkMatrix matrix;
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000433
senorblanco@chromium.orgba31f1d2014-05-07 20:56:08 +0000434 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
435 matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000436
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000437 SkRTreeFactory factory;
438 SkPictureRecorder recorder;
439 SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
440
441 SkPaint greenPaint;
442 greenPaint.setColor(SK_ColorGREEN);
443 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
444 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
445 SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
senorblanco8f3937d2014-10-29 12:36:32 -0700446 SkAutoTUnref<SkShader> shader(SkPerlinNoiseShader::CreateTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
447
448 SkAutoTUnref<SkImageFilter> rectShaderFilter(SkRectShaderImageFilter::Create(shader.get()));
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000449
fsd8b57712015-05-20 00:52:17 -0700450 SkAutoTUnref<SkShader> greenColorShader(SkShader::CreateColorShader(SK_ColorGREEN));
451 SkImageFilter::CropRect leftSideCropRect(SkRect::MakeXYWH(0, 0, 32, 64));
452 SkAutoTUnref<SkImageFilter> rectShaderFilterLeft(SkRectShaderImageFilter::Create(greenColorShader.get(), &leftSideCropRect));
453 SkImageFilter::CropRect rightSideCropRect(SkRect::MakeXYWH(32, 0, 32, 64));
454 SkAutoTUnref<SkImageFilter> rectShaderFilterRight(SkRectShaderImageFilter::Create(greenColorShader.get(), &rightSideCropRect));
455
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000456 struct {
457 const char* fName;
458 SkImageFilter* fFilter;
459 } filters[] = {
460 { "color filter", SkColorFilterImageFilter::Create(cf.get()) },
461 { "displacement map", SkDisplacementMapEffect::Create(
462 SkDisplacementMapEffect::kR_ChannelSelectorType,
463 SkDisplacementMapEffect::kB_ChannelSelectorType,
fmalita5598b632015-09-15 11:26:13 -0700464 20.0f, gradientSource.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000465 { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
466 { "drop shadow", SkDropShadowImageFilter::Create(
sugoi234f0362014-10-23 13:59:52 -0700467 SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
468 SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000469 { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
470 location, SK_ColorGREEN, 0, 0) },
471 { "specular lighting",
472 SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
473 { "matrix convolution",
474 SkMatrixConvolutionImageFilter::Create(
475 kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
476 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
halcanary96fcdcc2015-08-27 07:41:13 -0700477 { "merge", SkMergeImageFilter::Create(nullptr, nullptr, SkXfermode::kSrcOver_Mode) },
fsd8b57712015-05-20 00:52:17 -0700478 { "merge with disjoint inputs", SkMergeImageFilter::Create(
479 rectShaderFilterLeft, rectShaderFilterRight, SkXfermode::kSrcOver_Mode) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000480 { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
481 { "dilate", SkDilateImageFilter::Create(3, 2) },
482 { "erode", SkErodeImageFilter::Create(2, 3) },
483 { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
halcanary96fcdcc2015-08-27 07:41:13 -0700484 SkRect::MakeXYWH(0, 0, 100, 100), nullptr) },
senorblanco8c874ee2015-03-20 06:38:17 -0700485 { "matrix", SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality) },
senorblanco@chromium.org29ac34e2014-05-28 19:29:25 +0000486 { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
senorblanco@chromium.org910702b2014-05-30 20:36:15 +0000487 { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
senorblanco8f3937d2014-10-29 12:36:32 -0700488 { "rect shader and blur", SkBlurImageFilter::Create(five, five, rectShaderFilter.get()) },
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000489 };
490
491 SkBitmap untiledResult, tiledResult;
492 int width = 64, height = 64;
493 untiledResult.allocN32Pixels(width, height);
494 tiledResult.allocN32Pixels(width, height);
495 SkCanvas tiledCanvas(tiledResult);
496 SkCanvas untiledCanvas(untiledResult);
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000497 int tileSize = 8;
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000498
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000499 for (int scale = 1; scale <= 2; ++scale) {
500 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
501 tiledCanvas.clear(0);
502 untiledCanvas.clear(0);
503 SkPaint paint;
504 paint.setImageFilter(filters[i].fFilter);
505 paint.setTextSize(SkIntToScalar(height));
506 paint.setColor(SK_ColorWHITE);
507 SkString str;
508 const char* text = "ABC";
509 SkScalar ypos = SkIntToScalar(height);
510 untiledCanvas.save();
511 untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
512 untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
513 untiledCanvas.restore();
514 for (int y = 0; y < height; y += tileSize) {
515 for (int x = 0; x < width; x += tileSize) {
516 tiledCanvas.save();
517 tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
518 tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
519 tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
520 tiledCanvas.restore();
521 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000522 }
senorblanco@chromium.orgd4db6572014-05-07 20:00:04 +0000523 untiledCanvas.flush();
524 tiledCanvas.flush();
525 for (int y = 0; y < height; y++) {
526 int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
527 REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
528 if (diffs) {
529 break;
530 }
senorblanco@chromium.org0a5c2332014-04-29 15:20:39 +0000531 }
532 }
533 }
534
535 for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
536 SkSafeUnref(filters[i].fFilter);
537 }
538}
539
mtklein3f3b3d02014-12-01 11:47:08 -0800540static void draw_saveLayer_picture(int width, int height, int tileSize,
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700541 SkBBHFactory* factory, SkBitmap* result) {
mtkleind910f542014-08-22 09:06:34 -0700542
543 SkMatrix matrix;
544 matrix.setTranslate(SkIntToScalar(50), 0);
545
546 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
547 SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
senorblanco8c874ee2015-03-20 06:38:17 -0700548 SkAutoTUnref<SkImageFilter> imageFilter(SkImageFilter::CreateMatrixFilter(matrix, kNone_SkFilterQuality, cfif.get()));
mtkleind910f542014-08-22 09:06:34 -0700549
550 SkPaint paint;
551 paint.setImageFilter(imageFilter.get());
552 SkPictureRecorder recorder;
553 SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
mtklein3f3b3d02014-12-01 11:47:08 -0800554 SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
555 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700556 factory, 0);
mtkleind910f542014-08-22 09:06:34 -0700557 recordingCanvas->translate(-55, 0);
558 recordingCanvas->saveLayer(&bounds, &paint);
559 recordingCanvas->restore();
560 SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
561
562 result->allocN32Pixels(width, height);
563 SkCanvas canvas(*result);
564 canvas.clear(0);
565 canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
566 canvas.drawPicture(picture1.get());
567}
568
569DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
570 // Check that matrix filter when drawn tiled with BBH exactly
571 // matches the same thing drawn without BBH.
572 // Tests pass by not asserting.
573
574 const int width = 200, height = 200;
575 const int tileSize = 100;
576 SkBitmap result1, result2;
577 SkRTreeFactory factory;
578
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700579 draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
halcanary96fcdcc2015-08-27 07:41:13 -0700580 draw_saveLayer_picture(width, height, tileSize, nullptr, &result2);
mtkleind910f542014-08-22 09:06:34 -0700581
582 for (int y = 0; y < height; y++) {
583 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
584 REPORTER_ASSERT(reporter, !diffs);
585 if (diffs) {
586 break;
587 }
588 }
589}
590
halcanary96fcdcc2015-08-27 07:41:13 -0700591static SkImageFilter* makeBlur(SkImageFilter* input = nullptr) {
senorblanco1150a6d2014-08-25 12:46:58 -0700592 return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input);
593}
594
halcanary96fcdcc2015-08-27 07:41:13 -0700595static SkImageFilter* makeDropShadow(SkImageFilter* input = nullptr) {
senorblanco1150a6d2014-08-25 12:46:58 -0700596 return SkDropShadowImageFilter::Create(
597 SkIntToScalar(100), SkIntToScalar(100),
598 SkIntToScalar(10), SkIntToScalar(10),
sugoi234f0362014-10-23 13:59:52 -0700599 SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
halcanary96fcdcc2015-08-27 07:41:13 -0700600 input, nullptr);
senorblanco1150a6d2014-08-25 12:46:58 -0700601}
602
603DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
604 SkAutoTUnref<SkImageFilter> filter1(makeBlur());
605 SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
606
607 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
608 SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
609 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
610
611 REPORTER_ASSERT(reporter, bounds == expectedBounds);
612}
613
614DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
615 SkAutoTUnref<SkImageFilter> filter1(makeDropShadow());
616 SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get()));
617
618 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
619 SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
620 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
621
622 REPORTER_ASSERT(reporter, bounds == expectedBounds);
623}
624
625DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
626 SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2));
627 SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
628
629 SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
630 SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
631 filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
632
633 REPORTER_ASSERT(reporter, bounds == expectedBounds);
634}
635
ajuma5788faa2015-02-13 09:05:47 -0800636DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
637 SkAutoTUnref<SkImageFilter> filter1(makeBlur());
638 SkAutoTUnref<SkImageFilter> filter2(makeBlur());
639 SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(filter1.get(), filter2.get()));
640
641 SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
642 SkRect expectedBounds = SkRect::MakeXYWH(
643 SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
644 SkRect boundsDst = SkRect::MakeEmpty();
645 composedFilter->computeFastBounds(boundsSrc, &boundsDst);
646
647 REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
648}
649
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700650static void draw_blurred_rect(SkCanvas* canvas) {
senorblanco837f5322014-07-14 10:19:54 -0700651 SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
652 SkPaint filterPaint;
653 filterPaint.setColor(SK_ColorWHITE);
654 filterPaint.setImageFilter(filter);
halcanary96fcdcc2015-08-27 07:41:13 -0700655 canvas->saveLayer(nullptr, &filterPaint);
senorblanco837f5322014-07-14 10:19:54 -0700656 SkPaint whitePaint;
657 whitePaint.setColor(SK_ColorWHITE);
658 canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
659 canvas->restore();
660}
661
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700662static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
senorblanco837f5322014-07-14 10:19:54 -0700663 canvas->save();
664 canvas->clipRect(clipRect);
665 canvas->drawPicture(picture);
666 canvas->restore();
667}
668
669DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
670 // Check that the blur filter when recorded with RTree acceleration,
671 // and drawn tiled (with subsequent clip rects) exactly
672 // matches the same filter drawn with without RTree acceleration.
673 // This tests that the "bleed" from the blur into the otherwise-blank
674 // tiles is correctly rendered.
675 // Tests pass by not asserting.
676
677 int width = 16, height = 8;
678 SkBitmap result1, result2;
679 result1.allocN32Pixels(width, height);
680 result2.allocN32Pixels(width, height);
681 SkCanvas canvas1(result1);
682 SkCanvas canvas2(result2);
683 int tileSize = 8;
684
685 canvas1.clear(0);
686 canvas2.clear(0);
687
688 SkRTreeFactory factory;
689
690 SkPictureRecorder recorder1, recorder2;
691 // The only difference between these two pictures is that one has RTree aceleration.
mtklein3f3b3d02014-12-01 11:47:08 -0800692 SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
693 SkIntToScalar(height),
halcanary96fcdcc2015-08-27 07:41:13 -0700694 nullptr, 0);
mtklein3f3b3d02014-12-01 11:47:08 -0800695 SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
696 SkIntToScalar(height),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700697 &factory, 0);
698 draw_blurred_rect(recordingCanvas1);
699 draw_blurred_rect(recordingCanvas2);
senorblanco837f5322014-07-14 10:19:54 -0700700 SkAutoTUnref<SkPicture> picture1(recorder1.endRecording());
701 SkAutoTUnref<SkPicture> picture2(recorder2.endRecording());
702 for (int y = 0; y < height; y += tileSize) {
703 for (int x = 0; x < width; x += tileSize) {
704 SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700705 draw_picture_clipped(&canvas1, tileRect, picture1);
706 draw_picture_clipped(&canvas2, tileRect, picture2);
senorblanco837f5322014-07-14 10:19:54 -0700707 }
708 }
709 for (int y = 0; y < height; y++) {
710 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
711 REPORTER_ASSERT(reporter, !diffs);
712 if (diffs) {
713 break;
714 }
715 }
716}
717
senorblanco@chromium.org91957432014-05-01 14:03:41 +0000718DEF_TEST(ImageFilterMatrixConvolution, reporter) {
719 // Check that a 1x3 filter does not cause a spurious assert.
720 SkScalar kernel[3] = {
721 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
722 };
723 SkISize kernelSize = SkISize::Make(1, 3);
724 SkScalar gain = SK_Scalar1, bias = 0;
725 SkIPoint kernelOffset = SkIPoint::Make(0, 0);
726
727 SkAutoTUnref<SkImageFilter> filter(
728 SkMatrixConvolutionImageFilter::Create(
729 kernelSize, kernel, gain, bias, kernelOffset,
730 SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
731
732 SkBitmap result;
733 int width = 16, height = 16;
734 result.allocN32Pixels(width, height);
735 SkCanvas canvas(result);
736 canvas.clear(0);
737
738 SkPaint paint;
739 paint.setImageFilter(filter);
740 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
741 canvas.drawRect(rect, paint);
742}
743
senorblanco@chromium.org8c7372b2014-05-02 19:13:11 +0000744DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
745 // Check that a filter with borders outside the target bounds
746 // does not crash.
747 SkScalar kernel[3] = {
748 0, 0, 0,
749 };
750 SkISize kernelSize = SkISize::Make(3, 1);
751 SkScalar gain = SK_Scalar1, bias = 0;
752 SkIPoint kernelOffset = SkIPoint::Make(2, 0);
753
754 SkAutoTUnref<SkImageFilter> filter(
755 SkMatrixConvolutionImageFilter::Create(
756 kernelSize, kernel, gain, bias, kernelOffset,
757 SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
758
759 SkBitmap result;
760
761 int width = 10, height = 10;
762 result.allocN32Pixels(width, height);
763 SkCanvas canvas(result);
764 canvas.clear(0);
765
766 SkPaint filterPaint;
767 filterPaint.setImageFilter(filter);
768 SkRect bounds = SkRect::MakeWH(1, 10);
769 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
770 SkPaint rectPaint;
771 canvas.saveLayer(&bounds, &filterPaint);
772 canvas.drawRect(rect, rectPaint);
773 canvas.restore();
774}
775
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000776DEF_TEST(ImageFilterCropRect, reporter) {
robertphillips9a53fd72015-06-22 09:46:59 -0700777 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
778 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
779
780 SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
robertphillipsefbffed2015-06-22 12:06:08 -0700781 SkImageFilter::Proxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -0700782
783 test_crop_rects(&proxy, reporter);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000784}
785
tfarina9ea53f92014-06-24 06:50:39 -0700786DEF_TEST(ImageFilterMatrix, reporter) {
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000787 SkBitmap temp;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000788 temp.allocN32Pixels(100, 100);
robertphillips9a53fd72015-06-22 09:46:59 -0700789 SkCanvas canvas(temp);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000790 canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
791
792 SkMatrix expectedMatrix = canvas.getTotalMatrix();
793
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000794 SkRTreeFactory factory;
795 SkPictureRecorder recorder;
796 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000797
798 SkPaint paint;
799 SkAutoTUnref<MatrixTestImageFilter> imageFilter(
800 new MatrixTestImageFilter(reporter, expectedMatrix));
801 paint.setImageFilter(imageFilter.get());
halcanary96fcdcc2015-08-27 07:41:13 -0700802 recordingCanvas->saveLayer(nullptr, &paint);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000803 SkPaint solidPaint;
804 solidPaint.setColor(0xFFFFFFFF);
805 recordingCanvas->save();
806 recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
807 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
808 recordingCanvas->restore(); // scale
809 recordingCanvas->restore(); // saveLayer
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000810 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000811
robertphillips9b14f262014-06-04 05:40:44 -0700812 canvas.drawPicture(picture);
senorblanco@chromium.org5251e2b2014-02-05 22:36:31 +0000813}
814
senorblanco3d822c22014-07-30 14:49:31 -0700815DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000816 SkRTreeFactory factory;
817 SkPictureRecorder recorder;
818 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
819
820 // Create an SkPicture which simply draws a green 1x1 rectangle.
821 SkPaint greenPaint;
822 greenPaint.setColor(SK_ColorGREEN);
823 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
824 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
825
826 // Wrap that SkPicture in an SkPictureImageFilter.
827 SkAutoTUnref<SkImageFilter> imageFilter(
828 SkPictureImageFilter::Create(picture.get()));
829
830 // Check that SkPictureImageFilter successfully serializes its contained
831 // SkPicture when not in cross-process mode.
832 SkPaint paint;
833 paint.setImageFilter(imageFilter.get());
834 SkPictureRecorder outerRecorder;
835 SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
836 SkPaint redPaintWithFilter;
837 redPaintWithFilter.setColor(SK_ColorRED);
838 redPaintWithFilter.setImageFilter(imageFilter.get());
839 outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
840 SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
841
842 SkBitmap bitmap;
843 bitmap.allocN32Pixels(1, 1);
robertphillips9a53fd72015-06-22 09:46:59 -0700844 SkCanvas canvas(bitmap);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000845
846 // The result here should be green, since the filter replaces the primitive's red interior.
847 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700848 canvas.drawPicture(outerPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000849 uint32_t pixel = *bitmap.getAddr32(0, 0);
850 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
851
852 // Check that, for now, SkPictureImageFilter does not serialize or
853 // deserialize its contained picture when the filter is serialized
854 // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
855 SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
856 SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
857 data->data(), data->size(), SkImageFilter::GetFlattenableType()));
858 SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
859
860 redPaintWithFilter.setImageFilter(unflattenedFilter);
861 SkPictureRecorder crossProcessRecorder;
862 SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
863 crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
864 SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
865
866 canvas.clear(0x0);
robertphillips9b14f262014-06-04 05:40:44 -0700867 canvas.drawPicture(crossProcessPicture);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000868 pixel = *bitmap.getAddr32(0, 0);
hendrikw446ee672015-06-16 09:28:37 -0700869 // If the security precautions are enabled, the result here should not be green, since the
870 // filter draws nothing.
871 REPORTER_ASSERT(reporter, SkPicture::PictureIOSecurityPrecautionsEnabled()
872 ? pixel != SK_ColorGREEN : pixel == SK_ColorGREEN);
senorblanco@chromium.org97f5fc62014-05-30 20:50:56 +0000873}
874
senorblanco3d822c22014-07-30 14:49:31 -0700875DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
876 SkRTreeFactory factory;
877 SkPictureRecorder recorder;
878 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
879
880 // Create an SkPicture which simply draws a green 1x1 rectangle.
881 SkPaint greenPaint;
882 greenPaint.setColor(SK_ColorGREEN);
883 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
884 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
885
robertphillips9a53fd72015-06-22 09:46:59 -0700886 SkAutoTUnref<SkImageFilter> imageFilter(SkPictureImageFilter::Create(picture.get()));
senorblanco3d822c22014-07-30 14:49:31 -0700887
888 SkBitmap result;
889 SkIPoint offset;
halcanary96fcdcc2015-08-27 07:41:13 -0700890 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr);
senorblanco3d822c22014-07-30 14:49:31 -0700891 SkBitmap bitmap;
892 bitmap.allocN32Pixels(2, 2);
robertphillipsefbffed2015-06-22 12:06:08 -0700893 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
894 SkBitmapDevice device(bitmap, props);
895 SkImageFilter::Proxy proxy(&device);
senorblanco3d822c22014-07-30 14:49:31 -0700896 REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
897}
898
tfarina9ea53f92014-06-24 06:50:39 -0700899DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000900 // Even when there's an empty saveLayer()/restore(), ensure that an image
901 // filter or color filter which affects transparent black still draws.
902
903 SkBitmap bitmap;
904 bitmap.allocN32Pixels(10, 10);
robertphillips9a53fd72015-06-22 09:46:59 -0700905 SkCanvas canvas(bitmap);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000906
907 SkRTreeFactory factory;
908 SkPictureRecorder recorder;
909
910 SkAutoTUnref<SkColorFilter> green(
911 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
912 SkAutoTUnref<SkColorFilterImageFilter> imageFilter(
913 SkColorFilterImageFilter::Create(green.get()));
914 SkPaint imageFilterPaint;
915 imageFilterPaint.setImageFilter(imageFilter.get());
916 SkPaint colorFilterPaint;
917 colorFilterPaint.setColorFilter(green.get());
918
919 SkRect bounds = SkRect::MakeWH(10, 10);
920
921 SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
922 recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
923 recordingCanvas->restore();
924 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
925
926 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700927 canvas.drawPicture(picture);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000928 uint32_t pixel = *bitmap.getAddr32(0, 0);
929 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
930
931 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700932 recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000933 recordingCanvas->restore();
934 SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
935
936 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700937 canvas.drawPicture(picture2);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000938 pixel = *bitmap.getAddr32(0, 0);
939 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
940
941 recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
942 recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
943 recordingCanvas->restore();
944 SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
945
946 canvas.clear(0);
robertphillips9b14f262014-06-04 05:40:44 -0700947 canvas.drawPicture(picture3);
senorblanco@chromium.org68250c82014-05-06 22:52:55 +0000948 pixel = *bitmap.getAddr32(0, 0);
949 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
950}
951
robertphillips9a53fd72015-06-22 09:46:59 -0700952static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) {
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000953 SkBitmap bitmap;
954 bitmap.allocN32Pixels(100, 100);
955 bitmap.eraseARGB(0, 0, 0, 0);
956
957 // Check that a blur with an insane radius does not crash or assert.
958 SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
959
960 SkPaint paint;
961 paint.setImageFilter(blur);
robertphillips9a53fd72015-06-22 09:46:59 -0700962 canvas->drawSprite(bitmap, 0, 0, &paint);
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000963}
964
965DEF_TEST(HugeBlurImageFilter, reporter) {
966 SkBitmap temp;
967 temp.allocN32Pixels(100, 100);
robertphillips9a53fd72015-06-22 09:46:59 -0700968 SkCanvas canvas(temp);
969 test_huge_blur(&canvas, reporter);
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +0000970}
971
senorblanco3a495202014-09-29 07:57:20 -0700972DEF_TEST(MatrixConvolutionSanityTest, reporter) {
973 SkScalar kernel[1] = { 0 };
974 SkScalar gain = SK_Scalar1, bias = 0;
975 SkIPoint kernelOffset = SkIPoint::Make(1, 1);
976
halcanary96fcdcc2015-08-27 07:41:13 -0700977 // Check that an enormous (non-allocatable) kernel gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -0700978 SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create(
979 SkISize::Make(1<<30, 1<<30),
980 kernel,
981 gain,
982 bias,
983 kernelOffset,
984 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
985 false));
986
halcanary96fcdcc2015-08-27 07:41:13 -0700987 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -0700988
halcanary96fcdcc2015-08-27 07:41:13 -0700989 // Check that a nullptr kernel gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -0700990 conv.reset(SkMatrixConvolutionImageFilter::Create(
991 SkISize::Make(1, 1),
halcanary96fcdcc2015-08-27 07:41:13 -0700992 nullptr,
senorblanco3a495202014-09-29 07:57:20 -0700993 gain,
994 bias,
995 kernelOffset,
996 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
997 false));
998
halcanary96fcdcc2015-08-27 07:41:13 -0700999 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001000
halcanary96fcdcc2015-08-27 07:41:13 -07001001 // Check that a kernel width < 1 gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -07001002 conv.reset(SkMatrixConvolutionImageFilter::Create(
1003 SkISize::Make(0, 1),
1004 kernel,
1005 gain,
1006 bias,
1007 kernelOffset,
1008 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
1009 false));
1010
halcanary96fcdcc2015-08-27 07:41:13 -07001011 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001012
halcanary96fcdcc2015-08-27 07:41:13 -07001013 // Check that kernel height < 1 gives a nullptr filter.
senorblanco3a495202014-09-29 07:57:20 -07001014 conv.reset(SkMatrixConvolutionImageFilter::Create(
1015 SkISize::Make(1, -1),
1016 kernel,
1017 gain,
1018 bias,
1019 kernelOffset,
1020 SkMatrixConvolutionImageFilter::kRepeat_TileMode,
1021 false));
1022
halcanary96fcdcc2015-08-27 07:41:13 -07001023 REPORTER_ASSERT(reporter, nullptr == conv.get());
senorblanco3a495202014-09-29 07:57:20 -07001024}
1025
robertphillips9a53fd72015-06-22 09:46:59 -07001026static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* reporter) {
1027 canvas->clear(0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001028
1029 SkBitmap bitmap;
1030 bitmap.allocN32Pixels(1, 1);
1031 bitmap.eraseARGB(255, 255, 255, 255);
1032
1033 SkAutoTUnref<SkColorFilter> green(
1034 SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
1035 SkAutoTUnref<SkColorFilterImageFilter> greenFilter(
1036 SkColorFilterImageFilter::Create(green.get()));
1037 SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
1038 SkAutoTUnref<SkColorFilterImageFilter> croppedOut(
halcanary96fcdcc2015-08-27 07:41:13 -07001039 SkColorFilterImageFilter::Create(green.get(), nullptr, &cropRect));
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001040
1041 // Check that an xfermode image filter whose input has been cropped out still draws the other
1042 // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
1043 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
1044 SkAutoTUnref<SkImageFilter> xfermodeNoFg(
1045 SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
1046 SkAutoTUnref<SkImageFilter> xfermodeNoBg(
1047 SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
1048 SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
1049 SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
1050
1051 SkPaint paint;
1052 paint.setImageFilter(xfermodeNoFg);
robertphillips9a53fd72015-06-22 09:46:59 -07001053 canvas->drawSprite(bitmap, 0, 0, &paint);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001054
1055 uint32_t pixel;
kkinnunena9d9a392015-03-06 07:16:00 -08001056 SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
robertphillips9a53fd72015-06-22 09:46:59 -07001057 canvas->readPixels(info, &pixel, 4, 0, 0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001058 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1059
1060 paint.setImageFilter(xfermodeNoBg);
robertphillips9a53fd72015-06-22 09:46:59 -07001061 canvas->drawSprite(bitmap, 0, 0, &paint);
1062 canvas->readPixels(info, &pixel, 4, 0, 0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001063 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1064
1065 paint.setImageFilter(xfermodeNoFgNoBg);
robertphillips9a53fd72015-06-22 09:46:59 -07001066 canvas->drawSprite(bitmap, 0, 0, &paint);
1067 canvas->readPixels(info, &pixel, 4, 0, 0);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001068 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1069}
1070
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001071DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
1072 SkBitmap temp;
1073 temp.allocN32Pixels(50, 50);
robertphillips9a53fd72015-06-22 09:46:59 -07001074 SkCanvas canvas(temp);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001075 canvas.clear(0x0);
1076
1077 SkBitmap bitmap;
1078 bitmap.allocN32Pixels(10, 10);
1079 bitmap.eraseColor(SK_ColorGREEN);
1080
1081 SkMatrix matrix;
1082 matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
1083 matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
1084 SkAutoTUnref<SkImageFilter> matrixFilter(
senorblanco8c874ee2015-03-20 06:38:17 -07001085 SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality));
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001086
1087 // Test that saveLayer() with a filter nested inside another saveLayer() applies the
1088 // correct offset to the filter matrix.
1089 SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
halcanary96fcdcc2015-08-27 07:41:13 -07001090 canvas.saveLayer(&bounds1, nullptr);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001091 SkPaint filterPaint;
1092 filterPaint.setImageFilter(matrixFilter);
1093 SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
1094 canvas.saveLayer(&bounds2, &filterPaint);
1095 SkPaint greenPaint;
1096 greenPaint.setColor(SK_ColorGREEN);
1097 canvas.drawRect(bounds2, greenPaint);
1098 canvas.restore();
1099 canvas.restore();
1100 SkPaint strokePaint;
1101 strokePaint.setStyle(SkPaint::kStroke_Style);
1102 strokePaint.setColor(SK_ColorRED);
1103
kkinnunena9d9a392015-03-06 07:16:00 -08001104 SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001105 uint32_t pixel;
1106 canvas.readPixels(info, &pixel, 4, 25, 25);
1107 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1108
1109 // Test that drawSprite() with a filter nested inside a saveLayer() applies the
1110 // correct offset to the filter matrix.
1111 canvas.clear(0x0);
1112 canvas.readPixels(info, &pixel, 4, 25, 25);
halcanary96fcdcc2015-08-27 07:41:13 -07001113 canvas.saveLayer(&bounds1, nullptr);
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001114 canvas.drawSprite(bitmap, 20, 20, &filterPaint);
1115 canvas.restore();
1116
1117 canvas.readPixels(info, &pixel, 4, 25, 25);
1118 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1119}
1120
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001121DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
1122 SkBitmap temp;
1123 temp.allocN32Pixels(100, 100);
robertphillips9a53fd72015-06-22 09:46:59 -07001124 SkCanvas canvas(temp);
1125 test_xfermode_cropped_input(&canvas, reporter);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001126}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001127
ajuma5788faa2015-02-13 09:05:47 -08001128DEF_TEST(ComposedImageFilterOffset, reporter) {
1129 SkBitmap bitmap;
1130 bitmap.allocN32Pixels(100, 100);
1131 bitmap.eraseARGB(0, 0, 0, 0);
robertphillipsefbffed2015-06-22 12:06:08 -07001132 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1133 SkBitmapDevice device(bitmap, props);
1134 SkImageFilter::Proxy proxy(&device);
ajuma5788faa2015-02-13 09:05:47 -08001135
1136 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
halcanary96fcdcc2015-08-27 07:41:13 -07001137 SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect));
ajuma8e8c9402015-02-13 10:15:46 -08001138 SkAutoTUnref<SkImageFilter> blurFilter(makeBlur());
1139 SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter, offsetFilter.get()));
ajuma5788faa2015-02-13 09:05:47 -08001140 SkBitmap result;
1141 SkIPoint offset;
halcanary96fcdcc2015-08-27 07:41:13 -07001142 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), nullptr);
ajuma5788faa2015-02-13 09:05:47 -08001143 REPORTER_ASSERT(reporter, composedFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
1144 REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
1145}
1146
senorblanco24d2a7b2015-07-13 10:27:05 -07001147DEF_TEST(PartialCropRect, reporter) {
1148 SkBitmap bitmap;
1149 bitmap.allocN32Pixels(100, 100);
1150 bitmap.eraseARGB(0, 0, 0, 0);
1151 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1152 SkBitmapDevice device(bitmap, props);
1153 SkImageFilter::Proxy proxy(&device);
1154
1155 SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30),
senorblancoed7cf272015-07-16 15:19:11 -07001156 SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge);
halcanary96fcdcc2015-08-27 07:41:13 -07001157 SkAutoTUnref<SkImageFilter> filter(make_grayscale(nullptr, &cropRect));
senorblanco24d2a7b2015-07-13 10:27:05 -07001158 SkBitmap result;
1159 SkIPoint offset;
halcanary96fcdcc2015-08-27 07:41:13 -07001160 SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), nullptr);
senorblanco24d2a7b2015-07-13 10:27:05 -07001161 REPORTER_ASSERT(reporter, filter->filterImage(&proxy, bitmap, ctx, &result, &offset));
1162 REPORTER_ASSERT(reporter, offset.fX == 0);
1163 REPORTER_ASSERT(reporter, offset.fY == 0);
1164 REPORTER_ASSERT(reporter, result.width() == 20);
1165 REPORTER_ASSERT(reporter, result.height() == 30);
1166}
1167
senorblanco0abdf762015-08-20 11:10:41 -07001168DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
1169
1170 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
1171 SkAutoTUnref<SkImageFilter> lighting(SkLightingImageFilter::CreatePointLitDiffuse(
1172 location, SK_ColorGREEN, 0, 0));
1173 REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
1174
1175 SkAutoTUnref<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
1176 REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
1177 {
1178 SkColorFilter* grayCF;
1179 REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
1180 REPORTER_ASSERT(reporter, !grayCF->affectsTransparentBlack());
1181 grayCF->unref();
1182 }
1183 REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
1184
1185 SkAutoTUnref<SkImageFilter> grayBlur(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, gray.get()));
1186 REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
1187
1188 SkScalar greenMatrix[20] = { 0, 0, 0, 0, 0,
1189 0, 0, 0, 0, 1,
1190 0, 0, 0, 0, 0,
1191 0, 0, 0, 0, 1 };
1192 SkAutoTUnref<SkColorFilter> greenCF(SkColorMatrixFilter::Create(greenMatrix));
1193 SkAutoTUnref<SkImageFilter> green(SkColorFilterImageFilter::Create(greenCF));
1194
1195 REPORTER_ASSERT(reporter, greenCF->affectsTransparentBlack());
1196 REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
1197
1198 SkAutoTUnref<SkImageFilter> greenBlur(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, green.get()));
1199 REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
1200
1201 uint8_t allOne[256], identity[256];
1202 for (int i = 0; i < 256; ++i) {
1203 identity[i] = i;
1204 allOne[i] = 255;
1205 }
1206
1207 SkAutoTUnref<SkColorFilter> identityCF(
1208 SkTableColorFilter::CreateARGB(identity, identity, identity, allOne));
1209 SkAutoTUnref<SkImageFilter> identityFilter(SkColorFilterImageFilter::Create(identityCF.get()));
1210 REPORTER_ASSERT(reporter, !identityCF->affectsTransparentBlack());
1211 REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
1212
1213 SkAutoTUnref<SkColorFilter> forceOpaqueCF(
1214 SkTableColorFilter::CreateARGB(allOne, identity, identity, identity));
1215 SkAutoTUnref<SkImageFilter> forceOpaque(SkColorFilterImageFilter::Create(forceOpaqueCF.get()));
1216 REPORTER_ASSERT(reporter, forceOpaqueCF->affectsTransparentBlack());
1217 REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
1218}
1219
fmalitacd56f812015-09-14 13:31:18 -07001220// Verify that SkImageSource survives serialization
1221DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
1222 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10));
1223 surface->getCanvas()->clear(SK_ColorGREEN);
1224 SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
1225 SkAutoTUnref<SkImageFilter> filter(SkImageSource::Create(image));
1226
1227 SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(filter));
1228 SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
1229 data->data(), data->size(), SkImageFilter::GetFlattenableType()));
1230 SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
1231 REPORTER_ASSERT(reporter, unflattenedFilter);
1232
1233 SkBitmap bm;
1234 bm.allocN32Pixels(10, 10);
fmalita23cb88c2015-09-15 06:56:23 -07001235 bm.eraseColor(SK_ColorBLUE);
fmalitacd56f812015-09-14 13:31:18 -07001236 SkPaint paint;
1237 paint.setColor(SK_ColorRED);
1238 paint.setImageFilter(unflattenedFilter);
1239
1240 SkCanvas canvas(bm);
1241 canvas.drawRect(SkRect::MakeWH(10, 10), paint);
1242 REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
1243}
1244
senorblanco@chromium.org58d14662014-02-03 22:36:39 +00001245#if SK_SUPPORT_GPU
reed4a8126e2014-09-22 07:29:03 -07001246
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +00001247DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
1248 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
halcanary96fcdcc2015-08-27 07:41:13 -07001249 if (nullptr == context) {
senorblancoc8e93402015-04-21 07:20:36 -07001250 return;
1251 }
robertphillipsefbffed2015-06-22 12:06:08 -07001252 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1253
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001254 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001255 SkSurface::kNo_Budgeted,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001256 SkImageInfo::MakeN32Premul(100, 100),
bsalomonafe30052015-01-16 07:32:33 -08001257 0,
bsalomon74f681d2015-06-23 14:38:48 -07001258 &props,
1259 SkGpuDevice::kUninit_InitContents));
robertphillipsefbffed2015-06-22 12:06:08 -07001260 SkImageFilter::Proxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -07001261
1262 test_crop_rects(&proxy, reporter);
tfarina@chromium.org9f9d5822013-12-18 22:15:12 +00001263}
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001264
1265DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
1266 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
halcanary96fcdcc2015-08-27 07:41:13 -07001267 if (nullptr == context) {
senorblancoc8e93402015-04-21 07:20:36 -07001268 return;
1269 }
robertphillipsefbffed2015-06-22 12:06:08 -07001270 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1271
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001272 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001273 SkSurface::kNo_Budgeted,
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001274 SkImageInfo::MakeN32Premul(100, 100),
bsalomonafe30052015-01-16 07:32:33 -08001275 0,
bsalomon74f681d2015-06-23 14:38:48 -07001276 &props,
1277 SkGpuDevice::kUninit_InitContents));
robertphillips9a53fd72015-06-22 09:46:59 -07001278 SkCanvas canvas(device);
1279
1280 test_huge_blur(&canvas, reporter);
senorblanco@chromium.org09843fd2014-03-24 20:50:59 +00001281}
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001282
1283DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) {
1284 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
halcanary96fcdcc2015-08-27 07:41:13 -07001285 if (nullptr == context) {
senorblancoc8e93402015-04-21 07:20:36 -07001286 return;
1287 }
robertphillipsefbffed2015-06-22 12:06:08 -07001288 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1289
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001290 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001291 SkSurface::kNo_Budgeted,
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001292 SkImageInfo::MakeN32Premul(1, 1),
bsalomonafe30052015-01-16 07:32:33 -08001293 0,
bsalomon74f681d2015-06-23 14:38:48 -07001294 &props,
1295 SkGpuDevice::kUninit_InitContents));
robertphillips9a53fd72015-06-22 09:46:59 -07001296 SkCanvas canvas(device);
1297
1298 test_xfermode_cropped_input(&canvas, reporter);
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +00001299}
senorblanco32673b92014-09-09 09:15:04 -07001300
1301DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) {
1302 GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
halcanary96fcdcc2015-08-27 07:41:13 -07001303 if (nullptr == context) {
senorblancoc8e93402015-04-21 07:20:36 -07001304 return;
1305 }
robertphillipsefbffed2015-06-22 12:06:08 -07001306 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1307
senorblanco32673b92014-09-09 09:15:04 -07001308 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
bsalomonafe30052015-01-16 07:32:33 -08001309 SkSurface::kNo_Budgeted,
senorblanco32673b92014-09-09 09:15:04 -07001310 SkImageInfo::MakeN32Premul(1, 1),
bsalomonafe30052015-01-16 07:32:33 -08001311 0,
bsalomon74f681d2015-06-23 14:38:48 -07001312 &props,
1313 SkGpuDevice::kUninit_InitContents));
robertphillipsefbffed2015-06-22 12:06:08 -07001314 SkImageFilter::Proxy proxy(device);
robertphillips9a53fd72015-06-22 09:46:59 -07001315
1316 test_negative_blur_sigma(&proxy, reporter);
senorblanco32673b92014-09-09 09:15:04 -07001317}
senorblanco@chromium.org58d14662014-02-03 22:36:39 +00001318#endif