Flutter Impeller
aiks_dl_atlas_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 "display_list/dl_sampling_options.h"
6 #include "display_list/dl_types.h"
7 #include "display_list/effects/dl_color_filter.h"
8 #include "display_list/effects/image_filters/dl_matrix_image_filter.h"
9 #include "display_list/geometry/dl_geometry_types.h"
11 
12 #include "flutter/display_list/dl_blend_mode.h"
13 #include "flutter/display_list/dl_builder.h"
14 #include "flutter/display_list/dl_color.h"
15 #include "flutter/display_list/dl_paint.h"
16 #include "flutter/testing/testing.h"
17 #include "impeller/core/formats.h"
23 
24 namespace impeller {
25 namespace testing {
26 
27 using namespace flutter;
28 
29 namespace {
30 RSTransform MakeTranslation(Scalar tx, Scalar ty) {
31  return RSTransform::Make({tx, ty}, 1, DlDegrees(0));
32 }
33 
34 std::tuple<std::vector<DlRect>, //
35  std::vector<RSTransform>, //
36  sk_sp<DlImageImpeller>> CreateTestData(const AiksTest* test) {
37  // Draws the image as four squares stiched together.
38  auto atlas =
39  DlImageImpeller::Make(test->CreateTextureForFixture("bay_bridge.jpg"));
40  auto size = atlas->impeller_texture()->GetSize();
41  // Divide image into four quadrants.
42  Scalar half_width = size.width / 2;
43  Scalar half_height = size.height / 2;
44  std::vector<DlRect> texture_coordinates = {
45  DlRect::MakeLTRB(0, 0, half_width, half_height),
46  DlRect::MakeLTRB(half_width, 0, size.width, half_height),
47  DlRect::MakeLTRB(0, half_height, half_width, size.height),
48  DlRect::MakeLTRB(half_width, half_height, size.width, size.height)};
49  // Position quadrants adjacent to eachother.
50  std::vector<RSTransform> transforms = {
51  MakeTranslation(0, 0), MakeTranslation(half_width, 0),
52  MakeTranslation(0, half_height),
53  MakeTranslation(half_width, half_height)};
54  return std::make_tuple(texture_coordinates, transforms, atlas);
55 }
56 
57 } // namespace
58 
59 TEST_P(AiksTest, DrawAtlasNoColor) {
60  DisplayListBuilder builder;
61  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
62 
63  builder.Scale(GetContentScale().x, GetContentScale().y);
64  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
65  /*colors=*/nullptr, /*count=*/4, DlBlendMode::kSrcOver,
66  DlImageSampling::kNearestNeighbor, nullptr);
67 
68  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
69 }
70 
71 TEST_P(AiksTest, DrawAtlasWithColorAdvanced) {
72  DisplayListBuilder builder;
73  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
74 
75  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kGreen(),
76  DlColor::kBlue(), DlColor::kYellow()};
77 
78  builder.Scale(GetContentScale().x, GetContentScale().y);
79  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
80  colors.data(), /*count=*/4, DlBlendMode::kModulate,
81  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
82 
83  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
84 }
85 
86 TEST_P(AiksTest, DrawAtlasWithColorSimple) {
87  DisplayListBuilder builder;
88  // Draws the image as four squares stiched together.
89  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
90 
91  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kGreen(),
92  DlColor::kBlue(), DlColor::kYellow()};
93 
94  builder.Scale(GetContentScale().x, GetContentScale().y);
95  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
96  colors.data(), /*count=*/4, DlBlendMode::kSrcATop,
97  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
98 
99  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
100 }
101 
102 TEST_P(AiksTest, DrawAtlasWithOpacity) {
103  DisplayListBuilder builder;
104  // Draws the image as four squares stiched together slightly
105  // opaque
106  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
107 
108  DlPaint paint;
109  paint.setAlpha(128);
110  builder.Scale(GetContentScale().x, GetContentScale().y);
111  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
112  /*colors=*/nullptr, 4, DlBlendMode::kSrcOver,
113  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr,
114  &paint);
115 
116  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
117 }
118 
119 TEST_P(AiksTest, DrawAtlasNoColorFullSize) {
120  auto atlas = DlImageImpeller::Make(CreateTextureForFixture("bay_bridge.jpg"));
121  auto size = atlas->impeller_texture()->GetSize();
122  std::vector<DlRect> texture_coordinates = {
123  DlRect::MakeLTRB(0, 0, size.width, size.height)};
124  std::vector<RSTransform> transforms = {MakeTranslation(0, 0)};
125 
126  DisplayListBuilder builder;
127  builder.Scale(GetContentScale().x, GetContentScale().y);
128  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
129  /*colors=*/nullptr, /*count=*/1, DlBlendMode::kSrcOver,
130  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
131 
132  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
133 }
134 
135 // Regression test for https://github.com/flutter/flutter/issues/127374.
136 TEST_P(AiksTest, DrawAtlasAdvancedAndTransform) {
137  DisplayListBuilder builder;
138  // Draws the image as four squares stiched together.
139  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
140 
141  builder.Scale(0.25, 0.25);
142  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
143  /*colors=*/nullptr, /*count=*/4, DlBlendMode::kModulate,
144  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
145 
146  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
147 }
148 
149 // Regression test for https://github.com/flutter/flutter/issues/127374.
150 TEST_P(AiksTest, DrawAtlasWithColorAdvancedAndTransform) {
151  DisplayListBuilder builder;
152  // Draws the image as four squares stiched together.
153  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
154  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kGreen(),
155  DlColor::kBlue(), DlColor::kYellow()};
156 
157  builder.Scale(0.25, 0.25);
158  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
159  colors.data(), /*count=*/4, DlBlendMode::kModulate,
160  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
161 
162  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
163 }
164 
165 TEST_P(AiksTest, DrawAtlasPlusWideGamut) {
166  DisplayListBuilder builder;
167  EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
169 
170  // Draws the image as four squares stiched together.
171  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
172  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kGreen(),
173  DlColor::kBlue(), DlColor::kYellow()};
174 
175  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
176  colors.data(), /*count=*/4, DlBlendMode::kPlus,
177  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
178 
179  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
180 }
181 
182 TEST_P(AiksTest, DlAtlasGeometryNoBlendRenamed) {
183  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
184 
185  DlAtlasGeometry geom(atlas->impeller_texture(), transforms.data(),
186  texture_coordinates.data(), nullptr, transforms.size(),
187  BlendMode::kSrcOver, {}, std::nullopt);
188 
189  EXPECT_FALSE(geom.ShouldUseBlend());
190  EXPECT_FALSE(geom.ShouldSkip());
191 
192  ContentContext context(GetContext(), nullptr);
193  auto vertex_buffer =
195 
196  EXPECT_EQ(vertex_buffer.index_type, IndexType::kNone);
197  EXPECT_EQ(vertex_buffer.vertex_count, texture_coordinates.size() * 6);
198 }
199 
200 TEST_P(AiksTest, DlAtlasGeometryBlend) {
201  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
202 
203  std::vector<DlColor> colors;
204  colors.reserve(texture_coordinates.size());
205  for (auto i = 0u; i < texture_coordinates.size(); i++) {
206  colors.push_back(DlColor::ARGB(0.5, 1, 1, 1));
207  }
208  DlAtlasGeometry geom(
209  atlas->impeller_texture(), transforms.data(), texture_coordinates.data(),
210  colors.data(), transforms.size(), BlendMode::kSrcOver, {}, std::nullopt);
211 
212  EXPECT_TRUE(geom.ShouldUseBlend());
213  EXPECT_FALSE(geom.ShouldSkip());
214 
215  ContentContext context(GetContext(), nullptr);
216  auto vertex_buffer =
218 
219  EXPECT_EQ(vertex_buffer.index_type, IndexType::kNone);
220  EXPECT_EQ(vertex_buffer.vertex_count, texture_coordinates.size() * 6);
221 }
222 
223 TEST_P(AiksTest, DlAtlasGeometryColorButNoBlend) {
224  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
225 
226  std::vector<DlColor> colors;
227  colors.reserve(texture_coordinates.size());
228  for (auto i = 0u; i < texture_coordinates.size(); i++) {
229  colors.push_back(DlColor::ARGB(0.5, 1, 1, 1));
230  }
231  DlAtlasGeometry geom(atlas->impeller_texture(), transforms.data(),
232  texture_coordinates.data(), colors.data(),
233  transforms.size(), BlendMode::kSrc, {}, std::nullopt);
234 
235  // Src blend mode means that colors would be ignored, even if provided.
236  EXPECT_FALSE(geom.ShouldUseBlend());
237  EXPECT_FALSE(geom.ShouldSkip());
238 }
239 
240 TEST_P(AiksTest, DlAtlasGeometrySkip) {
241  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
242 
243  std::vector<DlColor> colors;
244  colors.reserve(texture_coordinates.size());
245  for (auto i = 0u; i < texture_coordinates.size(); i++) {
246  colors.push_back(DlColor::ARGB(0.5, 1, 1, 1));
247  }
248  DlAtlasGeometry geom(atlas->impeller_texture(), transforms.data(),
249  texture_coordinates.data(), colors.data(),
250  transforms.size(), BlendMode::kClear, {}, std::nullopt);
251  EXPECT_TRUE(geom.ShouldSkip());
252 }
253 
254 TEST_P(AiksTest, DrawImageRectWithBlendColorFilter) {
255  sk_sp<DlImageImpeller> texture =
256  DlImageImpeller::Make(CreateTextureForFixture("bay_bridge.jpg"));
257 
258  DisplayListBuilder builder;
259  DlPaint paint = DlPaint().setColorFilter(DlColorFilter::MakeBlend(
260  DlColor::kRed().withAlphaF(0.4), DlBlendMode::kSrcOver));
261 
262  DlMatrix filter_matrix = DlMatrix();
263  auto filter = flutter::DlMatrixImageFilter(filter_matrix,
264  flutter::DlImageSampling::kLinear);
265  DlPaint paint_with_filter = paint;
266  paint_with_filter.setImageFilter(&filter);
267 
268  // Compare porter-duff blend modes.
269  builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
270  // Uses image filter to disable atlas conversion.
271  builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()),
272  DlRect::MakeLTRB(0, 0, 500, 500), {},
273  &paint_with_filter);
274 
275  // Uses atlas conversion.
276  builder.Translate(600, 0);
277  builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()),
278  DlRect::MakeLTRB(0, 0, 500, 500), {}, &paint);
279 
280  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
281 }
282 
283 TEST_P(AiksTest, DrawImageRectWithMatrixColorFilter) {
284  sk_sp<DlImageImpeller> texture =
285  DlImageImpeller::Make(CreateTextureForFixture("bay_bridge.jpg"));
286 
287  DisplayListBuilder builder;
288  static const constexpr ColorMatrix kColorInversion = {
289  .array = {
290  -1.0, 0, 0, 1.0, 0, //
291  0, -1.0, 0, 1.0, 0, //
292  0, 0, -1.0, 1.0, 0, //
293  1.0, 1.0, 1.0, 1.0, 0 //
294  }};
295  DlPaint paint = DlPaint().setColorFilter(
296  DlColorFilter::MakeMatrix(kColorInversion.array));
297 
298  DlMatrix filter_matrix = DlMatrix();
299  auto filter = flutter::DlMatrixImageFilter(filter_matrix,
300  flutter::DlImageSampling::kLinear);
301  DlPaint paint_with_filter = paint;
302  paint_with_filter.setImageFilter(&filter);
303 
304  // Compare inverting color matrix filter.
305  builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
306  // Uses image filter to disable atlas conversion.
307  builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()),
308  DlRect::MakeLTRB(0, 0, 500, 500), {},
309  &paint_with_filter);
310 
311  // Uses atlas conversion.
312  builder.Translate(600, 0);
313  builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()),
314  DlRect::MakeLTRB(0, 0, 500, 500), {}, &paint);
315 
316  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
317 }
318 
319 TEST_P(AiksTest, DrawAtlasWithColorBurn) {
320  DisplayListBuilder builder;
321  auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
322 
323  std::vector<DlColor> colors = {DlColor::kDarkGrey(), DlColor::kBlack(),
324  DlColor::kLightGrey(), DlColor::kWhite()};
325 
326  builder.Scale(GetContentScale().x, GetContentScale().y);
327  builder.DrawAtlas(atlas, transforms.data(), texture_coordinates.data(),
328  colors.data(), /*count=*/4, DlBlendMode::kColorBurn,
329  DlImageSampling::kNearestNeighbor, /*cullRect=*/nullptr);
330 
331  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
332 }
333 
334 } // namespace testing
335 } // namespace impeller
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
A wrapper around data provided by a drawAtlas call.
VertexBuffer CreateBlendVertexBuffer(HostBuffer &host_buffer) const override
bool ShouldUseBlend() const override
Whether the blend shader should be used.
bool ShouldSkip() const override
VertexBuffer CreateSimpleVertexBuffer(HostBuffer &host_buffer) const override
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
int32_t x
AiksPlayground AiksTest
TEST_P(AiksTest, DrawAtlasNoColor)
float Scalar
Definition: scalar.h:19
@ kNone
Does not use the index buffer.
static constexpr const ColorMatrix kColorInversion
A color matrix which inverts colors.
Definition: color_filter.h:16
Scalar array[20]
Definition: color.h:118
static RSTransform Make(Point origin, Scalar scale, Radians radians)
Definition: rstransform.h:38