1 /*
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
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkBBHFactory.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColor.h"
14 #include "include/core/SkColorFilter.h"
15 #include "include/core/SkColorType.h"
16 #include "include/core/SkData.h"
17 #include "include/core/SkFlattenable.h"
18 #include "include/core/SkFont.h"
19 #include "include/core/SkImage.h"
20 #include "include/core/SkImageFilter.h"
21 #include "include/core/SkImageInfo.h"
22 #include "include/core/SkMatrix.h"
23 #include "include/core/SkPaint.h"
24 #include "include/core/SkPicture.h"
25 #include "include/core/SkPictureRecorder.h"
26 #include "include/core/SkPoint.h"
27 #include "include/core/SkPoint3.h"
28 #include "include/core/SkRect.h"
29 #include "include/core/SkRefCnt.h"
30 #include "include/core/SkSamplingOptions.h"
31 #include "include/core/SkScalar.h"
32 #include "include/core/SkSerialProcs.h"
33 #include "include/core/SkShader.h"
34 #include "include/core/SkSize.h"
35 #include "include/core/SkSurface.h"
36 #include "include/core/SkSurfaceProps.h"
37 #include "include/core/SkTileMode.h"
38 #include "include/core/SkTypes.h"
39 #include "include/effects/SkGradientShader.h"
40 #include "include/effects/SkImageFilters.h"
41 #include "include/effects/SkPerlinNoiseShader.h"
42 #include "include/encode/SkPngEncoder.h"
43 #include "include/gpu/GpuTypes.h"
44 #include "include/gpu/ganesh/GrTypes.h"
45 #include "include/private/base/SkTArray.h"
46 #include "include/private/base/SkTo.h"
47 #include "src/core/SkBitmapDevice.h"
48 #include "src/core/SkDevice.h"
49 #include "src/core/SkImageFilterTypes.h"
50 #include "src/core/SkImageFilter_Base.h"
51 #include "src/core/SkRectPriv.h"
52 #include "src/core/SkSpecialImage.h"
53 #include "src/effects/colorfilters/SkColorFilterBase.h"
54 #include "src/image/SkImage_Base.h"
55 #include "tests/CtsEnforcement.h"
56 #include "tests/Test.h"
57 #include "tools/EncodeUtils.h"
58 #include "tools/Resources.h"
59 #include "tools/ToolUtils.h"
60 #include "tools/fonts/FontToolUtils.h"
61
62 #if defined(SK_GANESH)
63 #include "include/gpu/ganesh/GrDirectContext.h"
64 #include "include/gpu/ganesh/GrRecordingContext.h"
65 #include "include/gpu/ganesh/SkImageGanesh.h"
66 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
67 #include "src/gpu/ganesh/GrCaps.h"
68 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
69 #include "src/gpu/ganesh/image/GrImageUtils.h"
70 #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
71 #include "src/gpu/ganesh/image/SkSpecialImage_Ganesh.h"
72 #endif
73
74 #if defined(SK_GRAPHITE)
75 #include "include/gpu/graphite/Context.h"
76 #include "include/gpu/graphite/Image.h"
77 #include "include/gpu/graphite/Surface.h"
78 #include "tools/graphite/GraphiteToolUtils.h"
79 #endif
80
81 #include <algorithm>
82 #include <cstdint>
83 #include <cstring>
84 #include <utility>
85 #include <limits>
86
87 using namespace skia_private;
88
89 class SkReadBuffer;
90 class SkWriteBuffer;
91 struct GrContextOptions;
92
93 static const int kBitmapSize = 4;
94
95 namespace {
96
97 static constexpr GrSurfaceOrigin kTestSurfaceOrigin = kTopLeft_GrSurfaceOrigin;
98
99 class MatrixTestImageFilter : public SkImageFilter_Base {
100 public:
MatrixTestImageFilter(skiatest::Reporter * reporter,const SkM44 & expectedMatrix)101 MatrixTestImageFilter(skiatest::Reporter* reporter, const SkM44& expectedMatrix)
102 : SkImageFilter_Base(nullptr, 0)
103 , fReporter(reporter)
104 , fExpectedMatrix(expectedMatrix) {
105 // Layers have an extra pixel of padding that adjusts the coordinate space
106 fExpectedMatrix.postTranslate(1.f, 1.f);
107 }
108
109 private:
getFactory() const110 Factory getFactory() const override {
111 SK_ABORT("Does not participate in serialization");
112 return nullptr;
113 }
getTypeName() const114 const char* getTypeName() const override { return "MatrixTestImageFilter"; }
115
onFilterImage(const skif::Context & ctx) const116 skif::FilterResult onFilterImage(const skif::Context& ctx) const override {
117 REPORTER_ASSERT(fReporter, ctx.mapping().layerMatrix() == fExpectedMatrix);
118 return ctx.source();
119 }
120
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const121 skif::LayerSpace<SkIRect> onGetInputLayerBounds(
122 const skif::Mapping& mapping,
123 const skif::LayerSpace<SkIRect>& desiredOutput,
124 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override {
125 return desiredOutput;
126 }
127
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const128 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
129 const skif::Mapping& mapping,
130 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override {
131 return contentBounds;
132 }
133
134 skiatest::Reporter* fReporter;
135 SkM44 fExpectedMatrix;
136 };
137
draw_gradient_circle(SkCanvas * canvas,int width,int height)138 void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
139 SkScalar x = SkIntToScalar(width / 2);
140 SkScalar y = SkIntToScalar(height / 2);
141 SkScalar radius = std::min(x, y) * 0.8f;
142 canvas->clear(0x00000000);
143 SkColor colors[2];
144 colors[0] = SK_ColorWHITE;
145 colors[1] = SK_ColorBLACK;
146 sk_sp<SkShader> shader(
147 SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
148 SkTileMode::kClamp)
149 );
150 SkPaint paint;
151 paint.setShader(shader);
152 canvas->drawCircle(x, y, radius, paint);
153 }
154
make_gradient_circle(int width,int height)155 SkBitmap make_gradient_circle(int width, int height) {
156 SkBitmap bitmap;
157 bitmap.allocN32Pixels(width, height);
158 SkCanvas canvas(bitmap);
159 draw_gradient_circle(&canvas, width, height);
160 return bitmap;
161 }
162
163 class FilterList {
164 public:
FilterList(const sk_sp<SkImageFilter> & input,const SkIRect * cropRect=nullptr)165 FilterList(const sk_sp<SkImageFilter>& input, const SkIRect* cropRect = nullptr) {
166 static const SkScalar kBlurSigma = SkIntToScalar(5);
167
168 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
169 {
170 sk_sp<SkColorFilter> cf(SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcIn));
171
172 this->addFilter("color filter",
173 SkImageFilters::ColorFilter(std::move(cf), input, cropRect));
174 }
175 {
176 sk_sp<SkImage> gradientImage(make_gradient_circle(64, 64).asImage());
177 sk_sp<SkImageFilter> gradientSource(SkImageFilters::Image(std::move(gradientImage),
178 SkFilterMode::kNearest));
179
180 this->addFilter("displacement map",
181 SkImageFilters::DisplacementMap(SkColorChannel::kR, SkColorChannel::kB, 20.0f,
182 std::move(gradientSource), input, cropRect));
183 }
184 this->addFilter("blur", SkImageFilters::Blur(SK_Scalar1, SK_Scalar1, input, cropRect));
185 this->addFilter("drop shadow", SkImageFilters::DropShadow(
186 SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN, input, cropRect));
187 this->addFilter("diffuse lighting",
188 SkImageFilters::PointLitDiffuse(location, SK_ColorGREEN, 0, 0, input, cropRect));
189 this->addFilter("specular lighting",
190 SkImageFilters::PointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input,
191 cropRect));
192 {
193 SkScalar kernel[9] = {
194 SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
195 SkIntToScalar(1), SkIntToScalar(-7), SkIntToScalar(1),
196 SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
197 };
198 const SkISize kernelSize = SkISize::Make(3, 3);
199 const SkScalar gain = SK_Scalar1, bias = 0;
200
201 // This filter needs a saveLayer bc it is in repeat mode
202 this->addFilter("matrix convolution",
203 SkImageFilters::MatrixConvolution(
204 kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
205 SkTileMode::kRepeat, false, input, cropRect),
206 true);
207 }
208 this->addFilter("merge", SkImageFilters::Merge(input, input, cropRect));
209
210 {
211 sk_sp<SkShader> greenColorShader = SkShaders::Color(SK_ColorGREEN);
212
213 SkIRect leftSideCropRect = SkIRect::MakeXYWH(0, 0, 32, 64);
214 sk_sp<SkImageFilter> shaderFilterLeft(SkImageFilters::Shader(greenColorShader,
215 &leftSideCropRect));
216 SkIRect rightSideCropRect = SkIRect::MakeXYWH(32, 0, 32, 64);
217 sk_sp<SkImageFilter> shaderFilterRight(SkImageFilters::Shader(greenColorShader,
218 &rightSideCropRect));
219
220
221 this->addFilter("merge with disjoint inputs", SkImageFilters::Merge(
222 std::move(shaderFilterLeft), std::move(shaderFilterRight), cropRect));
223 }
224
225 this->addFilter("offset", SkImageFilters::Offset(SK_Scalar1, SK_Scalar1, input, cropRect));
226 this->addFilter("dilate", SkImageFilters::Dilate(3, 2, input, cropRect));
227 this->addFilter("erode", SkImageFilters::Erode(2, 3, input, cropRect));
228 this->addFilter("tile", SkImageFilters::Tile(SkRect::MakeXYWH(0, 0, 50, 50),
229 cropRect ? SkRect::Make(*cropRect)
230 : SkRect::MakeXYWH(0, 0, 100, 100),
231 input));
232
233 if (!cropRect) {
234 SkMatrix matrix;
235
236 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
237 matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
238
239 this->addFilter("matrix",
240 SkImageFilters::MatrixTransform(matrix,
241 SkSamplingOptions(SkFilterMode::kLinear),
242 input));
243 }
244 {
245 sk_sp<SkImageFilter> blur(SkImageFilters::Blur(kBlurSigma, kBlurSigma, input));
246
247 this->addFilter("blur and offset", SkImageFilters::Offset(
248 kBlurSigma, kBlurSigma, std::move(blur), cropRect));
249 }
250 {
251 SkPictureRecorder recorder;
252 SkCanvas* recordingCanvas = recorder.beginRecording(64, 64);
253
254 SkPaint greenPaint;
255 greenPaint.setColor(SK_ColorGREEN);
256 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
257 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
258 sk_sp<SkImageFilter> pictureFilter(SkImageFilters::Picture(std::move(picture)));
259
260 this->addFilter("picture and blur", SkImageFilters::Blur(
261 kBlurSigma, kBlurSigma, std::move(pictureFilter), cropRect));
262 }
263 {
264 sk_sp<SkImageFilter> paintFilter(SkImageFilters::Shader(
265 SkShaders::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0)));
266
267 this->addFilter("paint and blur", SkImageFilters::Blur(
268 kBlurSigma, kBlurSigma, std::move(paintFilter), cropRect));
269 }
270 this->addFilter("blend", SkImageFilters::Blend(
271 SkBlendMode::kSrc, input, input, cropRect));
272 }
count() const273 int count() const { return fFilters.size(); }
getFilter(int index) const274 SkImageFilter* getFilter(int index) const { return fFilters[index].fFilter.get(); }
getName(int index) const275 const char* getName(int index) const { return fFilters[index].fName; }
needsSaveLayer(int index) const276 bool needsSaveLayer(int index) const { return fFilters[index].fNeedsSaveLayer; }
277 private:
278 struct Filter {
Filter__anon7cf8de7a0111::FilterList::Filter279 Filter() : fName(nullptr), fNeedsSaveLayer(false) {}
Filter__anon7cf8de7a0111::FilterList::Filter280 Filter(const char* name, sk_sp<SkImageFilter> filter, bool needsSaveLayer)
281 : fName(name)
282 , fFilter(std::move(filter))
283 , fNeedsSaveLayer(needsSaveLayer) {
284 }
285 const char* fName;
286 sk_sp<SkImageFilter> fFilter;
287 bool fNeedsSaveLayer;
288 };
addFilter(const char * name,sk_sp<SkImageFilter> filter,bool needsSaveLayer=false)289 void addFilter(const char* name, sk_sp<SkImageFilter> filter, bool needsSaveLayer = false) {
290 fFilters.push_back(Filter(name, std::move(filter), needsSaveLayer));
291 }
292
293 TArray<Filter> fFilters;
294 };
295
296 } // namespace
297
make_context(const SkIRect & out,const SkSpecialImage * src)298 static skif::Context make_context(const SkIRect& out, const SkSpecialImage* src) {
299 sk_sp<skif::Backend> backend;
300 if (src->isGaneshBacked()) {
301 backend = skif::MakeGaneshBackend(sk_ref_sp(src->getContext()), kTestSurfaceOrigin,
302 src->props(), src->colorType());
303 } else {
304 backend = skif::MakeRasterBackend(src->props(), src->colorType());
305 }
306
307 return skif::Context{std::move(backend),
308 skif::Mapping{SkM44()},
309 skif::LayerSpace<SkIRect>{out},
310 skif::FilterResult{sk_ref_sp(src)},
311 src->getColorSpace(),
312 /*stats=*/nullptr};
313 }
make_context(int outWidth,int outHeight,const SkSpecialImage * src)314 static skif::Context make_context(int outWidth, int outHeight, const SkSpecialImage* src) {
315 return make_context(SkIRect::MakeWH(outWidth, outHeight), src);
316 }
317
make_small_image()318 static sk_sp<SkImage> make_small_image() {
319 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize)));
320 SkCanvas* canvas = surface->getCanvas();
321 canvas->clear(0x00000000);
322 SkPaint darkPaint;
323 darkPaint.setColor(0xFF804020);
324 SkPaint lightPaint;
325 lightPaint.setColor(0xFF244484);
326 const int kRectSize = kBitmapSize / 4;
327 static_assert(kBitmapSize % 4 == 0, "bitmap size not multiple of 4");
328
329 for (int y = 0; y < kBitmapSize; y += kRectSize) {
330 for (int x = 0; x < kBitmapSize; x += kRectSize) {
331 canvas->save();
332 canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
333 canvas->drawRect(
334 SkRect::MakeXYWH(0, 0, kRectSize, kRectSize), darkPaint);
335 canvas->drawRect(
336 SkRect::MakeXYWH(kRectSize, 0, kRectSize, kRectSize), lightPaint);
337 canvas->drawRect(
338 SkRect::MakeXYWH(0, kRectSize, kRectSize, kRectSize), lightPaint);
339 canvas->drawRect(
340 SkRect::MakeXYWH(kRectSize, kRectSize, kRectSize, kRectSize), darkPaint);
341 canvas->restore();
342 }
343 }
344
345 return surface->makeImageSnapshot();
346 }
347
make_scale(float amount,sk_sp<SkImageFilter> input)348 static sk_sp<SkImageFilter> make_scale(float amount, sk_sp<SkImageFilter> input) {
349 float s = amount;
350 float matrix[20] = { s, 0, 0, 0, 0,
351 0, s, 0, 0, 0,
352 0, 0, s, 0, 0,
353 0, 0, 0, s, 0 };
354 sk_sp<SkColorFilter> filter(SkColorFilters::Matrix(matrix));
355 return SkImageFilters::ColorFilter(std::move(filter), std::move(input));
356 }
357
make_grayscale(sk_sp<SkImageFilter> input,const SkIRect * cropRect)358 static sk_sp<SkImageFilter> make_grayscale(sk_sp<SkImageFilter> input,
359 const SkIRect* cropRect) {
360 float matrix[20];
361 memset(matrix, 0, 20 * sizeof(float));
362 matrix[0] = matrix[5] = matrix[10] = 0.2126f;
363 matrix[1] = matrix[6] = matrix[11] = 0.7152f;
364 matrix[2] = matrix[7] = matrix[12] = 0.0722f;
365 matrix[18] = 1.0f;
366 sk_sp<SkColorFilter> filter(SkColorFilters::Matrix(matrix));
367 return SkImageFilters::ColorFilter(std::move(filter), std::move(input), cropRect);
368 }
369
make_blue(sk_sp<SkImageFilter> input,const SkIRect * cropRect)370 static sk_sp<SkImageFilter> make_blue(sk_sp<SkImageFilter> input, const SkIRect* cropRect) {
371 sk_sp<SkColorFilter> filter(SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kSrcIn));
372 return SkImageFilters::ColorFilter(std::move(filter), std::move(input), cropRect);
373 }
374
375
create_empty_device(GrRecordingContext * rContext,int widthHeight)376 static sk_sp<SkDevice> create_empty_device(GrRecordingContext* rContext, int widthHeight) {
377
378 const SkImageInfo ii = SkImageInfo::Make({ widthHeight, widthHeight },
379 kRGBA_8888_SkColorType,
380 kPremul_SkAlphaType);
381
382 if (rContext) {
383 return rContext->priv().createDevice(skgpu::Budgeted::kNo, ii, SkBackingFit::kApprox, 1,
384 skgpu::Mipmapped::kNo, skgpu::Protected::kNo,
385 kTestSurfaceOrigin, {},
386 skgpu::ganesh::Device::InitContents::kUninit);
387 } else {
388 SkBitmap bm;
389 SkAssertResult(bm.tryAllocPixels(ii));
390 return sk_make_sp<SkBitmapDevice>(bm, SkSurfaceProps());
391 }
392 }
393
create_empty_special_image(GrRecordingContext * rContext,int widthHeight,SkColor4f color=SkColors::kTransparent)394 static sk_sp<SkSpecialImage> create_empty_special_image(GrRecordingContext* rContext,
395 int widthHeight,
396 SkColor4f color = SkColors::kTransparent) {
397 sk_sp<SkDevice> device = create_empty_device(rContext, widthHeight);
398
399 SkASSERT(device);
400
401 SkPaint p;
402 p.setColor4f(color, /*colorSpace=*/nullptr);
403 p.setBlendMode(SkBlendMode::kSrc);
404 device->drawPaint(p);
405 return device->snapSpecial(SkIRect::MakeWH(widthHeight, widthHeight));
406 }
407
408
DEF_TEST(ImageFilter,reporter)409 DEF_TEST(ImageFilter, reporter) {
410 {
411 // Check that a color matrix filter followed by a color matrix filter
412 // concatenates into a single filter.
413 sk_sp<SkImageFilter> doubleBrightness(make_scale(2.0f, nullptr));
414 sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, std::move(doubleBrightness)));
415 REPORTER_ASSERT(reporter, nullptr == halfBrightness->getInput(0));
416 SkColorFilter* cf;
417 REPORTER_ASSERT(reporter, halfBrightness->asColorFilter(&cf));
418 cf->unref();
419 }
420
421 {
422 // Check that a color filter image filter without a crop rect can be
423 // expressed as a color filter.
424 sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
425 REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
426 }
427
428 {
429 // Check that a colorfilterimage filter without a crop rect but with an input
430 // that is another colorfilterimage can be expressed as a colorfilter (composed).
431 sk_sp<SkImageFilter> mode(make_blue(nullptr, nullptr));
432 sk_sp<SkImageFilter> gray(make_grayscale(std::move(mode), nullptr));
433 REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
434 }
435
436 {
437 // Test that if we exceed the limit of what ComposeColorFilter can combine, we still
438 // can build the DAG and won't assert if we call asColorFilter.
439 sk_sp<SkImageFilter> filter(make_blue(nullptr, nullptr));
440 const int kWayTooManyForComposeColorFilter = 100;
441 for (int i = 0; i < kWayTooManyForComposeColorFilter; ++i) {
442 filter = make_blue(filter, nullptr);
443 // the first few of these will succeed, but after we hit the internal limit,
444 // it will then return false.
445 (void)filter->asColorFilter(nullptr);
446 }
447 }
448
449 {
450 // Check that a color filter image filter with a crop rect cannot
451 // be expressed as a color filter.
452 SkIRect cropRect = SkIRect::MakeWH(100, 100);
453 sk_sp<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect));
454 REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(nullptr));
455 }
456
457 {
458 // Check that two non-commutative matrices are concatenated in
459 // the correct order.
460 float blueToRedMatrix[20] = { 0 };
461 blueToRedMatrix[2] = blueToRedMatrix[18] = 1;
462 float redToGreenMatrix[20] = { 0 };
463 redToGreenMatrix[5] = redToGreenMatrix[18] = 1;
464 sk_sp<SkColorFilter> blueToRed(SkColorFilters::Matrix(blueToRedMatrix));
465 sk_sp<SkImageFilter> filter1(SkImageFilters::ColorFilter(std::move(blueToRed), nullptr));
466 sk_sp<SkColorFilter> redToGreen(SkColorFilters::Matrix(redToGreenMatrix));
467 sk_sp<SkImageFilter> filter2(SkImageFilters::ColorFilter(std::move(redToGreen),
468 std::move(filter1)));
469
470 SkBitmap result;
471 result.allocN32Pixels(kBitmapSize, kBitmapSize);
472
473 SkPaint paint;
474 paint.setColor(SK_ColorBLUE);
475 paint.setImageFilter(std::move(filter2));
476 SkCanvas canvas(result);
477 canvas.clear(0x0);
478 SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
479 canvas.drawRect(rect, paint);
480 uint32_t pixel = *result.getAddr32(0, 0);
481 // The result here should be green, since we have effectively shifted blue to green.
482 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
483 }
484
485 {
486 // Tests pass by not asserting
487 sk_sp<SkImage> image(make_small_image());
488 SkBitmap result;
489 result.allocN32Pixels(kBitmapSize, kBitmapSize);
490
491 {
492 // This tests for :
493 // 1 ) location at (0,0,1)
494 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
495 // 2 ) location and target at same value
496 SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ);
497 // 3 ) large negative specular exponent value
498 SkScalar specularExponent = -1000;
499
500 sk_sp<SkImageFilter> bmSrc(SkImageFilters::Image(std::move(image), {}));
501 SkPaint paint;
502 paint.setImageFilter(SkImageFilters::SpotLitSpecular(
503 location, target, specularExponent, 180,
504 0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
505 std::move(bmSrc)));
506 SkCanvas canvas(result);
507 SkRect r = SkRect::MakeIWH(kBitmapSize, kBitmapSize);
508 canvas.drawRect(r, paint);
509 }
510 }
511 }
512
test_cropRects(skiatest::Reporter * reporter,GrRecordingContext * rContext)513 static void test_cropRects(skiatest::Reporter* reporter, GrRecordingContext* rContext) {
514 // Check that all filters offset to their absolute crop rect,
515 // unaffected by the input crop rect.
516 // Tests pass by not asserting.
517 sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 100));
518 SkASSERT(srcImg);
519
520 SkIRect inputCropRect = SkIRect::MakeXYWH(8, 13, 80, 80);
521 SkIRect cropRect = SkIRect::MakeXYWH(20, 30, 60, 60);
522 sk_sp<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect));
523
524 FilterList filters(input, &cropRect);
525
526 for (int i = 0; i < filters.count(); ++i) {
527 SkImageFilter* filter = filters.getFilter(i);
528 SkIPoint offset;
529 skif::Context ctx = make_context(100, 100, srcImg.get());
530 sk_sp<SkSpecialImage> resultImg(as_IFB(filter)->filterImage(ctx)
531 .imageAndOffset(ctx, &offset));
532 REPORTER_ASSERT(reporter, resultImg, "%s", filters.getName(i));
533 REPORTER_ASSERT(reporter, offset.fX == 20 && offset.fY == 30, "%s", filters.getName(i));
534 }
535 }
536
special_image_to_bitmap(GrDirectContext * dContext,const SkSpecialImage * src,SkBitmap * dst)537 static bool special_image_to_bitmap(GrDirectContext* dContext, const SkSpecialImage* src,
538 SkBitmap* dst) {
539 sk_sp<SkImage> img = src->asImage();
540 if (!img) {
541 return false;
542 }
543
544 if (!dst->tryAllocN32Pixels(src->width(), src->height())) {
545 return false;
546 }
547
548 return img->readPixels(dContext, dst->pixmap(), src->subset().fLeft, src->subset().fTop);
549 }
550
test_negative_blur_sigma(skiatest::Reporter * reporter,GrDirectContext * dContext)551 static void test_negative_blur_sigma(skiatest::Reporter* reporter,
552 GrDirectContext* dContext) {
553 // Check that SkBlurImageFilter will reject a negative sigma on creation, but properly uses the
554 // absolute value of the mapped sigma after CTM application.
555 static const int kWidth = 32, kHeight = 32;
556 static const SkScalar kBlurSigma = SkIntToScalar(5);
557
558 sk_sp<SkImageFilter> positiveFilter(SkImageFilters::Blur(kBlurSigma, kBlurSigma, nullptr));
559 sk_sp<SkImageFilter> negativeFilter(SkImageFilters::Blur(-kBlurSigma, kBlurSigma, nullptr));
560 REPORTER_ASSERT(reporter, !negativeFilter);
561
562 sk_sp<SkImage> gradient = make_gradient_circle(kWidth, kHeight).asImage();
563 sk_sp<SkSpecialImage> imgSrc;
564 if (dContext) {
565 imgSrc = SkSpecialImages::MakeFromTextureImage(
566 dContext, SkIRect::MakeWH(kWidth, kHeight), gradient, SkSurfaceProps());
567 } else {
568 imgSrc = SkSpecialImages::MakeFromRaster(SkIRect::MakeWH(kWidth, kHeight), gradient, {});
569 }
570
571 SkIPoint offset;
572 skif::Context ctx = make_context(32, 32, imgSrc.get());
573
574 sk_sp<SkSpecialImage> positiveResult(
575 as_IFB(positiveFilter)->filterImage(ctx).imageAndOffset(ctx, &offset));
576 REPORTER_ASSERT(reporter, positiveResult);
577
578 const SkM44 negativeScale = SkM44::Scale(-SK_Scalar1, SK_Scalar1);
579 skif::Context negativeCTX = ctx.withNewMapping(skif::Mapping(negativeScale));
580
581 sk_sp<SkSpecialImage> negativeResult(
582 as_IFB(positiveFilter)->filterImage(negativeCTX).imageAndOffset(ctx, &offset));
583 REPORTER_ASSERT(reporter, negativeResult);
584
585
586 SkBitmap positiveResultBM;
587 SkBitmap negativeResultBM;
588
589 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, positiveResult.get(),
590 &positiveResultBM));
591 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, negativeResult.get(),
592 &negativeResultBM));
593
594 for (int y = 0; y < kHeight; y++) {
595 int diffs = memcmp(positiveResultBM.getAddr32(0, y),
596 negativeResultBM.getAddr32(0, y),
597 positiveResultBM.rowBytes());
598 REPORTER_ASSERT(reporter, !diffs);
599 if (diffs) {
600 break;
601 }
602 }
603 }
604
DEF_TEST(ImageFilterNegativeBlurSigma,reporter)605 DEF_TEST(ImageFilterNegativeBlurSigma, reporter) {
606 test_negative_blur_sigma(reporter, nullptr);
607 }
608
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterNegativeBlurSigma_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)609 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterNegativeBlurSigma_Gpu,
610 reporter,
611 ctxInfo,
612 CtsEnforcement::kNever) {
613 test_negative_blur_sigma(reporter, ctxInfo.directContext());
614 }
615
test_morphology_radius_with_mirror_ctm(skiatest::Reporter * reporter,GrDirectContext * dContext)616 static void test_morphology_radius_with_mirror_ctm(skiatest::Reporter* reporter,
617 GrDirectContext* dContext) {
618 // Check that SkMorphologyImageFilter maps the radius correctly when the
619 // CTM contains a mirroring transform.
620 static const int kWidth = 32, kHeight = 32;
621 static const int kRadius = 8;
622
623 sk_sp<SkImageFilter> filter(SkImageFilters::Dilate(kRadius, kRadius, nullptr));
624
625 SkBitmap bitmap;
626 bitmap.allocN32Pixels(kWidth, kHeight);
627 SkCanvas canvas(bitmap);
628 canvas.clear(SK_ColorTRANSPARENT);
629 SkPaint paint;
630 paint.setColor(SK_ColorWHITE);
631 canvas.drawRect(SkRect::MakeXYWH(kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2),
632 paint);
633 sk_sp<SkImage> image = bitmap.asImage();
634 sk_sp<SkSpecialImage> imgSrc;
635 if (dContext) {
636 imgSrc = SkSpecialImages::MakeFromTextureImage(
637 dContext, SkIRect::MakeWH(kWidth, kHeight), image, SkSurfaceProps());
638 } else {
639 imgSrc = SkSpecialImages::MakeFromRaster(SkIRect::MakeWH(kWidth, kHeight), image, {});
640 }
641
642 SkIPoint offset;
643 skif::Context ctx = make_context(32, 32, imgSrc.get());
644
645 sk_sp<SkSpecialImage> normalResult(
646 as_IFB(filter)->filterImage(ctx).imageAndOffset(ctx, &offset));
647 REPORTER_ASSERT(reporter, normalResult);
648
649 SkM44 mirrorX = SkM44::Translate(0, 32);
650 mirrorX.preScale(SK_Scalar1, -SK_Scalar1);
651 skif::Context mirrorXCTX = ctx.withNewMapping(skif::Mapping(mirrorX));
652
653 sk_sp<SkSpecialImage> mirrorXResult(
654 as_IFB(filter)->filterImage(mirrorXCTX).imageAndOffset(ctx, &offset));
655 REPORTER_ASSERT(reporter, mirrorXResult);
656
657 SkM44 mirrorY = SkM44::Translate(32, 0);
658 mirrorY.preScale(-SK_Scalar1, SK_Scalar1);
659 skif::Context mirrorYCTX = ctx.withNewMapping(skif::Mapping(mirrorY));
660
661 sk_sp<SkSpecialImage> mirrorYResult(
662 as_IFB(filter)->filterImage(mirrorYCTX).imageAndOffset(ctx, &offset));
663 REPORTER_ASSERT(reporter, mirrorYResult);
664
665 SkBitmap normalResultBM, mirrorXResultBM, mirrorYResultBM;
666
667 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, normalResult.get(),
668 &normalResultBM));
669 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, mirrorXResult.get(),
670 &mirrorXResultBM));
671 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, mirrorYResult.get(),
672 &mirrorYResultBM));
673
674 for (int y = 0; y < kHeight; y++) {
675 int diffs = memcmp(normalResultBM.getAddr32(0, y),
676 mirrorXResultBM.getAddr32(0, y),
677 normalResultBM.rowBytes());
678 REPORTER_ASSERT(reporter, !diffs);
679 if (diffs) {
680 break;
681 }
682 diffs = memcmp(normalResultBM.getAddr32(0, y),
683 mirrorYResultBM.getAddr32(0, y),
684 normalResultBM.rowBytes());
685 REPORTER_ASSERT(reporter, !diffs);
686 if (diffs) {
687 break;
688 }
689 }
690 }
691
DEF_TEST(MorphologyFilterRadiusWithMirrorCTM,reporter)692 DEF_TEST(MorphologyFilterRadiusWithMirrorCTM, reporter) {
693 test_morphology_radius_with_mirror_ctm(reporter, nullptr);
694 }
695
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(MorphologyFilterRadiusWithMirrorCTM_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)696 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(MorphologyFilterRadiusWithMirrorCTM_Gpu,
697 reporter,
698 ctxInfo,
699 CtsEnforcement::kNever) {
700 test_morphology_radius_with_mirror_ctm(reporter, ctxInfo.directContext());
701 }
702
test_zero_blur_sigma(skiatest::Reporter * reporter,GrDirectContext * dContext)703 static void test_zero_blur_sigma(skiatest::Reporter* reporter, GrDirectContext* dContext) {
704 // Check that SkBlurImageFilter with a zero sigma and a non-zero srcOffset works correctly.
705 SkIRect cropRect = SkIRect::MakeXYWH(5, 0, 5, 10);
706 sk_sp<SkImageFilter> input(SkImageFilters::Offset(0, 0, nullptr, &cropRect));
707 sk_sp<SkImageFilter> filter(SkImageFilters::Blur(0, 0, std::move(input), &cropRect));
708
709 sk_sp<SkSpecialImage> image = create_empty_special_image(dContext, 10, SkColors::kGreen);
710
711 SkIPoint offset;
712 skif::Context ctx = make_context(32, 32, image.get());
713
714
715 sk_sp<SkSpecialImage> result(as_IFB(filter)->filterImage(ctx).imageAndOffset(ctx, &offset));
716 REPORTER_ASSERT(reporter, offset.fX == 5 && offset.fY == 0);
717 REPORTER_ASSERT(reporter, result);
718 REPORTER_ASSERT(reporter, result->width() == 5 && result->height() == 10);
719
720 SkBitmap resultBM;
721
722 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, result.get(), &resultBM));
723
724 for (int y = 0; y < resultBM.height(); y++) {
725 for (int x = 0; x < resultBM.width(); x++) {
726 bool diff = *resultBM.getAddr32(x, y) != SK_ColorGREEN;
727 REPORTER_ASSERT(reporter, !diff);
728 if (diff) {
729 break;
730 }
731 }
732 }
733 }
734
DEF_TEST(ImageFilterZeroBlurSigma,reporter)735 DEF_TEST(ImageFilterZeroBlurSigma, reporter) {
736 test_zero_blur_sigma(reporter, nullptr);
737 }
738
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterZeroBlurSigma_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)739 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterZeroBlurSigma_Gpu,
740 reporter,
741 ctxInfo,
742 CtsEnforcement::kNever) {
743 test_zero_blur_sigma(reporter, ctxInfo.directContext());
744 }
745
746 // Tests that, even when an upstream filter has returned null (due to failure or clipping), a
747 // downstream filter that affects transparent black still does so even with a nullptr input.
test_fail_affects_transparent_black(skiatest::Reporter * reporter,GrDirectContext * dContext)748 static void test_fail_affects_transparent_black(skiatest::Reporter* reporter,
749 GrDirectContext* dContext) {
750 sk_sp<SkImageFilter> failFilter = SkImageFilters::Empty();
751 sk_sp<SkSpecialImage> source(create_empty_special_image(dContext, 5));
752 skif::Context ctx = make_context(1, 1, source.get());
753
754 sk_sp<SkColorFilter> green(SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kSrc));
755 SkASSERT(as_CFB(green)->affectsTransparentBlack());
756 sk_sp<SkImageFilter> greenFilter(SkImageFilters::ColorFilter(std::move(green),
757 std::move(failFilter)));
758 SkIPoint offset;
759 sk_sp<SkSpecialImage> result(as_IFB(greenFilter)->filterImage(ctx)
760 .imageAndOffset(ctx, &offset));
761 REPORTER_ASSERT(reporter, nullptr != result.get());
762 if (result) {
763 SkBitmap resultBM;
764 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, result.get(), &resultBM));
765 REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
766 }
767 }
768
DEF_TEST(ImageFilterFailAffectsTransparentBlack,reporter)769 DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) {
770 test_fail_affects_transparent_black(reporter, nullptr);
771 }
772
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterFailAffectsTransparentBlack_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)773 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterFailAffectsTransparentBlack_Gpu,
774 reporter,
775 ctxInfo,
776 CtsEnforcement::kNever) {
777 test_fail_affects_transparent_black(reporter, ctxInfo.directContext());
778 }
779
DEF_TEST(ImageFilterDrawTiled,reporter)780 DEF_TEST(ImageFilterDrawTiled, reporter) {
781 // Check that all filters when drawn tiled (with subsequent clip rects) exactly
782 // match the same filters drawn with a single full-canvas bitmap draw.
783 // Tests pass by not asserting.
784
785 FilterList filters(nullptr);
786
787 SkBitmap untiledResult, tiledResult;
788 const int width = 64, height = 64;
789 untiledResult.allocN32Pixels(width, height);
790 tiledResult.allocN32Pixels(width, height);
791 SkCanvas tiledCanvas(tiledResult);
792 SkCanvas untiledCanvas(untiledResult);
793 const int tileSize = 8;
794
795 SkPaint textPaint;
796 textPaint.setColor(SK_ColorWHITE);
797 SkFont font(ToolUtils::DefaultPortableTypeface(), height);
798
799 const char* text = "ABC";
800 const SkScalar yPos = SkIntToScalar(height);
801
802 for (int scale = 1; scale <= 2; ++scale) {
803 for (int i = 0; i < filters.count(); ++i) {
804 SkPaint combinedPaint;
805 combinedPaint.setColor(SK_ColorWHITE);
806 combinedPaint.setImageFilter(sk_ref_sp(filters.getFilter(i)));
807
808 untiledCanvas.clear(SK_ColorTRANSPARENT);
809 untiledCanvas.save();
810 untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
811 untiledCanvas.drawString(text, 0, yPos, font, combinedPaint);
812 untiledCanvas.restore();
813
814 tiledCanvas.clear(SK_ColorTRANSPARENT);
815 for (int y = 0; y < height; y += tileSize) {
816 for (int x = 0; x < width; x += tileSize) {
817 tiledCanvas.save();
818 const SkRect clipRect = SkRect::MakeXYWH(x, y, tileSize, tileSize);
819 tiledCanvas.clipRect(clipRect);
820 if (filters.needsSaveLayer(i)) {
821 tiledCanvas.saveLayer(nullptr, &combinedPaint);
822 tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
823 tiledCanvas.drawString(text, 0, yPos, font, textPaint);
824 tiledCanvas.restore();
825 } else {
826 tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
827 tiledCanvas.drawString(text, 0, yPos, font, combinedPaint);
828 }
829
830 tiledCanvas.restore();
831 }
832 }
833
834 if (!ToolUtils::equal_pixels(untiledResult, tiledResult)) {
835 SkString encoded;
836 SkString errString("Tiled image filter doesn't match untiled reference");
837 errString.append("\nExpected: ");
838 if (ToolUtils::BitmapToBase64DataURI(untiledResult, &encoded)) {
839 errString.append(encoded);
840 } else {
841 errString.append("failed to encode");
842 }
843
844 errString.append("\nActual: ");
845 if (ToolUtils::BitmapToBase64DataURI(tiledResult, &encoded)) {
846 errString.append(encoded);
847 } else {
848 errString.append("failed to encode");
849 }
850
851 ERRORF(reporter, "%s\n%s", filters.getName(i), errString.c_str());
852 }
853 }
854 }
855 }
856
draw_saveLayer_picture(int width,int height,int tileSize,SkBBHFactory * factory,SkBitmap * result)857 static void draw_saveLayer_picture(int width, int height, int tileSize,
858 SkBBHFactory* factory, SkBitmap* result) {
859
860 SkMatrix matrix;
861 matrix.setTranslate(SkIntToScalar(50), 0);
862
863 sk_sp<SkColorFilter> cf(SkColorFilters::Blend(SK_ColorWHITE, SkBlendMode::kSrc));
864 sk_sp<SkImageFilter> cfif(SkImageFilters::ColorFilter(std::move(cf), nullptr));
865 sk_sp<SkImageFilter> imageFilter(SkImageFilters::MatrixTransform(matrix,
866 SkSamplingOptions(),
867 std::move(cfif)));
868
869 SkPaint paint;
870 paint.setImageFilter(std::move(imageFilter));
871 SkPictureRecorder recorder;
872 SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
873 SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
874 SkIntToScalar(height),
875 factory);
876 recordingCanvas->translate(-55, 0);
877 recordingCanvas->saveLayer(&bounds, &paint);
878 recordingCanvas->restore();
879 sk_sp<SkPicture> picture1(recorder.finishRecordingAsPicture());
880
881 result->allocN32Pixels(width, height);
882 SkCanvas canvas(*result);
883 canvas.clear(0);
884 canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
885 canvas.drawPicture(picture1.get());
886 }
887
DEF_TEST(ImageFilterDrawMatrixBBH,reporter)888 DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
889 // Check that matrix filter when drawn tiled with BBH exactly
890 // matches the same thing drawn without BBH.
891 // Tests pass by not asserting.
892
893 const int width = 200, height = 200;
894 const int tileSize = 100;
895 SkBitmap result1, result2;
896 SkRTreeFactory factory;
897
898 draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
899 draw_saveLayer_picture(width, height, tileSize, nullptr, &result2);
900
901 for (int y = 0; y < height; y++) {
902 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
903 REPORTER_ASSERT(reporter, !diffs);
904 if (diffs) {
905 break;
906 }
907 }
908 }
909
make_blur(sk_sp<SkImageFilter> input)910 static sk_sp<SkImageFilter> make_blur(sk_sp<SkImageFilter> input) {
911 return SkImageFilters::Blur(SK_Scalar1, SK_Scalar1, std::move(input));
912 }
913
make_drop_shadow(sk_sp<SkImageFilter> input)914 static sk_sp<SkImageFilter> make_drop_shadow(sk_sp<SkImageFilter> input) {
915 return SkImageFilters::DropShadow(100, 100, 10, 10, SK_ColorBLUE, std::move(input));
916 }
917
DEF_TEST(ImageFilterBlurThenShadowBounds,reporter)918 DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
919 sk_sp<SkImageFilter> filter1(make_blur(nullptr));
920 sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
921
922 static const SkIRect kContentBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
923
924 // For output, the [0,0,100,100] source is expanded to [-3,-3,103,103] by the initial blur.
925 // The drop shadow is translated by [100,100] and further outset by 30px -> [67,67,233,233],
926 // The blend unions the inner blur result with the drop shadow to get [-3,-3,233,233].
927 static const SkIRect kExpectedOutputBounds = SkIRect::MakeLTRB(-3, -3, 233, 233);
928 SkIRect outputBounds = filter2->filterBounds(kContentBounds,
929 SkMatrix::I(),
930 SkImageFilter::kForward_MapDirection);
931 REPORTER_ASSERT(reporter, outputBounds == kExpectedOutputBounds);
932
933 // For input, it should be able to restrict itself to the source content.
934 SkIRect inputBounds = filter2->filterBounds(kExpectedOutputBounds,
935 SkMatrix::I(),
936 SkImageFilter::kReverse_MapDirection,
937 &kContentBounds);
938
939 REPORTER_ASSERT(reporter, inputBounds == kContentBounds);
940 }
941
DEF_TEST(ImageFilterShadowThenBlurBounds,reporter)942 DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
943 sk_sp<SkImageFilter> filter1(make_drop_shadow(nullptr));
944 sk_sp<SkImageFilter> filter2(make_blur(std::move(filter1)));
945
946 static const SkIRect kContentBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
947 // For output, the [0,0,100,100] source is translated by 100px and outset by 30px for the drop
948 // shadow = [70,70,230,230], then blended back with its original to get [0,0,230,230]. This is
949 // then outset by 3px for the outer blur to get [-3,-3,233,233].
950 static const SkIRect kExpectedOutputBounds = SkIRect::MakeLTRB(-3, -3, 233, 233);
951 SkIRect outputBounds = filter2->filterBounds(kContentBounds,
952 SkMatrix::I(),
953 SkImageFilter::kForward_MapDirection);
954 REPORTER_ASSERT(reporter, outputBounds == kExpectedOutputBounds);
955
956 // For input, it should be able to restrict itself to the source content.
957 SkIRect inputBounds = filter2->filterBounds(kExpectedOutputBounds,
958 SkMatrix::I(),
959 SkImageFilter::kReverse_MapDirection,
960 &kContentBounds);
961 REPORTER_ASSERT(reporter, inputBounds == kContentBounds);
962 }
963
DEF_TEST(ImageFilterDilateThenBlurBounds,reporter)964 DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
965 sk_sp<SkImageFilter> filter1(SkImageFilters::Dilate(2, 2, nullptr));
966 sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
967
968 static const SkIRect kContentBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
969 // For output, the [0,0,100,100] source is outset by dilate radius (2px) to [-2,-2,102,102].
970 // This is then translated by 100px and outset by 30px for the drop shadow = [68,68,232,232].
971 // Finally this is joined with the original dilate result to get [-2,-2,232,232].
972 static const SkIRect kExpectedOutputBounds = SkIRect::MakeLTRB(-2, -2, 232, 232);
973 SkIRect outputBounds = filter2->filterBounds(kContentBounds,
974 SkMatrix::I(),
975 SkImageFilter::kForward_MapDirection);
976 REPORTER_ASSERT(reporter, outputBounds == kExpectedOutputBounds);
977
978 // For input, it should be able to restrict itself to the source content.
979 SkIRect inputBounds = filter2->filterBounds(kExpectedOutputBounds,
980 SkMatrix::I(),
981 SkImageFilter::kReverse_MapDirection,
982 &kContentBounds);
983 REPORTER_ASSERT(reporter, inputBounds == kContentBounds);
984 }
985
DEF_TEST(ImageFilterScaledBlurRadius,reporter)986 DEF_TEST(ImageFilterScaledBlurRadius, reporter) {
987 // Each blur should spread 3*sigma, so 3 for the blur and 30 for the shadow
988 // (before the CTM). Bounds should be computed correctly in the presence of
989 // a (possibly negative) scale.
990 sk_sp<SkImageFilter> blur(make_blur(nullptr));
991 sk_sp<SkImageFilter> dropShadow(make_drop_shadow(nullptr));
992 {
993 // Uniform scale by 2.
994 SkMatrix scaleMatrix;
995 scaleMatrix.setScale(2, 2);
996 static const SkIRect kBounds = SkIRect::MakeLTRB(0, 0, 200, 200);
997
998 static const SkIRect kExpectedBlurBounds = SkIRect::MakeLTRB(-6, -6, 206, 206);
999 SkIRect blurBounds = blur->filterBounds(
1000 kBounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
1001 REPORTER_ASSERT(reporter, blurBounds == kExpectedBlurBounds);
1002 SkIRect reverseBlurBounds = blur->filterBounds(
1003 kExpectedBlurBounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &kBounds);
1004 REPORTER_ASSERT(reporter, reverseBlurBounds == kBounds);
1005
1006 static const SkIRect kExpectedShadowBounds = SkIRect::MakeLTRB(0, 0, 460, 460);
1007 SkIRect shadowBounds = dropShadow->filterBounds(
1008 kBounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
1009 REPORTER_ASSERT(reporter, shadowBounds == kExpectedShadowBounds);
1010
1011 SkIRect reverseShadowBounds = dropShadow->filterBounds(
1012 kExpectedShadowBounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &kBounds);
1013 REPORTER_ASSERT(reporter, reverseShadowBounds == kBounds);
1014 }
1015 {
1016 // Vertical flip.
1017 SkMatrix scaleMatrix;
1018 scaleMatrix.setScale(1, -1);
1019 static const SkIRect kBounds = SkIRect::MakeLTRB(0, -100, 100, 0);
1020
1021 static const SkIRect kExpectedBlurBounds = SkIRect::MakeLTRB(-3, -103, 103, 3);
1022 SkIRect blurBounds = blur->filterBounds(
1023 kBounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
1024 REPORTER_ASSERT(reporter, blurBounds == kExpectedBlurBounds);
1025 SkIRect reverseBlurBounds = blur->filterBounds(
1026 kExpectedBlurBounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &kBounds);
1027 REPORTER_ASSERT(reporter, reverseBlurBounds == kBounds);
1028
1029 SkIRect kExpectedShadowBounds = SkIRect::MakeLTRB(0, -230, 230, 0);
1030 SkIRect shadowBounds = dropShadow->filterBounds(
1031 kBounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
1032 REPORTER_ASSERT(reporter, shadowBounds == kExpectedShadowBounds);
1033 SkIRect reverseShadowBounds = dropShadow->filterBounds(
1034 kExpectedShadowBounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &kBounds);
1035 REPORTER_ASSERT(reporter, reverseShadowBounds == kBounds);
1036 }
1037 }
1038
DEF_TEST(ImageFilterComposedBlurFastBounds,reporter)1039 DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
1040 sk_sp<SkImageFilter> filter1(make_blur(nullptr));
1041 sk_sp<SkImageFilter> filter2(make_blur(nullptr));
1042 sk_sp<SkImageFilter> composedFilter(SkImageFilters::Compose(std::move(filter1),
1043 std::move(filter2)));
1044
1045 static const SkRect kBoundsSrc = SkRect::MakeIWH(100, 100);
1046 static const SkRect kExpectedBounds = SkRect::MakeXYWH(-6, -6, 112, 112);
1047 SkRect boundsDst = composedFilter->computeFastBounds(kBoundsSrc);
1048
1049 REPORTER_ASSERT(reporter, boundsDst == kExpectedBounds);
1050 }
1051
DEF_TEST(ImageFilterUnionBounds,reporter)1052 DEF_TEST(ImageFilterUnionBounds, reporter) {
1053 sk_sp<SkImageFilter> offset(SkImageFilters::Offset(50, 0, nullptr));
1054 // Regardless of which order they appear in, the image filter bounds should
1055 // be combined correctly.
1056 {
1057 sk_sp<SkImageFilter> composite(SkImageFilters::Blend(SkBlendMode::kSrcOver, offset));
1058 SkRect bounds = SkRect::MakeIWH(100, 100);
1059 // Intentionally aliasing here, as that's what the real callers do.
1060 bounds = composite->computeFastBounds(bounds);
1061 REPORTER_ASSERT(reporter, bounds == SkRect::MakeIWH(150, 100));
1062 }
1063 {
1064 sk_sp<SkImageFilter> composite(SkImageFilters::Blend(SkBlendMode::kSrcOver, nullptr,
1065 offset, nullptr));
1066 SkRect bounds = SkRect::MakeIWH(100, 100);
1067 // Intentionally aliasing here, as that's what the real callers do.
1068 bounds = composite->computeFastBounds(bounds);
1069 REPORTER_ASSERT(reporter, bounds == SkRect::MakeIWH(150, 100));
1070 }
1071 }
1072
test_imagefilter_merge_result_size(skiatest::Reporter * reporter,GrRecordingContext * rContext)1073 static void test_imagefilter_merge_result_size(skiatest::Reporter* reporter,
1074 GrRecordingContext* rContext) {
1075 SkBitmap greenBM;
1076 greenBM.allocN32Pixels(20, 20);
1077 greenBM.eraseColor(SK_ColorGREEN);
1078 sk_sp<SkImage> greenImage(greenBM.asImage());
1079 sk_sp<SkImageFilter> source(SkImageFilters::Image(std::move(greenImage), {}));
1080 sk_sp<SkImageFilter> merge(SkImageFilters::Merge(source, source));
1081
1082 sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 1));
1083
1084 skif::Context ctx = make_context(100, 100, srcImg.get());
1085 SkIPoint offset;
1086
1087 sk_sp<SkSpecialImage> resultImg(as_IFB(merge)->filterImage(ctx).imageAndOffset(ctx, &offset));
1088 REPORTER_ASSERT(reporter, resultImg);
1089
1090 REPORTER_ASSERT(reporter, resultImg->width() == 20 && resultImg->height() == 20);
1091 }
1092
DEF_TEST(ImageFilterMergeResultSize,reporter)1093 DEF_TEST(ImageFilterMergeResultSize, reporter) {
1094 test_imagefilter_merge_result_size(reporter, nullptr);
1095 }
1096
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMergeResultSize_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1097 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMergeResultSize_Gpu,
1098 reporter,
1099 ctxInfo,
1100 CtsEnforcement::kNever) {
1101 test_imagefilter_merge_result_size(reporter, ctxInfo.directContext());
1102 }
1103
draw_blurred_rect(SkCanvas * canvas)1104 static void draw_blurred_rect(SkCanvas* canvas) {
1105 SkPaint filterPaint;
1106 filterPaint.setColor(SK_ColorWHITE);
1107 filterPaint.setImageFilter(SkImageFilters::Blur(SkIntToScalar(8), 0, nullptr));
1108 canvas->saveLayer(nullptr, &filterPaint);
1109 SkPaint whitePaint;
1110 whitePaint.setColor(SK_ColorWHITE);
1111 canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
1112 canvas->restore();
1113 }
1114
draw_picture_clipped(SkCanvas * canvas,const SkRect & clipRect,const SkPicture * picture)1115 static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
1116 canvas->save();
1117 canvas->clipRect(clipRect);
1118 canvas->drawPicture(picture);
1119 canvas->restore();
1120 }
1121
DEF_TEST(ImageFilterDrawTiledBlurRTree,reporter)1122 DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
1123 // Check that the blur filter when recorded with RTree acceleration,
1124 // and drawn tiled (with subsequent clip rects) exactly
1125 // matches the same filter drawn with without RTree acceleration.
1126 // This tests that the "bleed" from the blur into the otherwise-blank
1127 // tiles is correctly rendered.
1128 // Tests pass by not asserting.
1129
1130 int width = 16, height = 8;
1131 SkBitmap result1, result2;
1132 result1.allocN32Pixels(width, height);
1133 result2.allocN32Pixels(width, height);
1134 SkCanvas canvas1(result1);
1135 SkCanvas canvas2(result2);
1136 int tileSize = 8;
1137
1138 canvas1.clear(0);
1139 canvas2.clear(0);
1140
1141 SkRTreeFactory factory;
1142
1143 SkPictureRecorder recorder1, recorder2;
1144 // The only difference between these two pictures is that one has RTree aceleration.
1145 SkCanvas* recordingCanvas1 = recorder1.beginRecording(width, height);
1146 SkCanvas* recordingCanvas2 = recorder2.beginRecording(width, height, &factory);
1147
1148 draw_blurred_rect(recordingCanvas1);
1149 draw_blurred_rect(recordingCanvas2);
1150 sk_sp<SkPicture> picture1(recorder1.finishRecordingAsPicture());
1151 sk_sp<SkPicture> picture2(recorder2.finishRecordingAsPicture());
1152 for (int y = 0; y < height; y += tileSize) {
1153 for (int x = 0; x < width; x += tileSize) {
1154 SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
1155 draw_picture_clipped(&canvas1, tileRect, picture1.get());
1156 draw_picture_clipped(&canvas2, tileRect, picture2.get());
1157 }
1158 }
1159 for (int y = 0; y < height; y++) {
1160 int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
1161 REPORTER_ASSERT(reporter, !diffs);
1162 if (diffs) {
1163 break;
1164 }
1165 }
1166 }
1167
DEF_TEST(ImageFilterMatrixConvolution,reporter)1168 DEF_TEST(ImageFilterMatrixConvolution, reporter) {
1169 // Check that a 1x3 filter does not cause a spurious assert.
1170 SkScalar kernel[3] = {
1171 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
1172 };
1173 SkISize kernelSize = SkISize::Make(1, 3);
1174 SkScalar gain = SK_Scalar1, bias = 0;
1175 SkIPoint kernelOffset = SkIPoint::Make(0, 0);
1176
1177 sk_sp<SkImageFilter> filter(SkImageFilters::MatrixConvolution(
1178 kernelSize, kernel, gain, bias, kernelOffset, SkTileMode::kRepeat, false, nullptr));
1179
1180 SkBitmap result;
1181 int width = 16, height = 16;
1182 result.allocN32Pixels(width, height);
1183 SkCanvas canvas(result);
1184 canvas.clear(0);
1185
1186 SkPaint paint;
1187 paint.setImageFilter(std::move(filter));
1188 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
1189 canvas.drawRect(rect, paint);
1190 }
1191
DEF_TEST(ImageFilterMatrixConvolutionBorder,reporter)1192 DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
1193 // Check that a filter with borders outside the target bounds
1194 // does not crash.
1195 SkScalar kernel[3] = {
1196 0, 0, 0,
1197 };
1198 SkISize kernelSize = SkISize::Make(3, 1);
1199 SkScalar gain = SK_Scalar1, bias = 0;
1200 SkIPoint kernelOffset = SkIPoint::Make(2, 0);
1201
1202 sk_sp<SkImageFilter> filter(SkImageFilters::MatrixConvolution(
1203 kernelSize, kernel, gain, bias, kernelOffset, SkTileMode::kClamp, true, nullptr));
1204
1205 SkBitmap result;
1206
1207 int width = 10, height = 10;
1208 result.allocN32Pixels(width, height);
1209 SkCanvas canvas(result);
1210 canvas.clear(0);
1211
1212 SkPaint filterPaint;
1213 filterPaint.setImageFilter(std::move(filter));
1214 SkRect bounds = SkRect::MakeIWH(1, 10);
1215 SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
1216 SkPaint rectPaint;
1217 canvas.saveLayer(&bounds, &filterPaint);
1218 canvas.drawRect(rect, rectPaint);
1219 canvas.restore();
1220 }
1221
test_big_kernel(skiatest::Reporter * reporter,GrRecordingContext * rContext)1222 static void test_big_kernel(skiatest::Reporter* reporter, GrRecordingContext* rContext) {
1223 // Check that a kernel that is too big for the GPU still works
1224 SkScalar identityKernel[49] = {
1225 0, 0, 0, 0, 0, 0, 0,
1226 0, 0, 0, 0, 0, 0, 0,
1227 0, 0, 0, 0, 0, 0, 0,
1228 0, 0, 0, 1, 0, 0, 0,
1229 0, 0, 0, 0, 0, 0, 0,
1230 0, 0, 0, 0, 0, 0, 0,
1231 0, 0, 0, 0, 0, 0, 0
1232 };
1233 SkISize kernelSize = SkISize::Make(7, 7);
1234 SkScalar gain = SK_Scalar1, bias = 0;
1235 SkIPoint kernelOffset = SkIPoint::Make(0, 0);
1236
1237 sk_sp<SkImageFilter> filter(SkImageFilters::MatrixConvolution(
1238 kernelSize, identityKernel, gain, bias, kernelOffset,
1239 SkTileMode::kClamp, true, nullptr));
1240
1241 sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 100));
1242 SkASSERT(srcImg);
1243
1244 SkIPoint offset;
1245 skif::Context ctx = make_context(100, 100, srcImg.get());
1246 sk_sp<SkSpecialImage> resultImg(as_IFB(filter)->filterImage(ctx).imageAndOffset(ctx, &offset));
1247 REPORTER_ASSERT(reporter, resultImg);
1248 REPORTER_ASSERT(reporter, SkToBool(rContext) == resultImg->isGaneshBacked());
1249 REPORTER_ASSERT(reporter, resultImg->width() == 100 && resultImg->height() == 100);
1250 REPORTER_ASSERT(reporter, offset.fX == 0 && offset.fY == 0);
1251 }
1252
DEF_TEST(ImageFilterMatrixConvolutionBigKernel,reporter)1253 DEF_TEST(ImageFilterMatrixConvolutionBigKernel, reporter) {
1254 test_big_kernel(reporter, nullptr);
1255 }
1256
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1257 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu,
1258 reporter,
1259 ctxInfo,
1260 CtsEnforcement::kNever) {
1261 test_big_kernel(reporter, ctxInfo.directContext());
1262 }
1263
DEF_TEST(ImageFilterCropRect,reporter)1264 DEF_TEST(ImageFilterCropRect, reporter) {
1265 test_cropRects(reporter, nullptr);
1266 }
1267
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCropRect_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1268 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCropRect_Gpu,
1269 reporter,
1270 ctxInfo,
1271 CtsEnforcement::kNever) {
1272 test_cropRects(reporter, ctxInfo.directContext());
1273 }
1274
DEF_TEST(ImageFilterMatrix,reporter)1275 DEF_TEST(ImageFilterMatrix, reporter) {
1276 SkBitmap temp;
1277 temp.allocN32Pixels(100, 100);
1278 SkCanvas canvas(temp);
1279 canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
1280
1281 const SkM44 expectedMatrix = canvas.getLocalToDevice();
1282
1283 SkRTreeFactory factory;
1284 SkPictureRecorder recorder;
1285 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory);
1286
1287 SkPaint paint;
1288 paint.setImageFilter(sk_sp<SkImageFilter>(new MatrixTestImageFilter(reporter, expectedMatrix)));
1289 recordingCanvas->saveLayer(nullptr, &paint);
1290 SkPaint solidPaint;
1291 solidPaint.setColor(0xFFFFFFFF);
1292 recordingCanvas->save();
1293 recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
1294 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
1295 recordingCanvas->restore(); // scale
1296 recordingCanvas->restore(); // saveLayer
1297
1298 canvas.drawPicture(recorder.finishRecordingAsPicture());
1299 }
1300
test_clipped_picture_imagefilter(skiatest::Reporter * reporter,GrRecordingContext * rContext)1301 static void test_clipped_picture_imagefilter(skiatest::Reporter* reporter,
1302 GrRecordingContext* rContext) {
1303 sk_sp<SkPicture> picture;
1304
1305 {
1306 SkRTreeFactory factory;
1307 SkPictureRecorder recorder;
1308 SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory);
1309
1310 // Create an SkPicture which simply draws a green 1x1 rectangle.
1311 SkPaint greenPaint;
1312 greenPaint.setColor(SK_ColorGREEN);
1313 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
1314 picture = recorder.finishRecordingAsPicture();
1315 }
1316
1317 sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 2));
1318
1319 sk_sp<SkImageFilter> imageFilter(SkImageFilters::Picture(picture));
1320
1321 SkIPoint offset;
1322 skif::Context ctx = make_context(SkIRect::MakeXYWH(1,1,1,1), srcImg.get());
1323
1324 sk_sp<SkSpecialImage> resultImage(
1325 as_IFB(imageFilter)->filterImage(ctx).imageAndOffset(ctx, &offset));
1326 REPORTER_ASSERT(reporter, !resultImage);
1327 }
1328
DEF_TEST(ImageFilterClippedPictureImageFilter,reporter)1329 DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
1330 test_clipped_picture_imagefilter(reporter, nullptr);
1331 }
1332
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterClippedPictureImageFilter_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1333 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterClippedPictureImageFilter_Gpu,
1334 reporter,
1335 ctxInfo,
1336 CtsEnforcement::kNever) {
1337 test_clipped_picture_imagefilter(reporter, ctxInfo.directContext());
1338 }
1339
DEF_TEST(ImageFilterEmptySaveLayer,reporter)1340 DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
1341 // Even when there's an empty saveLayer()/restore(), ensure that an image
1342 // filter or color filter which affects transparent black still draws.
1343
1344 SkBitmap bitmap;
1345 bitmap.allocN32Pixels(10, 10);
1346 SkCanvas canvas(bitmap);
1347
1348 SkRTreeFactory factory;
1349 SkPictureRecorder recorder;
1350
1351 sk_sp<SkColorFilter> green(SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kSrc));
1352 sk_sp<SkImageFilter> imageFilter(SkImageFilters::ColorFilter(green, nullptr));
1353 SkPaint imageFilterPaint;
1354 imageFilterPaint.setImageFilter(std::move(imageFilter));
1355 SkPaint colorFilterPaint;
1356 colorFilterPaint.setColorFilter(green);
1357
1358 SkRect bounds = SkRect::MakeIWH(10, 10);
1359
1360 SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory);
1361 recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
1362 recordingCanvas->restore();
1363 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1364
1365 canvas.clear(0);
1366 canvas.drawPicture(picture);
1367 uint32_t pixel = *bitmap.getAddr32(0, 0);
1368 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1369
1370 recordingCanvas = recorder.beginRecording(10, 10, &factory);
1371 recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
1372 recordingCanvas->restore();
1373 sk_sp<SkPicture> picture2(recorder.finishRecordingAsPicture());
1374
1375 canvas.clear(0);
1376 canvas.drawPicture(picture2);
1377 pixel = *bitmap.getAddr32(0, 0);
1378 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1379
1380 recordingCanvas = recorder.beginRecording(10, 10, &factory);
1381 recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
1382 recordingCanvas->restore();
1383 sk_sp<SkPicture> picture3(recorder.finishRecordingAsPicture());
1384
1385 canvas.clear(0);
1386 canvas.drawPicture(picture3);
1387 pixel = *bitmap.getAddr32(0, 0);
1388 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1389 }
1390
test_huge_blur(SkCanvas * canvas,skiatest::Reporter * reporter)1391 static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) {
1392 SkBitmap bitmap;
1393 bitmap.allocN32Pixels(100, 100);
1394 bitmap.eraseARGB(0, 0, 0, 0);
1395
1396 // Check that a blur with a very large radius does not crash or assert.
1397 SkPaint paint;
1398 paint.setImageFilter(SkImageFilters::Blur(SkIntToScalar(1<<30), SkIntToScalar(1<<30), nullptr));
1399 canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint);
1400 }
1401
DEF_TEST(HugeBlurImageFilter,reporter)1402 DEF_TEST(HugeBlurImageFilter, reporter) {
1403 SkBitmap temp;
1404 temp.allocN32Pixels(100, 100);
1405 SkCanvas canvas(temp);
1406 test_huge_blur(&canvas, reporter);
1407 }
1408
DEF_TEST(ImageFilterMatrixConvolutionTest,reporter)1409 DEF_TEST(ImageFilterMatrixConvolutionTest, reporter) {
1410 SkScalar kernel[1] = { 0 };
1411 SkScalar gain = SK_Scalar1, bias = 0;
1412 SkIPoint kernelOffset = SkIPoint::Make(1, 1);
1413
1414 // Check that an enormous (non-allocatable) kernel gives a nullptr filter.
1415 sk_sp<SkImageFilter> conv(SkImageFilters::MatrixConvolution(
1416 SkISize::Make(1<<30, 1<<30), kernel, gain, bias, kernelOffset,
1417 SkTileMode::kRepeat, false, nullptr));
1418
1419 REPORTER_ASSERT(reporter, nullptr == conv.get());
1420
1421 // Check that a nullptr kernel gives a nullptr filter.
1422 conv = SkImageFilters::MatrixConvolution(
1423 SkISize::Make(1, 1), nullptr, gain, bias, kernelOffset,
1424 SkTileMode::kRepeat, false, nullptr);
1425
1426 REPORTER_ASSERT(reporter, nullptr == conv.get());
1427
1428 // Check that a kernel width < 1 gives a nullptr filter.
1429 conv = SkImageFilters::MatrixConvolution(
1430 SkISize::Make(0, 1), kernel, gain, bias, kernelOffset,
1431 SkTileMode::kRepeat, false, nullptr);
1432
1433 REPORTER_ASSERT(reporter, nullptr == conv.get());
1434
1435 // Check that kernel height < 1 gives a nullptr filter.
1436 conv = SkImageFilters::MatrixConvolution(
1437 SkISize::Make(1, -1), kernel, gain, bias, kernelOffset,
1438 SkTileMode::kRepeat, false, nullptr);
1439
1440 REPORTER_ASSERT(reporter, nullptr == conv.get());
1441 }
1442
test_xfermode_cropped_input(SkSurface * surf,skiatest::Reporter * reporter)1443 static void test_xfermode_cropped_input(SkSurface* surf, skiatest::Reporter* reporter) {
1444 auto canvas = surf->getCanvas();
1445 canvas->clear(SK_ColorRED);
1446
1447 SkBitmap bitmap;
1448 bitmap.allocN32Pixels(1, 1);
1449 bitmap.eraseARGB(255, 255, 255, 255);
1450
1451 sk_sp<SkColorFilter> green(SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kSrcIn));
1452 sk_sp<SkImageFilter> greenFilter(SkImageFilters::ColorFilter(green, nullptr));
1453 SkIRect cropRect = SkIRect::MakeEmpty();
1454 sk_sp<SkImageFilter> croppedOut(SkImageFilters::ColorFilter(green, nullptr, &cropRect));
1455
1456 // Check that an blend image filter whose input has been cropped out still draws the other
1457 // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
1458 SkBlendMode mode = SkBlendMode::kSrcOver;
1459 sk_sp<SkImageFilter> xfermodeNoFg(SkImageFilters::Blend(
1460 mode, greenFilter, croppedOut, nullptr));
1461 sk_sp<SkImageFilter> xfermodeNoBg(SkImageFilters::Blend(
1462 mode, croppedOut, greenFilter, nullptr));
1463 sk_sp<SkImageFilter> xfermodeNoFgNoBg(SkImageFilters::Blend(
1464 mode, croppedOut, croppedOut, nullptr));
1465
1466 SkPaint paint;
1467 paint.setImageFilter(std::move(xfermodeNoFg));
1468 canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); // drawSprite
1469
1470 // xfermodeNoFg is a src-over blend between a green image and a transparent black image,
1471 // so should just be green.
1472 uint32_t pixel;
1473 SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
1474 surf->readPixels(info, &pixel, 4, 0, 0);
1475 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1476
1477 // xfermodeNoBg is the reverse of the above, but because it's src-over the final blend
1478 // between transparent black and green is still green.
1479 canvas->clear(SK_ColorRED); // should be overwritten
1480 paint.setImageFilter(std::move(xfermodeNoBg));
1481 canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); // drawSprite
1482 surf->readPixels(info, &pixel, 4, 0, 0);
1483 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1484
1485 // xfermodeNoFgNoBg is a src-over blend of two empty images, so should produce no change
1486 // to the image.
1487 canvas->clear(SK_ColorRED); // should not be overwritten
1488 paint.setImageFilter(std::move(xfermodeNoFgNoBg));
1489 canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); // drawSprite
1490 surf->readPixels(info, &pixel, 4, 0, 0);
1491 REPORTER_ASSERT(reporter, pixel == SK_ColorRED);
1492 }
1493
DEF_TEST(ImageFilterNestedSaveLayer,reporter)1494 DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
1495 SkBitmap temp;
1496 temp.allocN32Pixels(50, 50);
1497 SkCanvas canvas(temp);
1498 canvas.clear(0x0);
1499
1500 SkBitmap bitmap;
1501 bitmap.allocN32Pixels(10, 10);
1502 bitmap.eraseColor(SK_ColorGREEN);
1503
1504 SkMatrix matrix;
1505 matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
1506 matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
1507 sk_sp<SkImageFilter> matrixFilter(
1508 SkImageFilters::MatrixTransform(matrix, SkSamplingOptions(SkFilterMode::kLinear), nullptr));
1509
1510 // Test that saveLayer() with a filter nested inside another saveLayer() applies the
1511 // correct offset to the filter matrix.
1512 SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
1513 canvas.saveLayer(&bounds1, nullptr);
1514 SkPaint filterPaint;
1515 filterPaint.setImageFilter(std::move(matrixFilter));
1516 SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
1517 canvas.saveLayer(&bounds2, &filterPaint);
1518 SkPaint greenPaint;
1519 greenPaint.setColor(SK_ColorGREEN);
1520 canvas.drawRect(bounds2, greenPaint);
1521 canvas.restore();
1522 canvas.restore();
1523 SkPaint strokePaint;
1524 strokePaint.setStyle(SkPaint::kStroke_Style);
1525 strokePaint.setColor(SK_ColorRED);
1526
1527 SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
1528 uint32_t pixel;
1529 temp.readPixels(info, &pixel, 4, 25, 25);
1530 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1531
1532 // Test that drawSprite() with a filter nested inside a saveLayer() applies the
1533 // correct offset to the filter matrix.
1534 canvas.clear(0x0);
1535 temp.readPixels(info, &pixel, 4, 25, 25);
1536 canvas.saveLayer(&bounds1, nullptr);
1537 canvas.drawImage(bitmap.asImage(), 20, 20, SkSamplingOptions(), &filterPaint); // drawSprite
1538 canvas.restore();
1539
1540 temp.readPixels(info, &pixel, 4, 25, 25);
1541 REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1542 }
1543
DEF_TEST(XfermodeImageFilterCroppedInput,reporter)1544 DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
1545 test_xfermode_cropped_input(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100)).get(),
1546 reporter);
1547 }
1548
test_composed_imagefilter_offset(skiatest::Reporter * reporter,GrRecordingContext * rContext)1549 static void test_composed_imagefilter_offset(skiatest::Reporter* reporter,
1550 GrRecordingContext* rContext) {
1551 sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 100));
1552
1553 SkIRect cropRect = SkIRect::MakeXYWH(1, 0, 20, 20);
1554 sk_sp<SkImageFilter> offsetFilter(SkImageFilters::Offset(0, 0, nullptr, &cropRect));
1555 sk_sp<SkImageFilter> blurFilter(SkImageFilters::Blur(SK_Scalar1, SK_Scalar1,
1556 nullptr, &cropRect));
1557 sk_sp<SkImageFilter> composedFilter(SkImageFilters::Compose(std::move(blurFilter),
1558 std::move(offsetFilter)));
1559 SkIPoint offset;
1560 skif::Context ctx = make_context(100, 100, srcImg.get());
1561
1562 sk_sp<SkSpecialImage> resultImg(
1563 as_IFB(composedFilter)->filterImage(ctx).imageAndOffset(ctx, &offset));
1564 REPORTER_ASSERT(reporter, resultImg);
1565 REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
1566 }
1567
DEF_TEST(ComposedImageFilterOffset,reporter)1568 DEF_TEST(ComposedImageFilterOffset, reporter) {
1569 test_composed_imagefilter_offset(reporter, nullptr);
1570 }
1571
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterOffset_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1572 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterOffset_Gpu,
1573 reporter,
1574 ctxInfo,
1575 CtsEnforcement::kNever) {
1576 test_composed_imagefilter_offset(reporter, ctxInfo.directContext());
1577 }
1578
test_composed_imagefilter_bounds(skiatest::Reporter * reporter,GrDirectContext * dContext)1579 static void test_composed_imagefilter_bounds(skiatest::Reporter* reporter,
1580 GrDirectContext* dContext) {
1581 // The bounds passed to the inner filter must be filtered by the outer
1582 // filter, so that the inner filter produces the pixels that the outer
1583 // filter requires as input. This matters if the outer filter moves pixels.
1584 // Here, accounting for the outer offset is necessary so that the green
1585 // pixels of the picture are not clipped.
1586
1587 SkPictureRecorder recorder;
1588 SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeIWH(200, 100));
1589 recordingCanvas->clipRect(SkRect::MakeXYWH(100, 0, 100, 100));
1590 recordingCanvas->clear(SK_ColorGREEN);
1591 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1592 sk_sp<SkImageFilter> pictureFilter(SkImageFilters::Picture(picture));
1593 SkIRect cropRect = SkIRect::MakeWH(100, 100);
1594 sk_sp<SkImageFilter> offsetFilter(SkImageFilters::Offset(-100, 0, nullptr, &cropRect));
1595 sk_sp<SkImageFilter> composedFilter(SkImageFilters::Compose(std::move(offsetFilter),
1596 std::move(pictureFilter)));
1597
1598 sk_sp<SkSpecialImage> sourceImage(create_empty_special_image(dContext, 100));
1599 skif::Context ctx = make_context(100, 100, sourceImage.get());
1600
1601 SkIPoint offset;
1602 sk_sp<SkSpecialImage> result(
1603 as_IFB(composedFilter)->filterImage(ctx).imageAndOffset(ctx, &offset));
1604 REPORTER_ASSERT(reporter, offset.isZero());
1605 REPORTER_ASSERT(reporter, result);
1606 REPORTER_ASSERT(reporter, result->subset().size() == SkISize::Make(100, 100));
1607
1608 SkBitmap resultBM;
1609 REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, result.get(), &resultBM));
1610 REPORTER_ASSERT(reporter, resultBM.getColor(50, 50) == SK_ColorGREEN);
1611 }
1612
DEF_TEST(ComposedImageFilterBounds,reporter)1613 DEF_TEST(ComposedImageFilterBounds, reporter) {
1614 test_composed_imagefilter_bounds(reporter, nullptr);
1615 }
1616
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterBounds_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1617 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterBounds_Gpu,
1618 reporter,
1619 ctxInfo,
1620 CtsEnforcement::kNever) {
1621 test_composed_imagefilter_bounds(reporter, ctxInfo.directContext());
1622 }
1623
DEF_TEST(ImageFilterCanComputeFastBounds,reporter)1624 DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
1625
1626 {
1627 SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
1628 sk_sp<SkImageFilter> lighting(SkImageFilters::PointLitDiffuse(
1629 location, SK_ColorGREEN, 0, 0, nullptr));
1630 REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
1631 }
1632
1633 {
1634 sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
1635 REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
1636 {
1637 SkColorFilter* grayCF;
1638 REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
1639 REPORTER_ASSERT(reporter, !as_CFB(grayCF)->affectsTransparentBlack());
1640 grayCF->unref();
1641 }
1642 REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
1643
1644 sk_sp<SkImageFilter> grayBlur(SkImageFilters::Blur(
1645 SK_Scalar1, SK_Scalar1, std::move(gray)));
1646 REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
1647 }
1648
1649 {
1650 float greenMatrix[20] = { 0, 0, 0, 0, 0,
1651 0, 0, 0, 0, 1.0f/255,
1652 0, 0, 0, 0, 0,
1653 0, 0, 0, 0, 1.0f/255
1654 };
1655 sk_sp<SkColorFilter> greenCF(SkColorFilters::Matrix(greenMatrix));
1656 sk_sp<SkImageFilter> green(SkImageFilters::ColorFilter(greenCF, nullptr));
1657
1658 REPORTER_ASSERT(reporter, as_CFB(greenCF)->affectsTransparentBlack());
1659 REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
1660
1661 sk_sp<SkImageFilter> greenBlur(SkImageFilters::Blur(SK_Scalar1, SK_Scalar1,
1662 std::move(green)));
1663 REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
1664 }
1665
1666 uint8_t allOne[256], identity[256];
1667 for (int i = 0; i < 256; ++i) {
1668 identity[i] = i;
1669 allOne[i] = 255;
1670 }
1671
1672 sk_sp<SkColorFilter> identityCF(SkColorFilters::TableARGB(identity, identity,
1673 identity, allOne));
1674 sk_sp<SkImageFilter> identityFilter(SkImageFilters::ColorFilter(identityCF, nullptr));
1675 REPORTER_ASSERT(reporter, !as_CFB(identityCF)->affectsTransparentBlack());
1676 REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
1677
1678 sk_sp<SkColorFilter> forceOpaqueCF(SkColorFilters::TableARGB(allOne, identity,
1679 identity, identity));
1680 sk_sp<SkImageFilter> forceOpaque(SkImageFilters::ColorFilter(forceOpaqueCF, nullptr));
1681 REPORTER_ASSERT(reporter, as_CFB(forceOpaqueCF)->affectsTransparentBlack());
1682 REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
1683 }
1684
1685 // Verify that SkImageSource survives serialization
DEF_TEST(ImageFilterImageSourceSerialization,reporter)1686 DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
1687 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10)));
1688 surface->getCanvas()->clear(SK_ColorGREEN);
1689 sk_sp<SkImage> image(surface->makeImageSnapshot());
1690 sk_sp<SkImageFilter> filter(SkImageFilters::Image(std::move(image), SkFilterMode::kNearest));
1691
1692 SkSerialProcs sProcs;
1693 sProcs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
1694 return SkPngEncoder::Encode(as_IB(img)->directContext(), img, SkPngEncoder::Options{});
1695 };
1696 sk_sp<SkData> data(filter->serialize(&sProcs));
1697 sk_sp<SkImageFilter> unflattenedFilter = SkImageFilter::Deserialize(data->data(), data->size());
1698 REPORTER_ASSERT(reporter, unflattenedFilter);
1699
1700 SkBitmap bm;
1701 bm.allocN32Pixels(10, 10);
1702 bm.eraseColor(SK_ColorBLUE);
1703 SkPaint paint;
1704 paint.setColor(SK_ColorRED);
1705 paint.setImageFilter(unflattenedFilter);
1706
1707 SkCanvas canvas(bm);
1708 canvas.drawRect(SkRect::MakeIWH(10, 10), paint);
1709 REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
1710 }
1711
DEF_TEST(ImageFilterImageSourceUninitialized,r)1712 DEF_TEST(ImageFilterImageSourceUninitialized, r) {
1713 sk_sp<SkData> data(GetResourceAsData("crbug769134.fil"));
1714 if (!data) {
1715 return;
1716 }
1717 sk_sp<SkImageFilter> unflattenedFilter = SkImageFilter::Deserialize(data->data(), data->size());
1718 // This will fail. More importantly, msan will verify that we did not
1719 // compare against uninitialized memory.
1720 REPORTER_ASSERT(r, !unflattenedFilter);
1721 }
1722
test_large_blur_input(skiatest::Reporter * reporter,SkCanvas * canvas)1723 static void test_large_blur_input(skiatest::Reporter* reporter, SkCanvas* canvas) {
1724 SkBitmap largeBmp;
1725 int largeW = 5000;
1726 int largeH = 5000;
1727 // If we're GPU-backed make the bitmap too large to be converted into a texture.
1728 if (auto ctx = canvas->recordingContext()) {
1729 largeW = ctx->priv().caps()->maxTextureSize() + 1;
1730 }
1731
1732 largeBmp.allocN32Pixels(largeW, largeH);
1733 largeBmp.eraseColor(0);
1734 if (!largeBmp.getPixels()) {
1735 ERRORF(reporter, "Failed to allocate large bmp.");
1736 return;
1737 }
1738
1739 sk_sp<SkImage> largeImage(largeBmp.asImage());
1740 if (!largeImage) {
1741 ERRORF(reporter, "Failed to create large image.");
1742 return;
1743 }
1744
1745 sk_sp<SkImageFilter> largeSource(SkImageFilters::Image(std::move(largeImage), {}));
1746 if (!largeSource) {
1747 ERRORF(reporter, "Failed to create large SkImageSource.");
1748 return;
1749 }
1750
1751 sk_sp<SkImageFilter> blur(SkImageFilters::Blur(10.f, 10.f, std::move(largeSource)));
1752 if (!blur) {
1753 ERRORF(reporter, "Failed to create SkBlurImageFilter.");
1754 return;
1755 }
1756
1757 SkPaint paint;
1758 paint.setImageFilter(std::move(blur));
1759
1760 // This should not crash (http://crbug.com/570479).
1761 canvas->drawRect(SkRect::MakeIWH(largeW, largeH), paint);
1762 }
1763
DEF_TEST(ImageFilterBlurLargeImage,reporter)1764 DEF_TEST(ImageFilterBlurLargeImage, reporter) {
1765 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100)));
1766 test_large_blur_input(reporter, surface->getCanvas());
1767 }
1768
test_make_with_filter(skiatest::Reporter * reporter,const std::function<sk_sp<SkSurface> (int width,int height)> & createSurface,const std::function<sk_sp<SkImage> (sk_sp<SkImage> src,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset)> & makeWithFilter)1769 static void test_make_with_filter(
1770 skiatest::Reporter* reporter,
1771 const std::function<sk_sp<SkSurface>(int width, int height)>& createSurface,
1772 const std::function<sk_sp<SkImage>(sk_sp<SkImage> src,
1773 const SkImageFilter* filter,
1774 const SkIRect& subset,
1775 const SkIRect& clipBounds,
1776 SkIRect* outSubset,
1777 SkIPoint* offset)>& makeWithFilter) {
1778 sk_sp<SkSurface> surface(createSurface(192, 128));
1779 surface->getCanvas()->clear(SK_ColorRED);
1780 SkPaint bluePaint;
1781 bluePaint.setColor(SK_ColorBLUE);
1782 SkIRect subset = SkIRect::MakeXYWH(25, 20, 50, 50);
1783 surface->getCanvas()->drawRect(SkRect::Make(subset), bluePaint);
1784 sk_sp<SkImage> sourceImage = surface->makeImageSnapshot();
1785
1786 sk_sp<SkImageFilter> filter = make_grayscale(nullptr, nullptr);
1787 SkIRect clipBounds = SkIRect::MakeXYWH(30, 35, 100, 100);
1788 SkIRect outSubset;
1789 SkIPoint offset;
1790 sk_sp<SkImage> result;
1791
1792 result = makeWithFilter(sourceImage, nullptr, subset, clipBounds, &outSubset, &offset);
1793 REPORTER_ASSERT(reporter, !result); // filter is required
1794
1795 result = makeWithFilter(sourceImage, filter.get(), subset, clipBounds, nullptr, &offset);
1796 REPORTER_ASSERT(reporter, !result); // outSubset is required
1797
1798 result = makeWithFilter(sourceImage, filter.get(), subset, clipBounds, &outSubset, nullptr);
1799 REPORTER_ASSERT(reporter, !result); // offset is required
1800
1801 SkIRect bigSubset = SkIRect::MakeXYWH(-10000, -10000, 20000, 20000);
1802 result = makeWithFilter(sourceImage, filter.get(), bigSubset, clipBounds, &outSubset, &offset);
1803 REPORTER_ASSERT(reporter, !result); // subset needs to be w/in source's bounds
1804
1805 const SkIRect kEmpty = SkIRect::MakeEmpty();
1806 result = makeWithFilter(sourceImage, filter.get(), kEmpty, clipBounds, &outSubset, &offset);
1807 REPORTER_ASSERT(reporter, !result); // subset can't be empty
1808
1809 result = makeWithFilter(sourceImage, filter.get(), subset, kEmpty, &outSubset, &offset);
1810 REPORTER_ASSERT(reporter, !result); // clipBounds can't be empty
1811
1812 const SkIRect kLeftField = SkIRect::MakeXYWH(-1000, 0, 100, 100);
1813 result = makeWithFilter(sourceImage, filter.get(), subset, kLeftField, &outSubset, &offset);
1814 REPORTER_ASSERT(reporter, !result);
1815
1816 result = makeWithFilter(sourceImage, filter.get(), subset, clipBounds, &outSubset, &offset);
1817
1818 REPORTER_ASSERT(reporter, result);
1819 REPORTER_ASSERT(reporter, result->bounds().contains(outSubset));
1820 SkIRect destRect = SkIRect::MakeXYWH(offset.x(), offset.y(),
1821 outSubset.width(), outSubset.height());
1822 REPORTER_ASSERT(reporter, clipBounds.contains(destRect));
1823
1824 // In GPU-mode, this case creates a special image with a backing size that differs from
1825 // the content size
1826 {
1827 clipBounds.setXYWH(0, 0, 170, 100);
1828 subset.setXYWH(0, 0, 160, 90);
1829
1830 filter = SkImageFilters::Blend(SkBlendMode::kSrcOver, nullptr);
1831 result = makeWithFilter(sourceImage, filter.get(), subset, clipBounds, &outSubset, &offset);
1832 REPORTER_ASSERT(reporter, result);
1833
1834 // In Ganesh, we want the result image (and all intermediate steps) to have used the same
1835 // origin as the original surface.
1836 if (result && as_IB(result)->isGaneshBacked()) {
1837 SkImage_GaneshBase* base = static_cast<SkImage_GaneshBase*>(result.get());
1838 REPORTER_ASSERT(reporter, base->origin() == kTestSurfaceOrigin);
1839 }
1840 }
1841 }
1842
DEF_TEST(ImageFilterMakeWithFilter,reporter)1843 DEF_TEST(ImageFilterMakeWithFilter, reporter) {
1844 auto createRasterSurface = [](int width, int height) -> sk_sp<SkSurface> {
1845 const SkImageInfo info = SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
1846 return SkSurfaces::Raster(info);
1847 };
1848
1849 auto raster = [](sk_sp<SkImage> src,
1850 const SkImageFilter* filter,
1851 const SkIRect& subset,
1852 const SkIRect& clipBounds,
1853 SkIRect* outSubset,
1854 SkIPoint* offset) -> sk_sp<SkImage> {
1855 return SkImages::MakeWithFilter(std::move(src),
1856 filter,
1857 subset,
1858 clipBounds,
1859 outSubset,
1860 offset);
1861 };
1862
1863 test_make_with_filter(reporter, createRasterSurface, raster);
1864 }
1865
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Ganesh,reporter,ctxInfo,CtsEnforcement::kNever)1866 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Ganesh,
1867 reporter,
1868 ctxInfo,
1869 CtsEnforcement::kNever) {
1870 GrRecordingContext* rContext = ctxInfo.directContext();
1871
1872 auto createGaneshSurface = [rContext](int width, int height) -> sk_sp<SkSurface> {
1873 const SkImageInfo info = SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
1874 return SkSurfaces::RenderTarget(
1875 rContext, skgpu::Budgeted::kNo, info, 0, kTestSurfaceOrigin, nullptr);
1876 };
1877
1878 auto ganesh = [rContext](sk_sp<SkImage> src,
1879 const SkImageFilter* filter,
1880 const SkIRect& subset,
1881 const SkIRect& clipBounds,
1882 SkIRect* outSubset,
1883 SkIPoint* offset) -> sk_sp<SkImage> {
1884 return SkImages::MakeWithFilter(rContext,
1885 std::move(src),
1886 filter,
1887 subset,
1888 clipBounds,
1889 outSubset,
1890 offset);
1891 };
1892
1893 test_make_with_filter(reporter, createGaneshSurface, ganesh);
1894 }
1895
1896 #if defined(SK_GRAPHITE)
1897
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Graphite,reporter,context,CtsEnforcement::kApiLevel_202404)1898 DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Graphite,
1899 reporter,
1900 context,
1901 CtsEnforcement::kApiLevel_202404) {
1902 std::unique_ptr<skgpu::graphite::Recorder> recorder =
1903 context->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
1904
1905 auto createGraphiteSurface = [r = recorder.get()](int width, int height) -> sk_sp<SkSurface> {
1906 const SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
1907 return SkSurfaces::RenderTarget(r, info);
1908 };
1909
1910 auto graphite = [r = recorder.get()](sk_sp<SkImage> src,
1911 const SkImageFilter* filter,
1912 const SkIRect& subset,
1913 const SkIRect& clipBounds,
1914 SkIRect* outSubset,
1915 SkIPoint* offset) -> sk_sp<SkImage> {
1916 return SkImages::MakeWithFilter(r,
1917 std::move(src),
1918 filter,
1919 subset,
1920 clipBounds,
1921 outSubset,
1922 offset);
1923 };
1924
1925 test_make_with_filter(reporter, createGraphiteSurface, graphite);
1926 }
1927
1928 #endif
1929
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterHugeBlur_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1930 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterHugeBlur_Gpu,
1931 reporter,
1932 ctxInfo,
1933 CtsEnforcement::kNever) {
1934 sk_sp<SkSurface> surf(SkSurfaces::RenderTarget(
1935 ctxInfo.directContext(), skgpu::Budgeted::kNo, SkImageInfo::MakeN32Premul(100, 100)));
1936
1937 SkCanvas* canvas = surf->getCanvas();
1938
1939 test_huge_blur(canvas, reporter);
1940 }
1941
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(XfermodeImageFilterCroppedInput_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1942 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(XfermodeImageFilterCroppedInput_Gpu,
1943 reporter,
1944 ctxInfo,
1945 CtsEnforcement::kNever) {
1946 sk_sp<SkSurface> surf(SkSurfaces::RenderTarget(
1947 ctxInfo.directContext(),
1948 skgpu::Budgeted::kNo,
1949 SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
1950
1951 test_xfermode_cropped_input(surf.get(), reporter);
1952 }
1953
DEF_GANESH_TEST_FOR_ALL_CONTEXTS(ImageFilterBlurLargeImage_Gpu,reporter,ctxInfo,CtsEnforcement::kNever)1954 DEF_GANESH_TEST_FOR_ALL_CONTEXTS(ImageFilterBlurLargeImage_Gpu,
1955 reporter,
1956 ctxInfo,
1957 CtsEnforcement::kNever) {
1958 auto surface(SkSurfaces::RenderTarget(
1959 ctxInfo.directContext(),
1960 skgpu::Budgeted::kYes,
1961 SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
1962 test_large_blur_input(reporter, surface->getCanvas());
1963 }
1964
1965 /*
1966 * Test that colorfilterimagefilter does not require its CTM to be decomposed when it has more
1967 * than just scale/translate, but that other filters do.
1968 */
DEF_TEST(ImageFilterComplexCTM,reporter)1969 DEF_TEST(ImageFilterComplexCTM, reporter) {
1970 // just need a colorfilter to exercise the corresponding imagefilter
1971 sk_sp<SkColorFilter> cf = SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcATop);
1972 sk_sp<SkImageFilter> cfif = SkImageFilters::ColorFilter(cf, nullptr); // can handle
1973 sk_sp<SkImageFilter> blif = SkImageFilters::Blur(3, 3, nullptr); // cannot handle
1974 using MatrixCapability = SkImageFilter_Base::MatrixCapability;
1975
1976 struct {
1977 sk_sp<SkImageFilter> fFilter;
1978 MatrixCapability fExpectCapability;
1979 } recs[] = {
1980 { cfif, MatrixCapability::kComplex },
1981 { SkImageFilters::ColorFilter(cf, cfif), MatrixCapability::kComplex },
1982 { SkImageFilters::Merge(cfif, cfif), MatrixCapability::kComplex },
1983 { SkImageFilters::Compose(cfif, cfif), MatrixCapability::kComplex },
1984
1985 { blif, MatrixCapability::kScaleTranslate },
1986 { SkImageFilters::Blur(3, 3, cfif), MatrixCapability::kScaleTranslate },
1987 { SkImageFilters::ColorFilter(cf, blif), MatrixCapability::kScaleTranslate },
1988 { SkImageFilters::Merge(cfif, blif), MatrixCapability::kScaleTranslate },
1989 { SkImageFilters::Compose(blif, cfif), MatrixCapability::kScaleTranslate },
1990 };
1991
1992 for (const auto& rec : recs) {
1993 const MatrixCapability capability = as_IFB(rec.fFilter)->getCTMCapability();
1994 REPORTER_ASSERT(reporter, capability == rec.fExpectCapability);
1995 }
1996 }
1997
1998 // Test SkXfermodeImageFilter::filterBounds with different blending modes.
DEF_TEST(XfermodeImageFilterBounds,reporter)1999 DEF_TEST(XfermodeImageFilterBounds, reporter) {
2000 SkIRect background_rect = SkIRect::MakeXYWH(0, 0, 100, 100);
2001 SkIRect foreground_rect = SkIRect::MakeXYWH(50, 50, 100, 100);
2002 sk_sp<SkImageFilter> background = SkImageFilters::Crop(SkRect::Make(background_rect), nullptr);
2003 sk_sp<SkImageFilter> foreground = SkImageFilters::Crop(SkRect::Make(foreground_rect), nullptr);
2004
2005 SkIRect expectedBounds[kSkBlendModeCount];
2006 // Expect union of input rects by default.
2007 for (int i = 0; i < kSkBlendModeCount; ++i) {
2008 expectedBounds[i] = background_rect;
2009 expectedBounds[i].join(foreground_rect);
2010 }
2011
2012 SkIRect intersection = background_rect;
2013 intersection.intersect(foreground_rect);
2014 expectedBounds[static_cast<int>(SkBlendMode::kClear)] = SkIRect::MakeEmpty();
2015 expectedBounds[static_cast<int>(SkBlendMode::kSrc)] = foreground_rect;
2016 expectedBounds[static_cast<int>(SkBlendMode::kDst)] = background_rect;
2017 expectedBounds[static_cast<int>(SkBlendMode::kSrcIn)] = intersection;
2018 expectedBounds[static_cast<int>(SkBlendMode::kDstIn)] = intersection;
2019 expectedBounds[static_cast<int>(SkBlendMode::kSrcOut)] = foreground_rect;
2020 expectedBounds[static_cast<int>(SkBlendMode::kDstOut)] = background_rect;
2021 expectedBounds[static_cast<int>(SkBlendMode::kSrcATop)] = background_rect;
2022 expectedBounds[static_cast<int>(SkBlendMode::kDstATop)] = foreground_rect;
2023 expectedBounds[static_cast<int>(SkBlendMode::kModulate)] = intersection;
2024
2025 // Use a very large input bounds so that the crop rects stored in 'background' and 'foreground'
2026 // aren't restricted.
2027 SkIRect src = SkRectPriv::MakeILarge();
2028 for (int i = 0; i < kSkBlendModeCount; ++i) {
2029 sk_sp<SkImageFilter> xfermode(SkImageFilters::Blend(static_cast<SkBlendMode>(i),
2030 background, foreground, nullptr));
2031 auto bounds = xfermode->filterBounds(src, SkMatrix::I(),
2032 SkImageFilter::kForward_MapDirection, nullptr);
2033 REPORTER_ASSERT(reporter, bounds == expectedBounds[i]);
2034 }
2035
2036 // Test empty intersection.
2037 sk_sp<SkImageFilter> background2 =
2038 SkImageFilters::Crop(SkRect::MakeXYWH(0, 0, 20, 20), nullptr);
2039 sk_sp<SkImageFilter> foreground2 =
2040 SkImageFilters::Crop(SkRect::MakeXYWH(40, 40, 50, 50), nullptr);
2041 sk_sp<SkImageFilter> xfermode(SkImageFilters::Blend(
2042 SkBlendMode::kSrcIn, std::move(background2), std::move(foreground2), nullptr));
2043 auto bounds = xfermode->filterBounds(src, SkMatrix::I(),
2044 SkImageFilter::kForward_MapDirection, nullptr);
2045 REPORTER_ASSERT(reporter, bounds.isEmpty());
2046 }
2047
DEF_TEST(OffsetImageFilterBounds,reporter)2048 DEF_TEST(OffsetImageFilterBounds, reporter) {
2049 const SkIRect src = SkIRect::MakeXYWH(0, 0, 100, 100);
2050 const SkVector srcOffset = {-50.5f, -50.5f};
2051 sk_sp<SkImageFilter> offset(SkImageFilters::Offset(srcOffset.fX, srcOffset.fY, nullptr));
2052
2053 // Because the offset has a fractional component, the final output and required input bounds
2054 // will be rounded out to include an extra pixel.
2055 SkIRect expectedForward = SkRect::Make(src).makeOffset(srcOffset.fX, srcOffset.fY).roundOut();
2056 SkIRect boundsForward = offset->filterBounds(src, SkMatrix::I(),
2057 SkImageFilter::kForward_MapDirection, nullptr);
2058 REPORTER_ASSERT(reporter, boundsForward == expectedForward);
2059
2060 SkIRect expectedReverse = SkRect::Make(src).makeOffset(-srcOffset.fX, -srcOffset.fY).roundOut();
2061
2062 // Intersect 'expectedReverse' with the source because we are passing &src in as the known
2063 // input bounds, which is the bounds of non-transparent pixels that can be moved by the offset.
2064 // While the ::Offset filter could show all pixels inside 'expectedReverse' given that 'src'
2065 // is also the target device output of the filter, the required input can be made tighter.
2066 SkAssertResult(expectedReverse.intersect(src));
2067
2068 SkIRect boundsReverse = offset->filterBounds(src, SkMatrix::I(),
2069 SkImageFilter::kReverse_MapDirection, &src);
2070 REPORTER_ASSERT(reporter, boundsReverse == expectedReverse);
2071 }
2072
DEF_TEST(OffsetImageFilterBoundsNoOverflow,reporter)2073 DEF_TEST(OffsetImageFilterBoundsNoOverflow, reporter) {
2074 const SkIRect src = SkIRect::MakeXYWH(-10.f, -10.f, 20.f, 20.f);
2075 const SkScalar bigOffset = SkIntToScalar(std::numeric_limits<int>::max()) * 2.f / 3.f;
2076
2077 sk_sp<SkImageFilter> filter =
2078 SkImageFilters::Blend(SkBlendMode::kSrcOver,
2079 SkImageFilters::Offset(-bigOffset, -bigOffset, nullptr),
2080 SkImageFilters::Offset(bigOffset, bigOffset, nullptr));
2081 SkIRect boundsForward = filter->filterBounds(src, SkMatrix::I(),
2082 SkImageFilter::kForward_MapDirection, nullptr);
2083 // NOTE: isEmpty() will return true even if the l/r or t/b didn't overflow but the dimensions
2084 // would overflow an int32. However, when isEmpty64() is false, it means the actual edge coords
2085 // are valid, which is good enough for our purposes (and gfx::Rect has its own strategies for
2086 // ensuring such a rectangle doesn't get accidentally treated as empty during chromium's
2087 // conversions).
2088 REPORTER_ASSERT(reporter, !boundsForward.isEmpty64());
2089
2090 // When querying with unbounded input content, it should not overflow and should not be empty.
2091 SkIRect boundsReverse = filter->filterBounds(src, SkMatrix::I(),
2092 SkImageFilter::kReverse_MapDirection, nullptr);
2093 REPORTER_ASSERT(reporter, !boundsReverse.isEmpty64());
2094
2095 // However in this case, when 'src' is also passed as the content bounds, the ::Offset() filters
2096 // detect that they would be transparent black. This propagates up to the src-over blend and
2097 // the entire graph is identified as empty.
2098 boundsReverse = filter->filterBounds(src, SkMatrix::I(),
2099 SkImageFilter::kReverse_MapDirection, &src);
2100 REPORTER_ASSERT(reporter, boundsReverse.isEmpty64());
2101 }
2102
test_arithmetic_bounds(skiatest::Reporter * reporter,float k1,float k2,float k3,float k4,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const SkIRect * crop,const SkIRect & expected)2103 static void test_arithmetic_bounds(skiatest::Reporter* reporter, float k1, float k2, float k3,
2104 float k4, sk_sp<SkImageFilter> background,
2105 sk_sp<SkImageFilter> foreground,
2106 const SkIRect* crop, const SkIRect& expected) {
2107 sk_sp<SkImageFilter> arithmetic(SkImageFilters::Arithmetic(
2108 k1, k2, k3, k4, false, std::move(background), std::move(foreground), crop));
2109 // Use a very large input bounds so that the crop rects stored in 'background' and 'foreground'
2110 // aren't restricted.
2111 SkIRect src = SkRectPriv::MakeILarge();
2112 SkIRect bounds = arithmetic->filterBounds(src, SkMatrix::I(),
2113 SkImageFilter::kForward_MapDirection, nullptr);
2114 REPORTER_ASSERT(reporter, expected == bounds);
2115 }
2116
test_arithmetic_combinations(skiatest::Reporter * reporter,float v)2117 static void test_arithmetic_combinations(skiatest::Reporter* reporter, float v) {
2118 SkIRect bgRect = SkIRect::MakeXYWH(0, 0, 100, 100);
2119 SkIRect fgRect = SkIRect::MakeXYWH(50, 50, 100, 100);
2120 sk_sp<SkImageFilter> background = SkImageFilters::Crop(SkRect::Make(bgRect), nullptr);
2121 sk_sp<SkImageFilter> foreground = SkImageFilters::Crop(SkRect::Make(fgRect), nullptr);
2122
2123 SkIRect unionRect = bgRect;
2124 unionRect.join(fgRect);
2125 SkIRect intersection = bgRect;
2126 intersection.intersect(fgRect);
2127
2128 // Test with crop. When k4 is non-zero, the result is expected to be cropRect
2129 // regardless of inputs because the filter affects the whole crop area. When there is no crop
2130 // rect, it should report an effectively infinite output.
2131 static const SkIRect kInf = SkRectPriv::MakeILarge();
2132 test_arithmetic_bounds(reporter, 0, 0, 0, 0, background, foreground, nullptr,
2133 SkIRect::MakeEmpty());
2134 test_arithmetic_bounds(reporter, 0, 0, 0, v, background, foreground, nullptr, kInf);
2135 test_arithmetic_bounds(reporter, 0, 0, v, 0, background, foreground, nullptr, bgRect);
2136 test_arithmetic_bounds(reporter, 0, 0, v, v, background, foreground, nullptr, kInf);
2137 test_arithmetic_bounds(reporter, 0, v, 0, 0, background, foreground, nullptr, fgRect);
2138 test_arithmetic_bounds(reporter, 0, v, 0, v, background, foreground, nullptr, kInf);
2139 test_arithmetic_bounds(reporter, 0, v, v, 0, background, foreground, nullptr, unionRect);
2140 test_arithmetic_bounds(reporter, 0, v, v, v, background, foreground, nullptr, kInf);
2141 test_arithmetic_bounds(reporter, v, 0, 0, 0, background, foreground, nullptr, intersection);
2142 test_arithmetic_bounds(reporter, v, 0, 0, v, background, foreground, nullptr, kInf);
2143 test_arithmetic_bounds(reporter, v, 0, v, 0, background, foreground, nullptr, bgRect);
2144 test_arithmetic_bounds(reporter, v, 0, v, v, background, foreground, nullptr, kInf);
2145 test_arithmetic_bounds(reporter, v, v, 0, 0, background, foreground, nullptr, fgRect);
2146 test_arithmetic_bounds(reporter, v, v, 0, v, background, foreground, nullptr, kInf);
2147 test_arithmetic_bounds(reporter, v, v, v, 0, background, foreground, nullptr, unionRect);
2148 test_arithmetic_bounds(reporter, v, v, v, v, background, foreground, nullptr, kInf);
2149
2150 SkIRect cropRect = SkIRect::MakeXYWH(-111, -222, 333, 444);
2151 test_arithmetic_bounds(reporter, 0, 0, 0, 0, background, foreground, &cropRect,
2152 SkIRect::MakeEmpty());
2153 test_arithmetic_bounds(reporter, 0, 0, 0, v, background, foreground, &cropRect, cropRect);
2154 test_arithmetic_bounds(reporter, 0, 0, v, 0, background, foreground, &cropRect, bgRect);
2155 test_arithmetic_bounds(reporter, 0, 0, v, v, background, foreground, &cropRect, cropRect);
2156 test_arithmetic_bounds(reporter, 0, v, 0, 0, background, foreground, &cropRect, fgRect);
2157 test_arithmetic_bounds(reporter, 0, v, 0, v, background, foreground, &cropRect, cropRect);
2158 test_arithmetic_bounds(reporter, 0, v, v, 0, background, foreground, &cropRect, unionRect);
2159 test_arithmetic_bounds(reporter, 0, v, v, v, background, foreground, &cropRect, cropRect);
2160 test_arithmetic_bounds(reporter, v, 0, 0, 0, background, foreground, &cropRect, intersection);
2161 test_arithmetic_bounds(reporter, v, 0, 0, v, background, foreground, &cropRect, cropRect);
2162 test_arithmetic_bounds(reporter, v, 0, v, 0, background, foreground, &cropRect, bgRect);
2163 test_arithmetic_bounds(reporter, v, 0, v, v, background, foreground, &cropRect, cropRect);
2164 test_arithmetic_bounds(reporter, v, v, 0, 0, background, foreground, &cropRect, fgRect);
2165 test_arithmetic_bounds(reporter, v, v, 0, v, background, foreground, &cropRect, cropRect);
2166 test_arithmetic_bounds(reporter, v, v, v, 0, background, foreground, &cropRect, unionRect);
2167 test_arithmetic_bounds(reporter, v, v, v, v, background, foreground, &cropRect, cropRect);
2168 }
2169
2170 // Test SkArithmeticImageFilter::filterBounds with different blending modes.
DEF_TEST(ArithmeticImageFilterBounds,reporter)2171 DEF_TEST(ArithmeticImageFilterBounds, reporter) {
2172 test_arithmetic_combinations(reporter, 1);
2173 test_arithmetic_combinations(reporter, 0.5);
2174 }
2175
2176 // Test SkDisplacementMapEffect::filterBounds.
DEF_TEST(DisplacementMapBounds,reporter)2177 DEF_TEST(DisplacementMapBounds, reporter) {
2178 SkIRect floodBounds(SkIRect::MakeXYWH(20, 30, 10, 10));
2179 sk_sp<SkImageFilter> flood(SkImageFilters::Shader(SkShaders::Color(SK_ColorGREEN),
2180 &floodBounds));
2181 SkIRect tilingBounds(SkIRect::MakeXYWH(0, 0, 200, 100));
2182 sk_sp<SkImageFilter> tiling(SkImageFilters::Tile(SkRect::Make(floodBounds),
2183 SkRect::Make(tilingBounds),
2184 flood));
2185 sk_sp<SkImageFilter> displace(SkImageFilters::DisplacementMap(SkColorChannel::kR,
2186 SkColorChannel::kB,
2187 20.0f, nullptr, tiling));
2188
2189 // The filter graph rooted at 'displace' uses the dynamic source image for the displacement
2190 // component of ::DisplacementMap, modifying the color component produced by the ::Tile. The
2191 // output of the tiling filter will be 'tilingBounds', regardless of its input, so 'floodBounds'
2192 // has no effect on the output. Since 'tiling' doesn't reference any dynamic source image, it
2193 // also will not affect the required input bounds. The displacement map is sampled 1-to-1
2194 // with the output pixels, and covers the output unless the color's output makes that impossible
2195 // and the output is a subset of the desired output. Thus, the displacement can impact the
2196 // reported output bounds.
2197 SkIRect input(SkIRect::MakeXYWH(20, 30, 40, 50));
2198
2199 // 'input' is the desired output, which directly constrains the displacement component in this
2200 // specific filter graph.
2201 SkIRect actualInput = displace->filterBounds(input, SkMatrix::I(),
2202 SkImageFilter::kReverse_MapDirection);
2203 REPORTER_ASSERT(reporter, input == actualInput);
2204
2205 // 'input' is the content bounds, which don't affect output bounds because it's only referenced
2206 // by the displacement component and not the color component.
2207 SkIRect actualOutput = displace->filterBounds(input, SkMatrix::I(),
2208 SkImageFilter::kForward_MapDirection);
2209 REPORTER_ASSERT(reporter, tilingBounds.makeOutset(10, 10) == actualOutput);
2210 }
2211
2212 // Test SkImageSource::filterBounds.
DEF_TEST(ImageSourceBounds,reporter)2213 DEF_TEST(ImageSourceBounds, reporter) {
2214 sk_sp<SkImage> image(make_gradient_circle(64, 64).asImage());
2215 // Default src and dst rects.
2216 sk_sp<SkImageFilter> source1(SkImageFilters::Image(image, SkFilterMode::kNearest));
2217 SkIRect imageBounds = SkIRect::MakeWH(64, 64);
2218 SkIRect input(SkIRect::MakeXYWH(10, 20, 30, 40));
2219 REPORTER_ASSERT(reporter,
2220 imageBounds == source1->filterBounds(input, SkMatrix::I(),
2221 SkImageFilter::kForward_MapDirection,
2222 nullptr));
2223 REPORTER_ASSERT(reporter,
2224 source1->filterBounds(input, SkMatrix::I(),
2225 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2226 SkMatrix scale(SkMatrix::Scale(2, 2));
2227 SkIRect scaledBounds = SkIRect::MakeWH(128, 128);
2228 REPORTER_ASSERT(reporter,
2229 scaledBounds == source1->filterBounds(input, scale,
2230 SkImageFilter::kForward_MapDirection,
2231 nullptr));
2232 REPORTER_ASSERT(reporter,
2233 source1->filterBounds(input, scale,
2234 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2235
2236 // Specified src and dst rects (which are outside available pixels).
2237 SkRect src(SkRect::MakeXYWH(0.5, 0.5, 100.5, 100.5));
2238 SkRect dst(SkRect::MakeXYWH(-10.5, -10.5, 120.5, 120.5));
2239 sk_sp<SkImageFilter> source2(SkImageFilters::Image(image, src, dst,
2240 SkSamplingOptions(SkFilterMode::kLinear,
2241 SkMipmapMode::kLinear)));
2242
2243 SkRect clippedSrc = src;
2244 SkAssertResult(clippedSrc.intersect(SkRect::Make(image->dimensions())));
2245 SkRect clippedDst = SkMatrix::RectToRect(src, dst).mapRect(clippedSrc);
2246
2247 REPORTER_ASSERT(reporter,
2248 clippedDst.roundOut() ==
2249 source2->filterBounds(input, SkMatrix::I(),
2250 SkImageFilter::kForward_MapDirection, nullptr));
2251 REPORTER_ASSERT(reporter,
2252 source2->filterBounds(input, SkMatrix::I(),
2253 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2254 scale.mapRect(&clippedDst);
2255 scale.mapRect(&clippedSrc);
2256 REPORTER_ASSERT(reporter,
2257 clippedDst.roundOut() ==
2258 source2->filterBounds(input, scale,
2259 SkImageFilter::kForward_MapDirection, nullptr));
2260 REPORTER_ASSERT(reporter,
2261 source2->filterBounds(input, scale,
2262 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2263 }
2264
2265 // Test SkPictureImageFilter::filterBounds.
DEF_TEST(PictureImageSourceBounds,reporter)2266 DEF_TEST(PictureImageSourceBounds, reporter) {
2267 SkPictureRecorder recorder;
2268 SkCanvas* recordingCanvas = recorder.beginRecording(64, 64);
2269
2270 SkPaint greenPaint;
2271 greenPaint.setColor(SK_ColorGREEN);
2272 recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
2273 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
2274
2275 // Default target rect.
2276 sk_sp<SkImageFilter> source1(SkImageFilters::Picture(picture));
2277 SkIRect pictureBounds = SkIRect::MakeWH(64, 64);
2278 SkIRect input(SkIRect::MakeXYWH(10, 20, 30, 40));
2279 REPORTER_ASSERT(reporter,
2280 pictureBounds == source1->filterBounds(input, SkMatrix::I(),
2281 SkImageFilter::kForward_MapDirection,
2282 nullptr));
2283 REPORTER_ASSERT(reporter,
2284 source1->filterBounds(input, SkMatrix::I(),
2285 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2286 SkMatrix scale(SkMatrix::Scale(2, 2));
2287 SkIRect scaledPictureBounds = SkIRect::MakeWH(128, 128);
2288 REPORTER_ASSERT(reporter,
2289 scaledPictureBounds == source1->filterBounds(input, scale,
2290 SkImageFilter::kForward_MapDirection,
2291 nullptr));
2292 REPORTER_ASSERT(reporter,
2293 source1->filterBounds(input, scale,
2294 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2295
2296 // Specified target rect.
2297 SkRect targetRect(SkRect::MakeXYWH(9.5, 9.5, 31, 21));
2298 sk_sp<SkImageFilter> source2(SkImageFilters::Picture(picture, targetRect));
2299 REPORTER_ASSERT(reporter,
2300 targetRect.roundOut() == source2->filterBounds(input, SkMatrix::I(),
2301 SkImageFilter::kForward_MapDirection,
2302 nullptr));
2303 REPORTER_ASSERT(reporter,
2304 source2->filterBounds(input, SkMatrix::I(),
2305 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2306 scale.mapRect(&targetRect);
2307 REPORTER_ASSERT(reporter,
2308 targetRect.roundOut() == source2->filterBounds(input, scale,
2309 SkImageFilter::kForward_MapDirection,
2310 nullptr));
2311 REPORTER_ASSERT(reporter,
2312 source2->filterBounds(input, scale,
2313 SkImageFilter::kReverse_MapDirection, &input).isEmpty());
2314 }
2315
DEF_TEST(DropShadowImageFilter_Huge,reporter)2316 DEF_TEST(DropShadowImageFilter_Huge, reporter) {
2317 // Successful if it doesn't crash or trigger ASAN. (crbug.com/1264705)
2318 auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(300, 150));
2319
2320 SkPaint paint;
2321 paint.setImageFilter(SkImageFilters::DropShadowOnly(
2322 0.0f, 0.437009f, 14129.6f, 14129.6f, SK_ColorGRAY, nullptr));
2323
2324 surf->getCanvas()->saveLayer(nullptr, &paint);
2325 surf->getCanvas()->restore();
2326 }
2327
DEF_TEST(DisplacementImageFilter_InvalidInputs_ReturnsNullptr,reporter)2328 DEF_TEST(DisplacementImageFilter_InvalidInputs_ReturnsNullptr, reporter) {
2329 sk_sp<SkImageFilter> valid(SkImageFilters::Shader(SkShaders::Color(SK_ColorGREEN)));
2330 REPORTER_ASSERT(reporter, valid != nullptr);
2331
2332 REPORTER_ASSERT(
2333 reporter,
2334 nullptr == SkImageFilters::DisplacementMap(SkColorChannel::kR,
2335 SkColorChannel::kB,
2336 std::numeric_limits<float>::infinity(),
2337 valid,
2338 valid));
2339
2340 REPORTER_ASSERT(reporter,
2341 nullptr == SkImageFilters::DisplacementMap(static_cast<SkColorChannel>(22),
2342 SkColorChannel::kB,
2343 5.f,
2344 valid,
2345 valid));
2346 }
2347
DEF_TEST(ImageFilter_DrawExtremeMatrixTransform_DoesNotAssert,reporter)2348 DEF_TEST(ImageFilter_DrawExtremeMatrixTransform_DoesNotAssert, reporter) {
2349 // Found by fuzzing
2350 SkPaint p;
2351 p.setDither(true);
2352 p.setColor(SkColorSetARGB(255, 1, 255, 255));
2353 p.setStyle(SkPaint::Style::kFill_Style);
2354
2355 SkRRect rr = SkRRect::MakeRectXY({5, 10, 15, 20}, 2, 2);
2356
2357 sk_sp<SkImageFilter> ifs[4];
2358 ifs[0] = nullptr;
2359 ifs[1] = SkImageFilters::Blur(SkBits2Float(0xe0e0e0e), SkBits2Float(0x10108000), nullptr);
2360 SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kLinear);
2361 SkMatrix matrix = SkMatrix::MakeAll(SkBits2Float(0xfdfe0200),
2362 SkBits2Float(0xfdfdfdfd),
2363 SkBits2Float(0x2a0202fe),
2364 SkBits2Float(0x2020202),
2365 SkBits2Float(0x2020202),
2366 SkBits2Float(0x20200202),
2367 SkBits2Float(0x2fab0024),
2368 SkBits2Float(0x8),
2369 SkBits2Float(0x0));
2370 ifs[2] = SkImageFilters::MatrixTransform(matrix, sampling, nullptr);
2371 ifs[3] = SkImageFilters::Shader(nullptr);
2372 auto merged = SkImageFilters::Merge(ifs, 4, nullptr);
2373 p.setImageFilter(merged);
2374
2375 auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(128, 160));
2376 surface->getCanvas()->clear(SK_ColorWHITE);
2377 surface->getCanvas()->drawRRect(rr, p);
2378 }
2379