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
14namespace flutter {
15namespace testing {
16
17using ColorFilterLayerTest = LayerTest;
18
19#ifndef NDEBUG
20TEST_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
32TEST_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
45TEST_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
72TEST_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
101TEST_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
144TEST_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
198TEST_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