Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
dl_image_filter_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/display_list/dl_blend_mode.h"
6#include "flutter/display_list/dl_color.h"
7#include "flutter/display_list/dl_sampling_options.h"
8#include "flutter/display_list/dl_tile_mode.h"
9#include "flutter/display_list/effects/dl_color_filter.h"
10#include "flutter/display_list/effects/dl_image_filter.h"
11#include "flutter/display_list/testing/dl_test_equality.h"
12#include "flutter/display_list/utils/dl_comparable.h"
13#include "gtest/gtest.h"
14
19
20namespace flutter {
21namespace testing {
22
23// SkRect::contains treats the rect as a half-open interval which is
24// appropriate for so many operations. Unfortunately, we are using
25// it here to test containment of the corners of a transformed quad
26// so the corners of the quad that are measured against the right
27// and bottom edges are contained even if they are on the right or
28// bottom edge. This method does the "all sides inclusive" version
29// of SkRect::contains.
30static bool containsInclusive(const SkRect rect, const SkPoint p) {
31 // Test with a slight offset of 1E-9 to "forgive" IEEE bit-rounding
32 // Ending up with bounds that are off by 1E-9 (these numbers are all
33 // being tested in device space with this method) will be off by a
34 // negligible amount of a pixel that wouldn't contribute to changing
35 // the color of a pixel.
36 return (p.fX >= rect.fLeft - 1E-9 && //
37 p.fX <= rect.fRight + 1E-9 && //
38 p.fY >= rect.fTop - 1E-9 && //
39 p.fY <= rect.fBottom + 1E-9);
40}
41
42static bool containsInclusive(const SkRect rect, const SkPoint quad[4]) {
43 return (containsInclusive(rect, quad[0]) && //
44 containsInclusive(rect, quad[1]) && //
45 containsInclusive(rect, quad[2]) && //
46 containsInclusive(rect, quad[3]));
47}
48
49static bool containsInclusive(const SkIRect rect, const SkPoint quad[4]) {
50 return containsInclusive(SkRect::Make(rect), quad);
51}
52
53static bool containsInclusive(const SkIRect rect, const SkRect bounds) {
54 return (bounds.fLeft >= rect.fLeft - 1E-9 &&
55 bounds.fTop >= rect.fTop - 1E-9 &&
56 bounds.fRight <= rect.fRight + 1E-9 &&
57 bounds.fBottom <= rect.fBottom + 1E-9);
58}
59
60// Used to verify that the expected output bounds and reverse-engineered
61// "input bounds for output bounds" rectangles are included in the rectangle
62// returned from the various bounds computation methods under the specified
63// matrix.
64static void TestBoundsWithMatrix(const DlImageFilter& filter,
65 const SkMatrix& matrix,
66 const SkRect& sourceBounds,
67 const SkPoint expectedLocalOutputQuad[4]) {
68 SkRect device_input_bounds = matrix.mapRect(sourceBounds);
69 SkPoint expected_output_quad[4];
70 matrix.mapPoints(expected_output_quad, expectedLocalOutputQuad, 4);
71
72 SkIRect device_filter_ibounds;
73 ASSERT_EQ(filter.map_device_bounds(device_input_bounds.roundOut(), matrix,
74 device_filter_ibounds),
75 &device_filter_ibounds);
76 ASSERT_TRUE(containsInclusive(device_filter_ibounds, expected_output_quad));
77
78 SkIRect reverse_input_ibounds;
79 ASSERT_EQ(filter.get_input_device_bounds(device_filter_ibounds, matrix,
80 reverse_input_ibounds),
81 &reverse_input_ibounds);
82 ASSERT_TRUE(containsInclusive(reverse_input_ibounds, device_input_bounds));
83}
84
85static void TestInvalidBounds(const DlImageFilter& filter,
86 const SkMatrix& matrix,
87 const SkRect& localInputBounds) {
88 SkIRect device_input_bounds = matrix.mapRect(localInputBounds).roundOut();
89
90 SkRect local_filter_bounds;
91 ASSERT_EQ(filter.map_local_bounds(localInputBounds, local_filter_bounds),
92 nullptr);
93 ASSERT_EQ(local_filter_bounds, localInputBounds);
94
95 SkIRect device_filter_ibounds;
96 ASSERT_EQ(filter.map_device_bounds(device_input_bounds, matrix,
97 device_filter_ibounds),
98 nullptr);
99 ASSERT_EQ(device_filter_ibounds, device_input_bounds);
100
101 SkIRect reverse_input_ibounds;
102 ASSERT_EQ(filter.get_input_device_bounds(device_input_bounds, matrix,
103 reverse_input_ibounds),
104 nullptr);
105 ASSERT_EQ(reverse_input_ibounds, device_input_bounds);
106}
107
108// localInputBounds is a sample bounds for testing as input to the filter.
109// localExpectOutputBounds is the theoretical output bounds for applying
110// the filter to the localInputBounds.
111// localExpectInputBounds is the theoretical input bounds required for the
112// filter to cover the localExpectOutputBounds
113// If either of the expected bounds are nullptr then the bounds methods will
114// be assumed to be unable to perform their computations for the given
115// image filter and will be returning null.
116static void TestBounds(const DlImageFilter& filter,
117 const SkRect& sourceBounds,
118 const SkPoint expectedLocalOutputQuad[4]) {
119 SkRect local_filter_bounds;
120 ASSERT_EQ(filter.map_local_bounds(sourceBounds, local_filter_bounds),
121 &local_filter_bounds);
122 ASSERT_TRUE(containsInclusive(local_filter_bounds, expectedLocalOutputQuad));
123
124 for (int scale = 1; scale <= 4; scale++) {
125 for (int skew = 0; skew < 8; skew++) {
126 for (int degrees = 0; degrees <= 360; degrees += 15) {
127 SkMatrix matrix;
128 matrix.setScale(scale, scale);
129 matrix.postSkew(skew / 8.0, skew / 8.0);
130 matrix.postRotate(degrees);
131 ASSERT_TRUE(matrix.invert(nullptr));
132 TestBoundsWithMatrix(filter, matrix, sourceBounds,
133 expectedLocalOutputQuad);
134 matrix.setPerspX(0.001);
135 matrix.setPerspY(0.001);
136 ASSERT_TRUE(matrix.invert(nullptr));
137 TestBoundsWithMatrix(filter, matrix, sourceBounds,
138 expectedLocalOutputQuad);
139 }
140 }
141 }
142}
143
144static void TestBounds(const DlImageFilter& filter,
145 const SkRect& sourceBounds,
146 const SkRect& expectedLocalOutputBounds) {
147 SkPoint expected_local_output_quad[4];
148 expectedLocalOutputBounds.toQuad(expected_local_output_quad);
149 TestBounds(filter, sourceBounds, expected_local_output_quad);
150}
151
152TEST(DisplayListImageFilter, BlurConstructor) {
153 DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
154}
155
156TEST(DisplayListImageFilter, BlurShared) {
157 DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
158
159 ASSERT_NE(filter.shared().get(), &filter);
160 ASSERT_EQ(*filter.shared(), filter);
161}
162
163TEST(DisplayListImageFilter, BlurAsBlur) {
164 DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
165
166 ASSERT_NE(filter.asBlur(), nullptr);
167 ASSERT_EQ(filter.asBlur(), &filter);
168}
169
170TEST(DisplayListImageFilter, BlurContents) {
171 DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
172
173 ASSERT_EQ(filter.sigma_x(), 5.0);
174 ASSERT_EQ(filter.sigma_y(), 6.0);
175 ASSERT_EQ(filter.tile_mode(), DlTileMode::kMirror);
176}
177
178TEST(DisplayListImageFilter, BlurEquals) {
179 DlBlurImageFilter filter1(5.0, 6.0, DlTileMode::kMirror);
180 DlBlurImageFilter filter2(5.0, 6.0, DlTileMode::kMirror);
181
182 TestEquals(filter1, filter2);
183}
184
185TEST(DisplayListImageFilter, BlurWithLocalMatrixEquals) {
186 DlBlurImageFilter filter1(5.0, 6.0, DlTileMode::kMirror);
187 DlBlurImageFilter filter2(5.0, 6.0, DlTileMode::kMirror);
188
189 SkMatrix local_matrix = SkMatrix::Translate(10, 10);
190 TestEquals(*filter1.makeWithLocalMatrix(local_matrix),
191 *filter2.makeWithLocalMatrix(local_matrix));
192}
193
194TEST(DisplayListImageFilter, BlurNotEquals) {
195 DlBlurImageFilter filter1(5.0, 6.0, DlTileMode::kMirror);
196 DlBlurImageFilter filter2(7.0, 6.0, DlTileMode::kMirror);
197 DlBlurImageFilter filter3(5.0, 8.0, DlTileMode::kMirror);
198 DlBlurImageFilter filter4(5.0, 6.0, DlTileMode::kRepeat);
199
200 TestNotEquals(filter1, filter2, "Sigma X differs");
201 TestNotEquals(filter1, filter3, "Sigma Y differs");
202 TestNotEquals(filter1, filter4, "Tile Mode differs");
203}
204
205TEST(DisplayListImageFilter, BlurBounds) {
207 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
208 SkRect expected_output_bounds = input_bounds.makeOutset(15, 30);
209 TestBounds(filter, input_bounds, expected_output_bounds);
210}
211
212TEST(DisplayListImageFilter, BlurZeroSigma) {
213 std::shared_ptr<DlImageFilter> filter =
215 ASSERT_EQ(filter, nullptr);
217 ASSERT_EQ(filter, nullptr);
219 ASSERT_EQ(filter, nullptr);
220 filter =
222 ASSERT_EQ(filter, nullptr);
224 ASSERT_NE(filter, nullptr);
226 ASSERT_NE(filter, nullptr);
227}
228
229TEST(DisplayListImageFilter, DilateConstructor) {
230 DlDilateImageFilter filter(5.0, 6.0);
231}
232
233TEST(DisplayListImageFilter, DilateShared) {
234 DlDilateImageFilter filter(5.0, 6.0);
235
236 ASSERT_NE(filter.shared().get(), &filter);
237 ASSERT_EQ(*filter.shared(), filter);
238}
239
240TEST(DisplayListImageFilter, DilateAsDilate) {
241 DlDilateImageFilter filter(5.0, 6.0);
242
243 ASSERT_NE(filter.asDilate(), nullptr);
244 ASSERT_EQ(filter.asDilate(), &filter);
245}
246
247TEST(DisplayListImageFilter, DilateContents) {
248 DlDilateImageFilter filter(5.0, 6.0);
249
250 ASSERT_EQ(filter.radius_x(), 5.0);
251 ASSERT_EQ(filter.radius_y(), 6.0);
252}
253
254TEST(DisplayListImageFilter, DilateEquals) {
255 DlDilateImageFilter filter1(5.0, 6.0);
256 DlDilateImageFilter filter2(5.0, 6.0);
257
258 TestEquals(filter1, filter2);
259}
260
261TEST(DisplayListImageFilter, DilateWithLocalMatrixEquals) {
262 DlDilateImageFilter filter1(5.0, 6.0);
263 DlDilateImageFilter filter2(5.0, 6.0);
264
265 SkMatrix local_matrix = SkMatrix::Translate(10, 10);
266 TestEquals(*filter1.makeWithLocalMatrix(local_matrix),
267 *filter2.makeWithLocalMatrix(local_matrix));
268}
269
270TEST(DisplayListImageFilter, DilateNotEquals) {
271 DlDilateImageFilter filter1(5.0, 6.0);
272 DlDilateImageFilter filter2(7.0, 6.0);
273 DlDilateImageFilter filter3(5.0, 8.0);
274
275 TestNotEquals(filter1, filter2, "Radius X differs");
276 TestNotEquals(filter1, filter3, "Radius Y differs");
277}
278
279TEST(DisplayListImageFilter, DilateBounds) {
281 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
282 SkRect expected_output_bounds = input_bounds.makeOutset(5, 10);
283 TestBounds(filter, input_bounds, expected_output_bounds);
284}
285
286TEST(DisplayListImageFilter, ErodeConstructor) {
287 DlErodeImageFilter filter(5.0, 6.0);
288}
289
290TEST(DisplayListImageFilter, ErodeShared) {
291 DlErodeImageFilter filter(5.0, 6.0);
292
293 ASSERT_NE(filter.shared().get(), &filter);
294 ASSERT_EQ(*filter.shared(), filter);
295}
296
297TEST(DisplayListImageFilter, ErodeAsErode) {
298 DlErodeImageFilter filter(5.0, 6.0);
299
300 ASSERT_NE(filter.asErode(), nullptr);
301 ASSERT_EQ(filter.asErode(), &filter);
302}
303
304TEST(DisplayListImageFilter, ErodeContents) {
305 DlErodeImageFilter filter(5.0, 6.0);
306
307 ASSERT_EQ(filter.radius_x(), 5.0);
308 ASSERT_EQ(filter.radius_y(), 6.0);
309}
310
311TEST(DisplayListImageFilter, ErodeEquals) {
312 DlErodeImageFilter filter1(5.0, 6.0);
313 DlErodeImageFilter filter2(5.0, 6.0);
314
315 TestEquals(filter1, filter2);
316}
317
318TEST(DisplayListImageFilter, ErodeWithLocalMatrixEquals) {
319 DlErodeImageFilter filter1(5.0, 6.0);
320 DlErodeImageFilter filter2(5.0, 6.0);
321
322 SkMatrix local_matrix = SkMatrix::Translate(10, 10);
323 TestEquals(*filter1.makeWithLocalMatrix(local_matrix),
324 *filter2.makeWithLocalMatrix(local_matrix));
325}
326
327TEST(DisplayListImageFilter, ErodeNotEquals) {
328 DlErodeImageFilter filter1(5.0, 6.0);
329 DlErodeImageFilter filter2(7.0, 6.0);
330 DlErodeImageFilter filter3(5.0, 8.0);
331
332 TestNotEquals(filter1, filter2, "Radius X differs");
333 TestNotEquals(filter1, filter3, "Radius Y differs");
334}
335
336TEST(DisplayListImageFilter, ErodeBounds) {
338 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
339 SkRect expected_output_bounds = input_bounds.makeInset(5, 10);
340 TestBounds(filter, input_bounds, expected_output_bounds);
341}
342
343TEST(DisplayListImageFilter, MatrixConstructor) {
344 DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
345 0.5, 3.0, 15, //
346 0.0, 0.0, 1),
348}
349
350TEST(DisplayListImageFilter, MatrixShared) {
351 DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
352 0.5, 3.0, 15, //
353 0.0, 0.0, 1),
355
356 ASSERT_NE(filter.shared().get(), &filter);
357 ASSERT_EQ(*filter.shared(), filter);
358}
359
360TEST(DisplayListImageFilter, MatrixAsMatrix) {
361 DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
362 0.5, 3.0, 15, //
363 0.0, 0.0, 1),
365
366 ASSERT_NE(filter.asMatrix(), nullptr);
367 ASSERT_EQ(filter.asMatrix(), &filter);
368}
369
370TEST(DisplayListImageFilter, MatrixContents) {
371 SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
372 0.5, 3.0, 15, //
373 0.0, 0.0, 1);
375
376 ASSERT_EQ(filter.matrix(), matrix);
377 ASSERT_EQ(filter.sampling(), DlImageSampling::kLinear);
378}
379
380TEST(DisplayListImageFilter, MatrixEquals) {
381 SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
382 0.5, 3.0, 15, //
383 0.0, 0.0, 1);
386
387 TestEquals(filter1, filter2);
388}
389
390TEST(DisplayListImageFilter, MatrixWithLocalMatrixEquals) {
391 SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
392 0.5, 3.0, 15, //
393 0.0, 0.0, 1);
396
397 SkMatrix local_matrix = SkMatrix::Translate(10, 10);
398 TestEquals(*filter1.makeWithLocalMatrix(local_matrix),
399 *filter2.makeWithLocalMatrix(local_matrix));
400}
401
402TEST(DisplayListImageFilter, MatrixNotEquals) {
403 SkMatrix matrix1 = SkMatrix::MakeAll(2.0, 0.0, 10, //
404 0.5, 3.0, 15, //
405 0.0, 0.0, 1);
406 SkMatrix matrix2 = SkMatrix::MakeAll(5.0, 0.0, 10, //
407 0.5, 3.0, 15, //
408 0.0, 0.0, 1);
412
413 TestNotEquals(filter1, filter2, "Matrix differs");
414 TestNotEquals(filter1, filter3, "Sampling differs");
415}
416
417TEST(DisplayListImageFilter, MatrixBounds) {
418 SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
419 0.5, 3.0, 7, //
420 0.0, 0.0, 1);
421 SkMatrix inverse;
422 ASSERT_TRUE(matrix.invert(&inverse));
424 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
425 SkPoint expectedOutputQuad[4] = {
426 {50, 77}, // (20,20) => (20*2 + 10, 20/2 + 20*3 + 7) == (50, 77)
427 {50, 257}, // (20,80) => (20*2 + 10, 20/2 + 80*3 + 7) == (50, 257)
428 {170, 287}, // (80,80) => (80*2 + 10, 80/2 + 80*3 + 7) == (170, 287)
429 {170, 107}, // (80,20) => (80*2 + 10, 80/2 + 20*3 + 7) == (170, 107)
430 };
431 TestBounds(filter, input_bounds, expectedOutputQuad);
432}
433
434TEST(DisplayListImageFilter, ComposeConstructor) {
435 DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
436 0.5, 3.0, 15, //
437 0.0, 0.0, 1),
439 DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
440 DlComposeImageFilter filter(outer, inner);
441}
442
443TEST(DisplayListImageFilter, ComposeShared) {
444 DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
445 0.5, 3.0, 15, //
446 0.0, 0.0, 1),
448 DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
449 DlComposeImageFilter filter(outer, inner);
450
451 ASSERT_NE(filter.shared().get(), &filter);
452 ASSERT_EQ(*filter.shared(), filter);
453}
454
455TEST(DisplayListImageFilter, ComposeAsCompose) {
456 DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
457 0.5, 3.0, 15, //
458 0.0, 0.0, 1),
460 DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
461 DlComposeImageFilter filter(outer, inner);
462
463 ASSERT_NE(filter.asCompose(), nullptr);
464 ASSERT_EQ(filter.asCompose(), &filter);
465}
466
467TEST(DisplayListImageFilter, ComposeContents) {
468 DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
469 0.5, 3.0, 15, //
470 0.0, 0.0, 1),
472 DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
473 DlComposeImageFilter filter(outer, inner);
474
475 ASSERT_EQ(*filter.outer().get(), outer);
476 ASSERT_EQ(*filter.inner().get(), inner);
477}
478
479TEST(DisplayListImageFilter, ComposeEquals) {
480 DlMatrixImageFilter outer1(SkMatrix::MakeAll(2.0, 0.0, 10, //
481 0.5, 3.0, 15, //
482 0.0, 0.0, 1),
484 DlBlurImageFilter inner1(5.0, 6.0, DlTileMode::kMirror);
485 DlComposeImageFilter filter1(outer1, inner1);
486
487 DlMatrixImageFilter outer2(SkMatrix::MakeAll(2.0, 0.0, 10, //
488 0.5, 3.0, 15, //
489 0.0, 0.0, 1),
491 DlBlurImageFilter inner2(5.0, 6.0, DlTileMode::kMirror);
492 DlComposeImageFilter filter2(outer1, inner1);
493
494 TestEquals(filter1, filter2);
495}
496
497TEST(DisplayListImageFilter, ComposeWithLocalMatrixEquals) {
498 DlMatrixImageFilter outer1(SkMatrix::MakeAll(2.0, 0.0, 10, //
499 0.5, 3.0, 15, //
500 0.0, 0.0, 1),
502 DlBlurImageFilter inner1(5.0, 6.0, DlTileMode::kMirror);
503 DlComposeImageFilter filter1(outer1, inner1);
504
505 DlMatrixImageFilter outer2(SkMatrix::MakeAll(2.0, 0.0, 10, //
506 0.5, 3.0, 15, //
507 0.0, 0.0, 1),
509 DlBlurImageFilter inner2(5.0, 6.0, DlTileMode::kMirror);
510 DlComposeImageFilter filter2(outer1, inner1);
511
512 SkMatrix local_matrix = SkMatrix::Translate(10, 10);
513 TestEquals(*filter1.makeWithLocalMatrix(local_matrix),
514 *filter2.makeWithLocalMatrix(local_matrix));
515}
516
517TEST(DisplayListImageFilter, ComposeNotEquals) {
518 DlMatrixImageFilter outer1(SkMatrix::MakeAll(2.0, 0.0, 10, //
519 0.5, 3.0, 15, //
520 0.0, 0.0, 1),
522 DlBlurImageFilter inner1(5.0, 6.0, DlTileMode::kMirror);
523
524 DlMatrixImageFilter outer2(SkMatrix::MakeAll(5.0, 0.0, 10, //
525 0.5, 3.0, 15, //
526 0.0, 0.0, 1),
528 DlBlurImageFilter inner2(7.0, 6.0, DlTileMode::kMirror);
529
530 DlComposeImageFilter filter1(outer1, inner1);
531 DlComposeImageFilter filter2(outer2, inner1);
532 DlComposeImageFilter filter3(outer1, inner2);
533
534 TestNotEquals(filter1, filter2, "Outer differs");
535 TestNotEquals(filter1, filter3, "Inner differs");
536}
537
538TEST(DisplayListImageFilter, ComposeBounds) {
541 DlComposeImageFilter filter = DlComposeImageFilter(outer, inner);
542 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
543 SkRect expected_output_bounds =
544 input_bounds.makeOutset(36, 15).makeOutset(5, 10);
545 TestBounds(filter, input_bounds, expected_output_bounds);
546}
547
549 const SkRect& sourceBounds,
550 const SkRect& expectedOutputBounds,
551 const SkRect& expectedInputBounds) {
552 SkRect bounds;
553 EXPECT_EQ(filter.map_local_bounds(sourceBounds, bounds), nullptr);
554 EXPECT_EQ(bounds, expectedOutputBounds);
555
556 SkIRect ibounds;
557 EXPECT_EQ(
558 filter.map_device_bounds(sourceBounds.roundOut(), SkMatrix::I(), ibounds),
559 nullptr);
560 EXPECT_EQ(ibounds, expectedOutputBounds.roundOut());
561
562 EXPECT_EQ(filter.get_input_device_bounds(sourceBounds.roundOut(),
563 SkMatrix::I(), ibounds),
564 nullptr);
565 EXPECT_EQ(ibounds, expectedInputBounds.roundOut());
566}
567
568TEST(DisplayListImageFilter, ComposeBoundsWithUnboundedInner) {
569 auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
570 auto expected_bounds = SkRect::MakeLTRB(5, 2, 95, 98);
571
573 auto outer = DlBlurImageFilter(5.0, 6.0, DlTileMode::kRepeat);
574 auto inner = DlColorFilterImageFilter(color_filter.shared());
575 auto composed = DlComposeImageFilter(outer.shared(), inner.shared());
576
577 TestUnboundedBounds(composed, input_bounds, expected_bounds, expected_bounds);
578}
579
580TEST(DisplayListImageFilter, ComposeBoundsWithUnboundedOuter) {
581 auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
582 auto expected_bounds = SkRect::MakeLTRB(5, 2, 95, 98);
583
585 auto outer = DlColorFilterImageFilter(color_filter.shared());
586 auto inner = DlBlurImageFilter(5.0, 6.0, DlTileMode::kRepeat);
587 auto composed = DlComposeImageFilter(outer.shared(), inner.shared());
588
589 TestUnboundedBounds(composed, input_bounds, expected_bounds, expected_bounds);
590}
591
592TEST(DisplayListImageFilter, ComposeBoundsWithUnboundedInnerAndOuter) {
593 auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
594 auto expected_bounds = input_bounds;
595
598 auto outer = DlColorFilterImageFilter(color_filter1.shared());
599 auto inner = DlColorFilterImageFilter(color_filter2.shared());
600 auto composed = DlComposeImageFilter(outer.shared(), inner.shared());
601
602 TestUnboundedBounds(composed, input_bounds, expected_bounds, expected_bounds);
603}
604
605// See https://github.com/flutter/flutter/issues/108433
606TEST(DisplayListImageFilter, Issue108433) {
607 auto input_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
608 auto expected_bounds = SkIRect::MakeLTRB(5, 2, 95, 98);
609
611 auto dl_outer = DlBlurImageFilter(5.0, 6.0, DlTileMode::kRepeat);
612 auto dl_inner = DlColorFilterImageFilter(dl_color_filter.shared());
613 auto dl_compose = DlComposeImageFilter(dl_outer, dl_inner);
614
615 SkIRect dl_bounds;
616 ASSERT_EQ(
617 dl_compose.map_device_bounds(input_bounds, SkMatrix::I(), dl_bounds),
618 nullptr);
619 ASSERT_EQ(dl_bounds, expected_bounds);
620}
621
622TEST(DisplayListImageFilter, ColorFilterConstructor) {
624 DlColorFilterImageFilter filter(dl_color_filter);
625}
626
627TEST(DisplayListImageFilter, ColorFilterShared) {
629 DlColorFilterImageFilter filter(dl_color_filter);
630
631 ASSERT_EQ(*filter.shared(), filter);
632}
633
634TEST(DisplayListImageFilter, ColorFilterAsColorFilter) {
636 DlColorFilterImageFilter filter(dl_color_filter);
637
638 ASSERT_NE(filter.asColorFilter(), nullptr);
639 ASSERT_EQ(filter.asColorFilter(), &filter);
640}
641
642TEST(DisplayListImageFilter, ColorFilterContents) {
644 DlColorFilterImageFilter filter(dl_color_filter);
645
646 ASSERT_EQ(*filter.color_filter().get(), dl_color_filter);
647}
648
649TEST(DisplayListImageFilter, ColorFilterEquals) {
651 DlColorFilterImageFilter filter1(dl_color_filter1);
652
654 DlColorFilterImageFilter filter2(dl_color_filter2);
655
656 TestEquals(filter1, filter2);
657}
658
659TEST(DisplayListImageFilter, ColorFilterWithLocalMatrixEquals) {
661 DlColorFilterImageFilter filter1(dl_color_filter1);
662
664 DlColorFilterImageFilter filter2(dl_color_filter2);
665
666 SkMatrix local_matrix = SkMatrix::Translate(10, 10);
667 TestEquals(*filter1.makeWithLocalMatrix(local_matrix),
668 *filter2.makeWithLocalMatrix(local_matrix));
669}
670
671TEST(DisplayListImageFilter, ColorFilterNotEquals) {
673 DlColorFilterImageFilter filter1(dl_color_filter1);
674
676 DlColorFilterImageFilter filter2(dl_color_filter2);
677
679 DlColorFilterImageFilter filter3(dl_color_filter3);
680
681 TestNotEquals(filter1, filter2, "Color differs");
682 TestNotEquals(filter1, filter3, "Blend Mode differs");
683}
684
685TEST(DisplayListImageFilter, ColorFilterBounds) {
687 DlColorFilterImageFilter filter(dl_color_filter);
688 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
689 TestBounds(filter, input_bounds, input_bounds);
690}
691
692TEST(DisplayListImageFilter, ColorFilterModifiesTransparencyBounds) {
694 DlColorFilterImageFilter filter(dl_color_filter);
695 SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
696 TestInvalidBounds(filter, SkMatrix::I(), input_bounds);
697}
698
699TEST(DisplayListImageFilter, LocalImageFilterBounds) {
700 auto filter_matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
701 0.5, 3.0, 15, //
702 0.0, 0.0, 1);
703 std::vector<sk_sp<SkImageFilter>> sk_filters{
704 SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr),
707 SkImageFilters::Dilate(5.0, 10.0, nullptr),
709 filter_matrix, SkSamplingOptions(SkFilterMode::kLinear), nullptr),
711 SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr),
714 nullptr))};
715
717 std::vector<std::shared_ptr<DlImageFilter>> dl_filters{
718 std::make_shared<DlBlurImageFilter>(5.0, 6.0, DlTileMode::kRepeat),
719 std::make_shared<DlColorFilterImageFilter>(dl_color_filter.shared()),
720 std::make_shared<DlDilateImageFilter>(5, 10),
721 std::make_shared<DlMatrixImageFilter>(filter_matrix,
723 std::make_shared<DlComposeImageFilter>(
724 std::make_shared<DlBlurImageFilter>(5.0, 6.0, DlTileMode::kRepeat),
725 std::make_shared<DlColorFilterImageFilter>(
726 dl_color_filter.shared()))};
727
728 auto persp = SkMatrix::I();
729 persp.setPerspY(0.001);
730 std::vector<SkMatrix> matrices = {
731 SkMatrix::Translate(10.0, 10.0),
732 SkMatrix::Scale(2.0, 2.0).preTranslate(10.0, 10.0),
733 SkMatrix::RotateDeg(45).preTranslate(5.0, 5.0), persp};
734 std::vector<SkMatrix> bounds_matrices{SkMatrix::Translate(5.0, 10.0),
735 SkMatrix::Scale(2.0, 2.0)};
736
737 for (unsigned j = 0; j < matrices.size(); j++) {
738 DlLocalMatrixImageFilter filter(matrices[j], nullptr);
739 {
740 const auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
741 SkRect output_bounds;
742 EXPECT_EQ(filter.map_local_bounds(input_bounds, output_bounds),
743 &output_bounds);
744 EXPECT_EQ(input_bounds, output_bounds);
745 }
746 for (unsigned k = 0; k < bounds_matrices.size(); k++) {
747 auto& bounds_matrix = bounds_matrices[k];
748 {
749 const auto input_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
750 SkIRect output_bounds;
751 EXPECT_EQ(filter.map_device_bounds(input_bounds, bounds_matrix,
752 output_bounds),
753 &output_bounds);
754 EXPECT_EQ(input_bounds, output_bounds);
755 }
756 {
757 const auto output_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
758 SkIRect input_bounds;
759 EXPECT_EQ(filter.get_input_device_bounds(output_bounds, bounds_matrix,
760 input_bounds),
761 &input_bounds);
762 EXPECT_EQ(input_bounds, output_bounds);
763 }
764 }
765 }
766
767 for (unsigned i = 0; i < sk_filters.size(); i++) {
768 for (unsigned j = 0; j < matrices.size(); j++) {
769 for (unsigned k = 0; k < bounds_matrices.size(); k++) {
770 auto desc = "filter " + std::to_string(i + 1) //
771 + ", filter matrix " + std::to_string(j + 1) //
772 + ", bounds matrix " + std::to_string(k + 1);
773 auto& m = matrices[j];
774 auto& bounds_matrix = bounds_matrices[k];
775 auto sk_local_filter = sk_filters[i]->makeWithLocalMatrix(m);
776 auto dl_local_filter = dl_filters[i]->makeWithLocalMatrix(m);
777 if (!sk_local_filter || !dl_local_filter) {
778 // Temporarily relax the equivalence testing to allow Skia to expand
779 // their behavior. Once the Skia fixes are rolled in, the
780 // DlImageFilter should adapt to the new rules.
781 // See https://github.com/flutter/flutter/issues/114723
782 ASSERT_TRUE(sk_local_filter || !dl_local_filter) << desc;
783 continue;
784 }
785 {
786 auto input_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
787 SkIRect sk_rect, dl_rect;
788 sk_rect = sk_local_filter->filterBounds(
789 input_bounds, bounds_matrix,
791 if (dl_local_filter->map_device_bounds(input_bounds, bounds_matrix,
792 dl_rect)) {
793 ASSERT_EQ(sk_rect, dl_rect) << desc;
794 } else {
795 ASSERT_TRUE(dl_local_filter->modifies_transparent_black()) << desc;
796 ASSERT_FALSE(sk_local_filter->canComputeFastBounds()) << desc;
797 }
798 }
799 {
800 // Test for: Know the outset bounds to get the inset bounds
801 // Skia have some bounds calculate error of DilateFilter and
802 // MatrixFilter
803 // Skia issue: https://bugs.chromium.org/p/skia/issues/detail?id=13444
804 // flutter issue: https://github.com/flutter/flutter/issues/108693
805 if (i == 2 || i == 3) {
806 continue;
807 }
808 auto outset_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
809 SkIRect sk_rect, dl_rect;
810 sk_rect = sk_local_filter->filterBounds(
811 outset_bounds, bounds_matrix,
813 if (dl_local_filter->get_input_device_bounds(
814 outset_bounds, bounds_matrix, dl_rect)) {
815 ASSERT_EQ(sk_rect, dl_rect) << desc;
816 } else {
817 ASSERT_TRUE(dl_local_filter->modifies_transparent_black());
818 ASSERT_FALSE(sk_local_filter->canComputeFastBounds());
819 }
820 }
821 }
822 }
823 }
824}
825
826} // namespace testing
827} // namespace flutter
#define TEST(S, s, D, expected)
@ kSrcOver
r = s + (1-sa)*d
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
static sk_sp< SkImage > color_filter(const SkImage *image, SkColorFilter *colorFilter)
#define SK_ScalarNaN
Definition SkScalar.h:28
static sk_sp< SkColorFilter > Blend(const SkColor4f &c, sk_sp< SkColorSpace >, SkBlendMode mode)
static sk_sp< SkImageFilter > ColorFilter(sk_sp< SkColorFilter > cf, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Blur(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Compose(sk_sp< SkImageFilter > outer, sk_sp< SkImageFilter > inner)
static sk_sp< SkImageFilter > MatrixTransform(const SkMatrix &matrix, const SkSamplingOptions &sampling, sk_sp< SkImageFilter > input)
static sk_sp< SkImageFilter > Dilate(SkScalar radiusX, SkScalar radiusY, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
static SkMatrix RotateDeg(SkScalar deg)
Definition SkMatrix.h:104
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition SkMatrix.h:91
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition SkMatrix.h:179
static const SkMatrix & I()
SkMatrix & preTranslate(SkScalar dx, SkScalar dy)
Definition SkMatrix.cpp:263
std::shared_ptr< DlColorFilter > shared() const override
std::shared_ptr< DlImageFilter > shared() const override
static std::shared_ptr< DlImageFilter > Make(SkScalar sigma_x, SkScalar sigma_y, DlTileMode tile_mode)
const DlBlurImageFilter * asBlur() const override
DlTileMode tile_mode() const
const std::shared_ptr< const DlColorFilter > color_filter() const
std::shared_ptr< DlImageFilter > makeWithLocalMatrix(const SkMatrix &matrix) const override
const DlColorFilterImageFilter * asColorFilter() const override
std::shared_ptr< DlImageFilter > shared() const override
std::shared_ptr< DlImageFilter > shared() const override
const DlComposeImageFilter * asCompose() const override
std::shared_ptr< const DlImageFilter > outer() const
std::shared_ptr< const DlImageFilter > inner() const
const DlDilateImageFilter * asDilate() const override
std::shared_ptr< DlImageFilter > shared() const override
const DlErodeImageFilter * asErode() const override
std::shared_ptr< DlImageFilter > shared() const override
virtual SkIRect * map_device_bounds(const SkIRect &input_bounds, const SkMatrix &ctm, SkIRect &output_bounds) const =0
virtual SkRect * map_local_bounds(const SkRect &input_bounds, SkRect &output_bounds) const =0
virtual SkIRect * get_input_device_bounds(const SkIRect &output_bounds, const SkMatrix &ctm, SkIRect &input_bounds) const =0
virtual std::shared_ptr< DlImageFilter > makeWithLocalMatrix(const SkMatrix &matrix) const
SkIRect * map_device_bounds(const SkIRect &input_bounds, const SkMatrix &ctm, SkIRect &output_bounds) const override
SkRect * map_local_bounds(const SkRect &input_bounds, SkRect &output_bounds) const override
SkIRect * get_input_device_bounds(const SkIRect &output_bounds, const SkMatrix &ctm, SkIRect &input_bounds) const override
const DlMatrixImageFilter * asMatrix() const override
std::shared_ptr< DlImageFilter > shared() const override
const SkMatrix & matrix() const
DlImageSampling sampling() const
SkRect sk_rect
static void TestInvalidBounds(const DlImageFilter &filter, const SkMatrix &matrix, const SkRect &localInputBounds)
static void TestBoundsWithMatrix(const DlImageFilter &filter, const SkMatrix &matrix, const SkRect &sourceBounds, const SkPoint expectedLocalOutputQuad[4])
static void TestUnboundedBounds(DlImageFilter &filter, const SkRect &sourceBounds, const SkRect &expectedOutputBounds, const SkRect &expectedInputBounds)
static bool containsInclusive(const SkRect rect, const SkPoint p)
static void TestBounds(const DlImageFilter &filter, const SkRect &sourceBounds, const SkPoint expectedLocalOutputQuad[4])
static void TestNotEquals(T &source1, T &source2, const std::string &label)
static void TestEquals(T &source1, T &source2)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kSrcOver
r = s + (1-sa)*d
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
const Scalar scale
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition SkRect.h:91
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
void toQuad(SkPoint quad[4]) const
Definition SkRect.cpp:50
SkRect makeOutset(float dx, float dy) const
Definition SkRect.h:1002
SkRect makeInset(float dx, float dy) const
Definition SkRect.h:987
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
static constexpr DlColor kBlue()
Definition dl_color.h:26
static constexpr DlColor kRed()
Definition dl_color.h:24