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/color_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/SkColorFilter.h" |
12 | #include "third_party/skia/include/effects/SkColorMatrixFilter.h" |
13 | |
14 | namespace flutter { |
15 | namespace testing { |
16 | |
17 | using ColorFilterLayerTest = LayerTest; |
18 | |
19 | #ifndef NDEBUG |
20 | TEST_F(ColorFilterLayerTest, PaintingEmptyLayerDies) { |
21 | auto layer = std::make_shared<ColorFilterLayer>(sk_sp<SkColorFilter>()); |
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(ColorFilterLayerTest, 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<ColorFilterLayer>(sk_sp<SkColorFilter>()); |
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(ColorFilterLayerTest, 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<ColorFilterLayer>(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 | SkPaint filter_paint; |
60 | filter_paint.setColorFilter(nullptr); |
61 | layer->Paint(paint_context()); |
62 | EXPECT_EQ( |
63 | mock_canvas().draw_calls(), |
64 | std::vector({MockCanvas::DrawCall{ |
65 | 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, |
66 | nullptr, 1}}, |
67 | MockCanvas::DrawCall{ |
68 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
69 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
70 | } |
71 | |
72 | TEST_F(ColorFilterLayerTest, SimpleFilter) { |
73 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
74 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
75 | const SkPath child_path = SkPath().addRect(child_bounds); |
76 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
77 | auto layer_filter = |
78 | SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); |
79 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
80 | auto layer = std::make_shared<ColorFilterLayer>(layer_filter); |
81 | layer->Add(mock_layer); |
82 | |
83 | layer->Preroll(preroll_context(), initial_transform); |
84 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
85 | EXPECT_TRUE(layer->needs_painting()); |
86 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
87 | |
88 | SkPaint filter_paint; |
89 | filter_paint.setColorFilter(layer_filter); |
90 | layer->Paint(paint_context()); |
91 | EXPECT_EQ( |
92 | mock_canvas().draw_calls(), |
93 | std::vector({MockCanvas::DrawCall{ |
94 | 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, |
95 | nullptr, 1}}, |
96 | MockCanvas::DrawCall{ |
97 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
98 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
99 | } |
100 | |
101 | TEST_F(ColorFilterLayerTest, MultipleChildren) { |
102 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
103 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); |
104 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
105 | const SkPath child_path2 = |
106 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
107 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
108 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
109 | auto layer_filter = |
110 | SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); |
111 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
112 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
113 | auto layer = std::make_shared<ColorFilterLayer>(layer_filter); |
114 | layer->Add(mock_layer1); |
115 | layer->Add(mock_layer2); |
116 | |
117 | SkRect children_bounds = child_path1.getBounds(); |
118 | children_bounds.join(child_path2.getBounds()); |
119 | layer->Preroll(preroll_context(), initial_transform); |
120 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
121 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
122 | EXPECT_EQ(layer->paint_bounds(), children_bounds); |
123 | EXPECT_TRUE(mock_layer1->needs_painting()); |
124 | EXPECT_TRUE(mock_layer2->needs_painting()); |
125 | EXPECT_TRUE(layer->needs_painting()); |
126 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
127 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
128 | |
129 | SkPaint filter_paint; |
130 | filter_paint.setColorFilter(layer_filter); |
131 | layer->Paint(paint_context()); |
132 | EXPECT_EQ( |
133 | mock_canvas().draw_calls(), |
134 | std::vector({MockCanvas::DrawCall{ |
135 | 0, MockCanvas::SaveLayerData{children_bounds, |
136 | filter_paint, nullptr, 1}}, |
137 | MockCanvas::DrawCall{ |
138 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
139 | MockCanvas::DrawCall{ |
140 | 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
141 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
142 | } |
143 | |
144 | TEST_F(ColorFilterLayerTest, Nested) { |
145 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
146 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); |
147 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
148 | const SkPath child_path2 = |
149 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
150 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
151 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
152 | auto layer_filter1 = |
153 | SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); |
154 | auto layer_filter2 = |
155 | SkColorMatrixFilter::MakeLightingFilter(SK_ColorMAGENTA, SK_ColorBLUE); |
156 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
157 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
158 | auto layer1 = std::make_shared<ColorFilterLayer>(layer_filter1); |
159 | auto layer2 = std::make_shared<ColorFilterLayer>(layer_filter2); |
160 | layer2->Add(mock_layer2); |
161 | layer1->Add(mock_layer1); |
162 | layer1->Add(layer2); |
163 | |
164 | SkRect children_bounds = child_path1.getBounds(); |
165 | children_bounds.join(child_path2.getBounds()); |
166 | layer1->Preroll(preroll_context(), initial_transform); |
167 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
168 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
169 | EXPECT_EQ(layer1->paint_bounds(), children_bounds); |
170 | EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); |
171 | EXPECT_TRUE(mock_layer1->needs_painting()); |
172 | EXPECT_TRUE(mock_layer2->needs_painting()); |
173 | EXPECT_TRUE(layer1->needs_painting()); |
174 | EXPECT_TRUE(layer2->needs_painting()); |
175 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
176 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
177 | |
178 | SkPaint filter_paint1, filter_paint2; |
179 | filter_paint1.setColorFilter(layer_filter1); |
180 | filter_paint2.setColorFilter(layer_filter2); |
181 | layer1->Paint(paint_context()); |
182 | EXPECT_EQ( |
183 | mock_canvas().draw_calls(), |
184 | std::vector({MockCanvas::DrawCall{ |
185 | 0, MockCanvas::SaveLayerData{children_bounds, |
186 | filter_paint1, nullptr, 1}}, |
187 | MockCanvas::DrawCall{ |
188 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
189 | MockCanvas::DrawCall{ |
190 | 1, MockCanvas::SaveLayerData{child_path2.getBounds(), |
191 | filter_paint2, nullptr, 2}}, |
192 | MockCanvas::DrawCall{ |
193 | 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
194 | MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, |
195 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
196 | } |
197 | |
198 | TEST_F(ColorFilterLayerTest, Readback) { |
199 | auto layer_filter = SkColorFilters::LinearToSRGBGamma(); |
200 | auto initial_transform = SkMatrix(); |
201 | |
202 | // ColorFilterLayer does not read from surface |
203 | auto layer = std::make_shared<ColorFilterLayer>(layer_filter); |
204 | preroll_context()->surface_needs_readback = false; |
205 | layer->Preroll(preroll_context(), initial_transform); |
206 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
207 | |
208 | // ColorFilterLayer blocks child with readback |
209 | auto mock_layer = |
210 | std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true); |
211 | layer->Add(mock_layer); |
212 | preroll_context()->surface_needs_readback = false; |
213 | layer->Preroll(preroll_context(), initial_transform); |
214 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
215 | } |
216 | |
217 | } // namespace testing |
218 | } // namespace flutter |
219 | |