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