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/shader_mask_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/SkShader.h"
12#include "third_party/skia/include/effects/SkPerlinNoiseShader.h"
13
14namespace flutter {
15namespace testing {
16
17using ShaderMaskLayerTest = LayerTest;
18
19#ifndef NDEBUG
20TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) {
21 auto layer =
22 std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, SkBlendMode::kSrc);
23
24 layer->Preroll(preroll_context(), SkMatrix());
25 EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
26 EXPECT_FALSE(layer->needs_painting());
27
28 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
29 "needs_painting\\(\\)");
30}
31
32TEST_F(ShaderMaskLayerTest, PaintBeforePreollDies) {
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 =
37 std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, SkBlendMode::kSrc);
38 layer->Add(mock_layer);
39
40 EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
41 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
42 "needs_painting\\(\\)");
43}
44#endif
45
46TEST_F(ShaderMaskLayerTest, EmptyFilter) {
47 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
48 const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
49 const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f);
50 const SkPath child_path = SkPath().addRect(child_bounds);
51 const SkPaint child_paint = SkPaint(SkColors::kYellow);
52 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
53 auto layer = std::make_shared<ShaderMaskLayer>(nullptr, layer_bounds,
54 SkBlendMode::kSrc);
55 layer->Add(mock_layer);
56
57 layer->Preroll(preroll_context(), initial_transform);
58 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
59 EXPECT_EQ(layer->paint_bounds(), child_bounds);
60 EXPECT_TRUE(mock_layer->needs_painting());
61 EXPECT_TRUE(layer->needs_painting());
62 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
63
64 SkPaint filter_paint;
65 filter_paint.setBlendMode(SkBlendMode::kSrc);
66 filter_paint.setShader(nullptr);
67 layer->Paint(paint_context());
68 EXPECT_EQ(
69 mock_canvas().draw_calls(),
70 std::vector({MockCanvas::DrawCall{
71 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(),
72 nullptr, 1}},
73 MockCanvas::DrawCall{
74 1, MockCanvas::DrawPathData{child_path, child_paint}},
75 MockCanvas::DrawCall{
76 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate(
77 layer_bounds.fLeft, layer_bounds.fTop)}},
78 MockCanvas::DrawCall{
79 1, MockCanvas::DrawRectData{SkRect::MakeWH(
80 layer_bounds.width(),
81 layer_bounds.height()),
82 filter_paint}},
83 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
84}
85
86TEST_F(ShaderMaskLayerTest, SimpleFilter) {
87 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
88 const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
89 const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f);
90 const SkPath child_path = SkPath().addRect(child_bounds);
91 const SkPaint child_paint = SkPaint(SkColors::kYellow);
92 auto layer_filter =
93 SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f);
94 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
95 auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
96 SkBlendMode::kSrc);
97 layer->Add(mock_layer);
98
99 layer->Preroll(preroll_context(), initial_transform);
100 EXPECT_EQ(layer->paint_bounds(), child_bounds);
101 EXPECT_TRUE(layer->needs_painting());
102 EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
103
104 SkPaint filter_paint;
105 filter_paint.setBlendMode(SkBlendMode::kSrc);
106 filter_paint.setShader(layer_filter);
107 layer->Paint(paint_context());
108 EXPECT_EQ(
109 mock_canvas().draw_calls(),
110 std::vector({MockCanvas::DrawCall{
111 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(),
112 nullptr, 1}},
113 MockCanvas::DrawCall{
114 1, MockCanvas::DrawPathData{child_path, child_paint}},
115 MockCanvas::DrawCall{
116 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate(
117 layer_bounds.fLeft, layer_bounds.fTop)}},
118 MockCanvas::DrawCall{
119 1, MockCanvas::DrawRectData{SkRect::MakeWH(
120 layer_bounds.width(),
121 layer_bounds.height()),
122 filter_paint}},
123 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
124}
125
126TEST_F(ShaderMaskLayerTest, MultipleChildren) {
127 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
128 const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
129 const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f);
130 const SkPath child_path1 = SkPath().addRect(child_bounds);
131 const SkPath child_path2 =
132 SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
133 const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
134 const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
135 auto layer_filter =
136 SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f);
137 auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
138 auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
139 auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
140 SkBlendMode::kSrc);
141 layer->Add(mock_layer1);
142 layer->Add(mock_layer2);
143
144 SkRect children_bounds = child_path1.getBounds();
145 children_bounds.join(child_path2.getBounds());
146 layer->Preroll(preroll_context(), initial_transform);
147 EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
148 EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
149 EXPECT_EQ(layer->paint_bounds(), children_bounds);
150 EXPECT_TRUE(mock_layer1->needs_painting());
151 EXPECT_TRUE(mock_layer2->needs_painting());
152 EXPECT_TRUE(layer->needs_painting());
153 EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
154 EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
155
156 SkPaint filter_paint;
157 filter_paint.setBlendMode(SkBlendMode::kSrc);
158 filter_paint.setShader(layer_filter);
159 layer->Paint(paint_context());
160 EXPECT_EQ(
161 mock_canvas().draw_calls(),
162 std::vector({MockCanvas::DrawCall{
163 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(),
164 nullptr, 1}},
165 MockCanvas::DrawCall{
166 1, MockCanvas::DrawPathData{child_path1, child_paint1}},
167 MockCanvas::DrawCall{
168 1, MockCanvas::DrawPathData{child_path2, child_paint2}},
169 MockCanvas::DrawCall{
170 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate(
171 layer_bounds.fLeft, layer_bounds.fTop)}},
172 MockCanvas::DrawCall{
173 1, MockCanvas::DrawRectData{SkRect::MakeWH(
174 layer_bounds.width(),
175 layer_bounds.height()),
176 filter_paint}},
177 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
178}
179
180TEST_F(ShaderMaskLayerTest, Nested) {
181 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f);
182 const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 7.5f, 8.5f);
183 const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f);
184 const SkPath child_path1 = SkPath().addRect(child_bounds);
185 const SkPath child_path2 =
186 SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f));
187 const SkPaint child_paint1 = SkPaint(SkColors::kYellow);
188 const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
189 auto layer_filter1 =
190 SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f);
191 auto layer_filter2 =
192 SkPerlinNoiseShader::MakeImprovedNoise(2.0f, 2.0f, 2, 2.0f);
193 auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
194 auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
195 auto layer1 = std::make_shared<ShaderMaskLayer>(layer_filter1, layer_bounds,
196 SkBlendMode::kSrc);
197 auto layer2 = std::make_shared<ShaderMaskLayer>(layer_filter2, layer_bounds,
198 SkBlendMode::kSrc);
199 layer2->Add(mock_layer2);
200 layer1->Add(mock_layer1);
201 layer1->Add(layer2);
202
203 SkRect children_bounds = child_path1.getBounds();
204 children_bounds.join(child_path2.getBounds());
205 layer1->Preroll(preroll_context(), initial_transform);
206 EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
207 EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
208 EXPECT_EQ(layer1->paint_bounds(), children_bounds);
209 EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds());
210 EXPECT_TRUE(mock_layer1->needs_painting());
211 EXPECT_TRUE(mock_layer2->needs_painting());
212 EXPECT_TRUE(layer1->needs_painting());
213 EXPECT_TRUE(layer2->needs_painting());
214 EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
215 EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
216
217 SkPaint filter_paint1, filter_paint2;
218 filter_paint1.setBlendMode(SkBlendMode::kSrc);
219 filter_paint2.setBlendMode(SkBlendMode::kSrc);
220 filter_paint1.setShader(layer_filter1);
221 filter_paint2.setShader(layer_filter2);
222 layer1->Paint(paint_context());
223 EXPECT_EQ(
224 mock_canvas().draw_calls(),
225 std::vector(
226 {MockCanvas::DrawCall{
227 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr,
228 1}},
229 MockCanvas::DrawCall{
230 1, MockCanvas::DrawPathData{child_path1, child_paint1}},
231 MockCanvas::DrawCall{
232 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(),
233 nullptr, 2}},
234 MockCanvas::DrawCall{
235 2, MockCanvas::DrawPathData{child_path2, child_paint2}},
236 MockCanvas::DrawCall{
237 2, MockCanvas::ConcatMatrixData{SkMatrix::Translate(
238 layer_bounds.fLeft, layer_bounds.fTop)}},
239 MockCanvas::DrawCall{
240 2,
241 MockCanvas::DrawRectData{
242 SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()),
243 filter_paint2}},
244 MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
245 MockCanvas::DrawCall{
246 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate(
247 layer_bounds.fLeft, layer_bounds.fTop)}},
248 MockCanvas::DrawCall{
249 1,
250 MockCanvas::DrawRectData{
251 SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()),
252 filter_paint1}},
253 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
254}
255
256TEST_F(ShaderMaskLayerTest, Readback) {
257 auto initial_transform = SkMatrix();
258 const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f);
259 auto layer_filter =
260 SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f);
261 auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
262 SkBlendMode::kSrc);
263
264 // ShaderMaskLayer does not read from surface
265 preroll_context()->surface_needs_readback = false;
266 layer->Preroll(preroll_context(), initial_transform);
267 EXPECT_FALSE(preroll_context()->surface_needs_readback);
268
269 // ShaderMaskLayer blocks child with readback
270 auto mock_layer =
271 std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
272 layer->Add(mock_layer);
273 preroll_context()->surface_needs_readback = false;
274 layer->Preroll(preroll_context(), initial_transform);
275 EXPECT_FALSE(preroll_context()->surface_needs_readback);
276}
277
278} // namespace testing
279} // namespace flutter
280