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/flow/layers/backdrop_filter_layer.h" |
6 | |
7 | #include "flutter/flow/testing/layer_test.h" |
8 | #include "flutter/flow/testing/mock_layer.h" |
9 | #include "flutter/fml/macros.h" |
10 | #include "flutter/testing/mock_canvas.h" |
11 | #include "third_party/skia/include/core/SkImageFilter.h" |
12 | #include "third_party/skia/include/effects/SkImageFilters.h" |
13 | |
14 | namespace flutter { |
15 | namespace testing { |
16 | |
17 | using BackdropFilterLayerTest = LayerTest; |
18 | |
19 | #ifndef NDEBUG |
20 | TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) { |
21 | auto layer = std::make_shared<BackdropFilterLayer>(sk_sp<SkImageFilter>()); |
22 | |
23 | layer->Preroll(preroll_context(), SkMatrix()); |
24 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
25 | EXPECT_FALSE(layer->needs_painting()); |
26 | EXPECT_FALSE(layer->needs_system_composite()); |
27 | |
28 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
29 | "needs_painting\\(\\)" ); |
30 | } |
31 | |
32 | TEST_F(BackdropFilterLayerTest, PaintBeforePrerollDies) { |
33 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
34 | const SkPath child_path = SkPath().addRect(child_bounds); |
35 | auto mock_layer = std::make_shared<MockLayer>(child_path); |
36 | auto layer = std::make_shared<BackdropFilterLayer>(sk_sp<SkImageFilter>()); |
37 | layer->Add(mock_layer); |
38 | |
39 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
40 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
41 | "needs_painting\\(\\)" ); |
42 | } |
43 | #endif |
44 | |
45 | TEST_F(BackdropFilterLayerTest, EmptyFilter) { |
46 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
47 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
48 | const SkPath child_path = SkPath().addRect(child_bounds); |
49 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
50 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
51 | auto layer = std::make_shared<BackdropFilterLayer>(nullptr); |
52 | layer->Add(mock_layer); |
53 | |
54 | layer->Preroll(preroll_context(), initial_transform); |
55 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
56 | EXPECT_TRUE(layer->needs_painting()); |
57 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
58 | |
59 | layer->Paint(paint_context()); |
60 | EXPECT_EQ( |
61 | mock_canvas().draw_calls(), |
62 | std::vector({MockCanvas::DrawCall{ |
63 | 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), |
64 | nullptr, 1}}, |
65 | MockCanvas::DrawCall{ |
66 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
67 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
68 | } |
69 | |
70 | TEST_F(BackdropFilterLayerTest, SimpleFilter) { |
71 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
72 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
73 | const SkPath child_path = SkPath().addRect(child_bounds); |
74 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
75 | auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); |
76 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
77 | auto layer = std::make_shared<BackdropFilterLayer>(layer_filter); |
78 | layer->Add(mock_layer); |
79 | |
80 | layer->Preroll(preroll_context(), initial_transform); |
81 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
82 | EXPECT_TRUE(layer->needs_painting()); |
83 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
84 | |
85 | layer->Paint(paint_context()); |
86 | EXPECT_EQ( |
87 | mock_canvas().draw_calls(), |
88 | std::vector({MockCanvas::DrawCall{ |
89 | 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), |
90 | layer_filter, 1}}, |
91 | MockCanvas::DrawCall{ |
92 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
93 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
94 | } |
95 | |
96 | TEST_F(BackdropFilterLayerTest, MultipleChildren) { |
97 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
98 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); |
99 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
100 | const SkPath child_path2 = |
101 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
102 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
103 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
104 | auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); |
105 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
106 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
107 | auto layer = std::make_shared<BackdropFilterLayer>(layer_filter); |
108 | layer->Add(mock_layer1); |
109 | layer->Add(mock_layer2); |
110 | |
111 | SkRect children_bounds = child_path1.getBounds(); |
112 | children_bounds.join(child_path2.getBounds()); |
113 | layer->Preroll(preroll_context(), initial_transform); |
114 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
115 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
116 | EXPECT_EQ(layer->paint_bounds(), children_bounds); |
117 | EXPECT_TRUE(mock_layer1->needs_painting()); |
118 | EXPECT_TRUE(mock_layer2->needs_painting()); |
119 | EXPECT_TRUE(layer->needs_painting()); |
120 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
121 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
122 | |
123 | layer->Paint(paint_context()); |
124 | EXPECT_EQ( |
125 | mock_canvas().draw_calls(), |
126 | std::vector({MockCanvas::DrawCall{ |
127 | 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), |
128 | layer_filter, 1}}, |
129 | MockCanvas::DrawCall{ |
130 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
131 | MockCanvas::DrawCall{ |
132 | 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
133 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
134 | } |
135 | |
136 | TEST_F(BackdropFilterLayerTest, Nested) { |
137 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
138 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); |
139 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
140 | const SkPath child_path2 = |
141 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
142 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
143 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
144 | auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); |
145 | auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray)); |
146 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
147 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
148 | auto layer1 = std::make_shared<BackdropFilterLayer>(layer_filter1); |
149 | auto layer2 = std::make_shared<BackdropFilterLayer>(layer_filter2); |
150 | layer2->Add(mock_layer2); |
151 | layer1->Add(mock_layer1); |
152 | layer1->Add(layer2); |
153 | |
154 | SkRect children_bounds = child_path1.getBounds(); |
155 | children_bounds.join(child_path2.getBounds()); |
156 | layer1->Preroll(preroll_context(), initial_transform); |
157 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
158 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
159 | EXPECT_EQ(layer1->paint_bounds(), children_bounds); |
160 | EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); |
161 | EXPECT_TRUE(mock_layer1->needs_painting()); |
162 | EXPECT_TRUE(mock_layer2->needs_painting()); |
163 | EXPECT_TRUE(layer1->needs_painting()); |
164 | EXPECT_TRUE(layer2->needs_painting()); |
165 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
166 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
167 | |
168 | layer1->Paint(paint_context()); |
169 | EXPECT_EQ(mock_canvas().draw_calls(), |
170 | std::vector( |
171 | {MockCanvas::DrawCall{ |
172 | 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), |
173 | layer_filter1, 1}}, |
174 | MockCanvas::DrawCall{ |
175 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
176 | MockCanvas::DrawCall{ |
177 | 1, MockCanvas::SaveLayerData{child_path2.getBounds(), |
178 | SkPaint(), layer_filter2, 2}}, |
179 | MockCanvas::DrawCall{ |
180 | 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
181 | MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, |
182 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
183 | } |
184 | |
185 | TEST_F(BackdropFilterLayerTest, Readback) { |
186 | sk_sp<SkImageFilter> no_filter; |
187 | auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); |
188 | auto initial_transform = SkMatrix(); |
189 | |
190 | // BDF with filter always reads from surface |
191 | auto layer1 = std::make_shared<BackdropFilterLayer>(layer_filter); |
192 | preroll_context()->surface_needs_readback = false; |
193 | layer1->Preroll(preroll_context(), initial_transform); |
194 | EXPECT_TRUE(preroll_context()->surface_needs_readback); |
195 | |
196 | // BDF with no filter does not read from surface itself |
197 | auto layer2 = std::make_shared<BackdropFilterLayer>(no_filter); |
198 | preroll_context()->surface_needs_readback = false; |
199 | layer2->Preroll(preroll_context(), initial_transform); |
200 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
201 | |
202 | // BDF with no filter does not block prior readback value |
203 | preroll_context()->surface_needs_readback = true; |
204 | layer2->Preroll(preroll_context(), initial_transform); |
205 | EXPECT_TRUE(preroll_context()->surface_needs_readback); |
206 | |
207 | // BDF with no filter blocks child with readback |
208 | auto mock_layer = |
209 | std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true); |
210 | layer2->Add(mock_layer); |
211 | preroll_context()->surface_needs_readback = false; |
212 | layer2->Preroll(preroll_context(), initial_transform); |
213 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
214 | } |
215 | |
216 | } // namespace testing |
217 | } // namespace flutter |
218 | |