| Flutter Impeller |

entity_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 <cstring>
6 #include <memory>
7 #include <optional>
8 #include <utility>
9 #include <vector>
10
11 #include "flutter/display_list/testing/dl_test_snippets.h"
12 #include "fml/logging.h"
13 #include "gtest/gtest.h"
14 #include "impeller/core/formats.h"
15 #include "impeller/core/texture_descriptor.h"
16 #include "impeller/entity/contents/atlas_contents.h"
17 #include "impeller/entity/contents/clip_contents.h"
18 #include "impeller/entity/contents/conical_gradient_contents.h"
19 #include "impeller/entity/contents/content_context.h"
20 #include "impeller/entity/contents/contents.h"
21 #include "impeller/entity/contents/filters/color_filter_contents.h"
22 #include "impeller/entity/contents/filters/filter_contents.h"
23 #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
24 #include "impeller/entity/contents/filters/inputs/filter_input.h"
25 #include "impeller/entity/contents/linear_gradient_contents.h"
26 #include "impeller/entity/contents/radial_gradient_contents.h"
27 #include "impeller/entity/contents/runtime_effect_contents.h"
28 #include "impeller/entity/contents/solid_color_contents.h"
29 #include "impeller/entity/contents/solid_rrect_blur_contents.h"
30 #include "impeller/entity/contents/text_contents.h"
31 #include "impeller/entity/contents/texture_contents.h"
32 #include "impeller/entity/contents/tiled_texture_contents.h"
33 #include "impeller/entity/entity.h"
34 #include "impeller/entity/entity_pass.h"
35 #include "impeller/entity/entity_pass_delegate.h"
36 #include "impeller/entity/entity_playground.h"
37 #include "impeller/entity/geometry/geometry.h"
38 #include "impeller/entity/geometry/point_field_geometry.h"
39 #include "impeller/entity/geometry/stroke_path_geometry.h"
40 #include "impeller/geometry/color.h"
41 #include "impeller/geometry/geometry_asserts.h"
42 #include "impeller/geometry/path_builder.h"
43 #include "impeller/geometry/point.h"
44 #include "impeller/geometry/sigma.h"
45 #include "impeller/geometry/vector.h"
46 #include "impeller/playground/playground.h"
47 #include "impeller/playground/widgets.h"
48 #include "impeller/renderer/command.h"
49 #include "impeller/renderer/pipeline_descriptor.h"
50 #include "impeller/renderer/render_pass.h"
51 #include "impeller/renderer/vertex_buffer_builder.h"
52 #include "impeller/typographer/backends/skia/text_frame_skia.h"
53 #include "impeller/typographer/backends/skia/typographer_context_skia.h"
54 #include "third_party/imgui/imgui.h"
55 #include "third_party/skia/include/core/SkTextBlob.h"
56
57 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
58 // NOLINTBEGIN(bugprone-unchecked-optional-access)
59
60 namespace impeller {
61 namespace testing {
62
63 using EntityTest = EntityPlayground;
64 INSTANTIATE_PLAYGROUND_SUITE(EntityTest);
65
66 TEST_P(EntityTest, CanCreateEntity) {
67 Entity entity;
68 ASSERT_TRUE(entity.GetTransform().IsIdentity());
69 }
70
71 class TestPassDelegate final : public EntityPassDelegate {
72 public:
73 explicit TestPassDelegate(bool collapse = false) : collapse_(collapse) {}
74
75 // |EntityPassDelegate|
76 ~TestPassDelegate() override = default;
77
78 // |EntityPassDelgate|
79 bool CanElide() override { return false; }
80
81 // |EntityPassDelgate|
82 bool CanCollapseIntoParentPass(EntityPass* entity_pass) override {
83 return collapse_;
84 }
85
86 // |EntityPassDelgate|
87 std::shared_ptr<Contents> CreateContentsForSubpassTarget(
88 std::shared_ptr<Texture> target,
89 const Matrix& transform) override {
90 return nullptr;
91 }
92
93 // |EntityPassDelegate|
94 std::shared_ptr<FilterContents> WithImageFilter(
95 const FilterInput::Variant& input,
96 const Matrix& effect_transform) const override {
97 return nullptr;
98 }
99
100 private:
101 const std::optional<Rect> coverage_;
102 const bool collapse_;
103 };
104
105 auto CreatePassWithRectPath(Rect rect,
106 std::optional<Rect> bounds_hint,
107 bool collapse = false) {
108 auto subpass = std::make_unique<EntityPass>();
109 Entity entity;
110 entity.SetContents(SolidColorContents::Make(
111 PathBuilder{}.AddRect(rect).TakePath(), Color::Red()));
112 subpass->AddEntity(std::move(entity));
113 subpass->SetDelegate(std::make_unique<TestPassDelegate>(collapse));
114 subpass->SetBoundsLimit(bounds_hint);
115 return subpass;
116 }
117
118 TEST_P(EntityTest, EntityPassRespectsSubpassBoundsLimit) {
119 EntityPass pass;
120
121 auto subpass0 = CreatePassWithRectPath(Rect::MakeLTRB(0, 0, 100, 100),
122 Rect::MakeLTRB(50, 50, 150, 150));
123 auto subpass1 = CreatePassWithRectPath(Rect::MakeLTRB(500, 500, 1000, 1000),
124 Rect::MakeLTRB(800, 800, 900, 900));
125
126 auto subpass0_coverage =
127 pass.GetSubpassCoverage(*subpass0.get(), std::nullopt);
128 ASSERT_TRUE(subpass0_coverage.has_value());
129 ASSERT_RECT_NEAR(subpass0_coverage.value(), Rect::MakeLTRB(50, 50, 100, 100));
130
131 auto subpass1_coverage =
132 pass.GetSubpassCoverage(*subpass1.get(), std::nullopt);
133 ASSERT_TRUE(subpass1_coverage.has_value());
134 ASSERT_RECT_NEAR(subpass1_coverage.value(),
135 Rect::MakeLTRB(800, 800, 900, 900));
136
137 pass.AddSubpass(std::move(subpass0));
138 pass.AddSubpass(std::move(subpass1));
139
140 auto coverage = pass.GetElementsCoverage(std::nullopt);
141 ASSERT_TRUE(coverage.has_value());
142 ASSERT_RECT_NEAR(coverage.value(), Rect::MakeLTRB(50, 50, 900, 900));
143 }
144
145 TEST_P(EntityTest, EntityPassCanMergeSubpassIntoParent) {
146 // Both a red and a blue box should appear if the pass merging has worked
147 // correctly.
148
149 EntityPass pass;
150 auto subpass = CreatePassWithRectPath(Rect::MakeLTRB(0, 0, 100, 100),
151 Rect::MakeLTRB(50, 50, 150, 150), true);
152 pass.AddSubpass(std::move(subpass));
153
154 Entity entity;
155 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
156 auto contents = std::make_unique<SolidColorContents>();
157 contents->SetGeometry(Geometry::MakeRect(Rect::MakeLTRB(100, 100, 200, 200)));
158 contents->SetColor(Color::Blue());
159 entity.SetContents(std::move(contents));
160
161 pass.AddEntity(std::move(entity));
162
163 ASSERT_TRUE(OpenPlaygroundHere(pass));
164 }
165
166 TEST_P(EntityTest, EntityPassCoverageRespectsCoverageLimit) {
167 // Rect is drawn entirely in negative area.
168 auto pass = CreatePassWithRectPath(Rect::MakeLTRB(-200, -200, -100, -100),
169 std::nullopt);
170
171 // Without coverage limit.
172 {
173 auto pass_coverage = pass->GetElementsCoverage(std::nullopt);
174 ASSERT_TRUE(pass_coverage.has_value());
175 ASSERT_RECT_NEAR(pass_coverage.value(),
176 Rect::MakeLTRB(-200, -200, -100, -100));
177 }
178
179 // With limit that doesn't overlap.
180 {
181 auto pass_coverage =
182 pass->GetElementsCoverage(Rect::MakeLTRB(0, 0, 100, 100));
183 ASSERT_FALSE(pass_coverage.has_value());
184 }
185
186 // With limit that partially overlaps.
187 {
188 auto pass_coverage =
189 pass->GetElementsCoverage(Rect::MakeLTRB(-150, -150, 0, 0));
190 ASSERT_TRUE(pass_coverage.has_value());
191 ASSERT_RECT_NEAR(pass_coverage.value(),
192 Rect::MakeLTRB(-150, -150, -100, -100));
193 }
194 }
195
196 TEST_P(EntityTest, FilterCoverageRespectsCropRect) {
197 auto image = CreateTextureForFixture("boston.jpg");
198 auto filter = ColorFilterContents::MakeBlend(BlendMode::kSoftLight,
199 FilterInput::Make({image}));
200
201 // Without the crop rect (default behavior).
202 {
203 auto actual = filter->GetCoverage({});
204 auto expected = Rect::MakeSize(image->GetSize());
205
206 ASSERT_TRUE(actual.has_value());
207 ASSERT_RECT_NEAR(actual.value(), expected);
208 }
209
210 // With the crop rect.
211 {
212 auto expected = Rect::MakeLTRB(50, 50, 100, 100);
213 filter->SetCoverageHint(expected);
214 auto actual = filter->GetCoverage({});
215
216 ASSERT_TRUE(actual.has_value());
217 ASSERT_RECT_NEAR(actual.value(), expected);
218 }
219 }
220
221 TEST_P(EntityTest, CanDrawRect) {
222 auto contents = std::make_shared<SolidColorContents>();
223 contents->SetGeometry(Geometry::MakeRect(Rect::MakeXYWH(100, 100, 100, 100)));
224 contents->SetColor(Color::Red());
225
226 Entity entity;
227 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
228 entity.SetContents(contents);
229
230 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
231 }
232
233 TEST_P(EntityTest, CanDrawRRect) {
234 auto contents = std::make_shared<SolidColorContents>();
235 auto path = PathBuilder{}
236 .SetConvexity(Convexity::kConvex)
237 .AddRoundedRect(Rect::MakeXYWH(100, 100, 100, 100), 10.0)
238 .TakePath();
239 contents->SetGeometry(Geometry::MakeFillPath(std::move(path)));
240 contents->SetColor(Color::Red());
241
242 Entity entity;
243 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
244 entity.SetContents(contents);
245
246 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
247 }
248
249 TEST_P(EntityTest, GeometryBoundsAreTransformed) {
250 auto geometry = Geometry::MakeRect(Rect::MakeXYWH(100, 100, 100, 100));
251 auto transform = Matrix::MakeScale({2.0, 2.0, 2.0});
252
253 ASSERT_RECT_NEAR(geometry->GetCoverage(transform).value(),
254 Rect::MakeXYWH(200, 200, 200, 200));
255 }
256
257 TEST_P(EntityTest, ThreeStrokesInOnePath) {
258 Path path = PathBuilder{}
259 .MoveTo({100, 100})
260 .LineTo({100, 200})
261 .MoveTo({100, 300})
262 .LineTo({100, 400})
263 .MoveTo({100, 500})
264 .LineTo({100, 600})
265 .TakePath();
266
267 Entity entity;
268 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
269 auto contents = std::make_unique<SolidColorContents>();
270 contents->SetGeometry(Geometry::MakeStrokePath(std::move(path), 5.0));
271 contents->SetColor(Color::Red());
272 entity.SetContents(std::move(contents));
273 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
274 }
275
276 TEST_P(EntityTest, StrokeWithTextureContents) {
277 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
278 Path path = PathBuilder{}
279 .MoveTo({100, 100})
280 .LineTo({100, 200})
281 .MoveTo({100, 300})
282 .LineTo({100, 400})
283 .MoveTo({100, 500})
284 .LineTo({100, 600})
285 .TakePath();
286
287 Entity entity;
288 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
289 auto contents = std::make_unique<TiledTextureContents>();
290 contents->SetGeometry(Geometry::MakeStrokePath(std::move(path), 100.0));
291 contents->SetTexture(bridge);
292 contents->SetTileModes(Entity::TileMode::kClamp, Entity::TileMode::kClamp);
293 entity.SetContents(std::move(contents));
294 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
295 }
296
297 TEST_P(EntityTest, TriangleInsideASquare) {
298 auto callback = [&](ContentContext& context, RenderPass& pass) {
299 Point offset(100, 100);
300
301 Point a =
302 IMPELLER_PLAYGROUND_POINT(Point(10, 10) + offset, 20, Color::White());
303 Point b =
304 IMPELLER_PLAYGROUND_POINT(Point(210, 10) + offset, 20, Color::White());
305 Point c =
306 IMPELLER_PLAYGROUND_POINT(Point(210, 210) + offset, 20, Color::White());
307 Point d =
308 IMPELLER_PLAYGROUND_POINT(Point(10, 210) + offset, 20, Color::White());
309 Point e =
310 IMPELLER_PLAYGROUND_POINT(Point(50, 50) + offset, 20, Color::White());
311 Point f =
312 IMPELLER_PLAYGROUND_POINT(Point(100, 50) + offset, 20, Color::White());
313 Point g =
314 IMPELLER_PLAYGROUND_POINT(Point(50, 150) + offset, 20, Color::White());
315 Path path = PathBuilder{}
316 .MoveTo(a)
317 .LineTo(b)
318 .LineTo(c)
319 .LineTo(d)
320 .Close()
321 .MoveTo(e)
322 .LineTo(f)
323 .LineTo(g)
324 .Close()
325 .TakePath();
326
327 Entity entity;
328 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
329 auto contents = std::make_unique<SolidColorContents>();
330 contents->SetGeometry(Geometry::MakeStrokePath(std::move(path), 20.0));
331 contents->SetColor(Color::Red());
332 entity.SetContents(std::move(contents));
333
334 return entity.Render(context, pass);
335 };
336 ASSERT_TRUE(OpenPlaygroundHere(callback));
337 }
338
339 TEST_P(EntityTest, StrokeCapAndJoinTest) {
340 const Point padding(300, 250);
341 const Point margin(140, 180);
342
343 auto callback = [&](ContentContext& context, RenderPass& pass) {
344 // Slightly above sqrt(2) by default, so that right angles are just below
345 // the limit and acute angles are over the limit (causing them to get
346 // beveled).
347 static Scalar miter_limit = 1.41421357;
348 static Scalar width = 30;
349
350 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
351 {
352 ImGui::SliderFloat("Miter limit", &miter_limit, 0, 30);
353 ImGui::SliderFloat("Stroke width", &width, 0, 100);
354 if (ImGui::Button("Reset")) {
355 miter_limit = 1.41421357;
356 width = 30;
357 }
358 }
359 ImGui::End();
360
361 auto world_matrix = Matrix::MakeScale(GetContentScale());
362 auto render_path = [width = width, &context, &pass, &world_matrix](
363 const Path& path, Cap cap, Join join) {
364 auto contents = std::make_unique<SolidColorContents>();
365 contents->SetGeometry(Geometry::MakeStrokePath(path.Clone(), width,
366 miter_limit, cap, join));
367 contents->SetColor(Color::Red());
368
369 Entity entity;
370 entity.SetTransform(world_matrix);
371 entity.SetContents(std::move(contents));
372
373 auto coverage = entity.GetCoverage();
374 if (coverage.has_value()) {
375 auto bounds_contents = std::make_unique<SolidColorContents>();
376 bounds_contents->SetGeometry(Geometry::MakeFillPath(
377 PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()));
378 bounds_contents->SetColor(Color::Green().WithAlpha(0.5));
379 Entity bounds_entity;
380 bounds_entity.SetContents(std::move(bounds_contents));
381 bounds_entity.Render(context, pass);
382 }
383
384 entity.Render(context, pass);
385 };
386
387 const Point a_def(0, 0), b_def(0, 100), c_def(150, 0), d_def(150, -100),
388 e_def(75, 75);
389 const Scalar r = 30;
390 // Cap::kButt demo.
391 {
392 Point off = Point(0, 0) * padding + margin;
393 auto [a, b] = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r,
394 Color::Black(), Color::White());
395 auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r,
396 Color::Black(), Color::White());
397 render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(),
398 Cap::kButt, Join::kBevel);
399 }
400
401 // Cap::kSquare demo.
402 {
403 Point off = Point(1, 0) * padding + margin;
404 auto [a, b] = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r,
405 Color::Black(), Color::White());
406 auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r,
407 Color::Black(), Color::White());
408 render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(),
409 Cap::kSquare, Join::kBevel);
410 }
411
412 // Cap::kRound demo.
413 {
414 Point off = Point(2, 0) * padding + margin;
415 auto [a, b] = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r,
416 Color::Black(), Color::White());
417 auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r,
418 Color::Black(), Color::White());
419 render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(),
420 Cap::kRound, Join::kBevel);
421 }
422
423 // Join::kBevel demo.
424 {
425 Point off = Point(0, 1) * padding + margin;
426 Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White());
427 Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White());
428 Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White());
429 render_path(
430 PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(),
431 Cap::kButt, Join::kBevel);
432 }
433
434 // Join::kMiter demo.
435 {
436 Point off = Point(1, 1) * padding + margin;
437 Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White());
438 Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White());
439 Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White());
440 render_path(
441 PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(),
442 Cap::kButt, Join::kMiter);
443 }
444
445 // Join::kRound demo.
446 {
447 Point off = Point(2, 1) * padding + margin;
448 Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White());
449 Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White());
450 Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White());
451 render_path(
452 PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(),
453 Cap::kButt, Join::kRound);
454 }
455
456 return true;
457 };
458 ASSERT_TRUE(OpenPlaygroundHere(callback));
459 }
460
461 TEST_P(EntityTest, CubicCurveTest) {
462 // Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3
463 Path path =
464 PathBuilder{}
465 .MoveTo({237.164, 125.003})
466 .CubicCurveTo({236.709, 125.184}, {236.262, 125.358},
467 {235.81, 125.538})
468 .CubicCurveTo({235.413, 125.68}, {234.994, 125.832},
469 {234.592, 125.977})
470 .CubicCurveTo({234.592, 125.977}, {234.591, 125.977},
471 {234.59, 125.977})
472 .CubicCurveTo({222.206, 130.435}, {207.708, 135.753},
473 {192.381, 141.429})
474 .CubicCurveTo({162.77, 151.336}, {122.17, 156.894}, {84.1123, 160})
475 .Close()
476 .TakePath();
477 Entity entity;
478 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
479 entity.SetContents(SolidColorContents::Make(std::move(path), Color::Red()));
480 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
481 }
482
483 TEST_P(EntityTest, CanDrawCorrectlyWithRotatedTransform) {
484 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
485 const char* input_axis[] = {"X", "Y", "Z"};
486 static int rotation_axis_index = 0;
487 static float rotation = 0;
488 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
489 ImGui::SliderFloat("Rotation", &rotation, -kPi, kPi);
490 ImGui::Combo("Rotation Axis", &rotation_axis_index, input_axis,
491 sizeof(input_axis) / sizeof(char*));
492 Matrix rotation_matrix;
493 switch (rotation_axis_index) {
494 case 0:
495 rotation_matrix = Matrix::MakeRotationX(Radians(rotation));
496 break;
497 case 1:
498 rotation_matrix = Matrix::MakeRotationY(Radians(rotation));
499 break;
500 case 2:
501 rotation_matrix = Matrix::MakeRotationZ(Radians(rotation));
502 break;
503 default:
504 rotation_matrix = Matrix{};
505 break;
506 }
507
508 if (ImGui::Button("Reset")) {
509 rotation = 0;
510 }
511 ImGui::End();
512 Matrix current_transform =
513 Matrix::MakeScale(GetContentScale())
514 .MakeTranslation(
515 Vector3(Point(pass.GetRenderTargetSize().width / 2.0,
516 pass.GetRenderTargetSize().height / 2.0)));
517 Matrix result_transform = current_transform * rotation_matrix;
518 Path path =
519 PathBuilder{}.AddRect(Rect::MakeXYWH(-300, -400, 600, 800)).TakePath();
520
521 Entity entity;
522 entity.SetTransform(result_transform);
523 entity.SetContents(SolidColorContents::Make(std::move(path), Color::Red()));
524 return entity.Render(context, pass);
525 };
526 ASSERT_TRUE(OpenPlaygroundHere(callback));
527 }
528
529 TEST_P(EntityTest, CubicCurveAndOverlapTest) {
530 // Compare with https://fiddle.skia.org/c/7a05a3e186c65a8dfb732f68020aae06
531 Path path =
532 PathBuilder{}
533 .MoveTo({359.934, 96.6335})
534 .CubicCurveTo({358.189, 96.7055}, {356.436, 96.7908},
535 {354.673, 96.8895})
536 .CubicCurveTo({354.571, 96.8953}, {354.469, 96.9016},
537 {354.367, 96.9075})
538 .CubicCurveTo({352.672, 97.0038}, {350.969, 97.113},
539 {349.259, 97.2355})
540 .CubicCurveTo({349.048, 97.2506}, {348.836, 97.2678},
541 {348.625, 97.2834})
542 .CubicCurveTo({347.019, 97.4014}, {345.407, 97.5299},
543 {343.789, 97.6722})
544 .CubicCurveTo({343.428, 97.704}, {343.065, 97.7402},
545 {342.703, 97.7734})
546 .CubicCurveTo({341.221, 97.9086}, {339.736, 98.0505},
547 {338.246, 98.207})
548 .CubicCurveTo({337.702, 98.2642}, {337.156, 98.3292},
549 {336.612, 98.3894})
550 .CubicCurveTo({335.284, 98.5356}, {333.956, 98.6837},
551 {332.623, 98.8476})
552 .CubicCurveTo({332.495, 98.8635}, {332.366, 98.8818},
553 {332.237, 98.8982})
554 .LineTo({332.237, 102.601})
555 .LineTo({321.778, 102.601})
556 .LineTo({321.778, 100.382})
557 .CubicCurveTo({321.572, 100.413}, {321.367, 100.442},
558 {321.161, 100.476})
559 .CubicCurveTo({319.22, 100.79}, {317.277, 101.123},
560 {315.332, 101.479})
561 .CubicCurveTo({315.322, 101.481}, {315.311, 101.482},
562 {315.301, 101.484})
563 .LineTo({310.017, 105.94})
564 .LineTo({309.779, 105.427})
565 .LineTo({314.403, 101.651})
566 .CubicCurveTo({314.391, 101.653}, {314.379, 101.656},
567 {314.368, 101.658})
568 .CubicCurveTo({312.528, 102.001}, {310.687, 102.366},
569 {308.846, 102.748})
570 .CubicCurveTo({307.85, 102.955}, {306.855, 103.182}, {305.859, 103.4})
571 .CubicCurveTo({305.048, 103.579}, {304.236, 103.75},
572 {303.425, 103.936})
573 .LineTo({299.105, 107.578})
574 .LineTo({298.867, 107.065})
575 .LineTo({302.394, 104.185})
576 .LineTo({302.412, 104.171})
577 .CubicCurveTo({301.388, 104.409}, {300.366, 104.67},
578 {299.344, 104.921})
579 .CubicCurveTo({298.618, 105.1}, {297.89, 105.269}, {297.165, 105.455})
580 .CubicCurveTo({295.262, 105.94}, {293.36, 106.445},
581 {291.462, 106.979})
582 .CubicCurveTo({291.132, 107.072}, {290.802, 107.163},
583 {290.471, 107.257})
584 .CubicCurveTo({289.463, 107.544}, {288.455, 107.839},
585 {287.449, 108.139})
586 .CubicCurveTo({286.476, 108.431}, {285.506, 108.73},
587 {284.536, 109.035})
588 .CubicCurveTo({283.674, 109.304}, {282.812, 109.579},
589 {281.952, 109.859})
590 .CubicCurveTo({281.177, 110.112}, {280.406, 110.377},
591 {279.633, 110.638})
592 .CubicCurveTo({278.458, 111.037}, {277.256, 111.449},
593 {276.803, 111.607})
594 .CubicCurveTo({276.76, 111.622}, {276.716, 111.637},
595 {276.672, 111.653})
596 .CubicCurveTo({275.017, 112.239}, {273.365, 112.836},
597 {271.721, 113.463})
598 .LineTo({271.717, 113.449})
599 .CubicCurveTo({271.496, 113.496}, {271.238, 113.559},
600 {270.963, 113.628})
601 .CubicCurveTo({270.893, 113.645}, {270.822, 113.663},
602 {270.748, 113.682})
603 .CubicCurveTo({270.468, 113.755}, {270.169, 113.834},
604 {269.839, 113.926})
605 .CubicCurveTo({269.789, 113.94}, {269.732, 113.957},
606 {269.681, 113.972})
607 .CubicCurveTo({269.391, 114.053}, {269.081, 114.143},
608 {268.756, 114.239})
609 .CubicCurveTo({268.628, 114.276}, {268.5, 114.314},
610 {268.367, 114.354})
611 .CubicCurveTo({268.172, 114.412}, {267.959, 114.478},
612 {267.752, 114.54})
613 .CubicCurveTo({263.349, 115.964}, {258.058, 117.695},
614 {253.564, 119.252})
615 .CubicCurveTo({253.556, 119.255}, {253.547, 119.258},
616 {253.538, 119.261})
617 .CubicCurveTo({251.844, 119.849}, {250.056, 120.474},
618 {248.189, 121.131})
619 .CubicCurveTo({248, 121.197}, {247.812, 121.264}, {247.621, 121.331})
620 .CubicCurveTo({247.079, 121.522}, {246.531, 121.715},
621 {245.975, 121.912})
622 .CubicCurveTo({245.554, 122.06}, {245.126, 122.212},
623 {244.698, 122.364})
624 .CubicCurveTo({244.071, 122.586}, {243.437, 122.811},
625 {242.794, 123.04})
626 .CubicCurveTo({242.189, 123.255}, {241.58, 123.472},
627 {240.961, 123.693})
628 .CubicCurveTo({240.659, 123.801}, {240.357, 123.909},
629 {240.052, 124.018})
630 .CubicCurveTo({239.12, 124.351}, {238.18, 124.687}, {237.22, 125.032})
631 .LineTo({237.164, 125.003})
632 .CubicCurveTo({236.709, 125.184}, {236.262, 125.358},
633 {235.81, 125.538})
634 .CubicCurveTo({235.413, 125.68}, {234.994, 125.832},
635 {234.592, 125.977})
636 .CubicCurveTo({234.592, 125.977}, {234.591, 125.977},
637 {234.59, 125.977})
638 .CubicCurveTo({222.206, 130.435}, {207.708, 135.753},
639 {192.381, 141.429})
640 .CubicCurveTo({162.77, 151.336}, {122.17, 156.894}, {84.1123, 160})
641 .LineTo({360, 160})
642 .LineTo({360, 119.256})
643 .LineTo({360, 106.332})
644 .LineTo({360, 96.6307})
645 .CubicCurveTo({359.978, 96.6317}, {359.956, 96.6326},
646 {359.934, 96.6335})
647 .Close()
648 .MoveTo({337.336, 124.143})
649 .CubicCurveTo({337.274, 122.359}, {338.903, 121.511},
650 {338.903, 121.511})
651 .CubicCurveTo({338.903, 121.511}, {338.96, 123.303},
652 {337.336, 124.143})
653 .Close()
654 .MoveTo({340.082, 121.849})
655 .CubicCurveTo({340.074, 121.917}, {340.062, 121.992},
656 {340.046, 122.075})
657 .CubicCurveTo({340.039, 122.109}, {340.031, 122.142},
658 {340.023, 122.177})
659 .CubicCurveTo({340.005, 122.26}, {339.98, 122.346},
660 {339.952, 122.437})
661 .CubicCurveTo({339.941, 122.473}, {339.931, 122.507},
662 {339.918, 122.544})
663 .CubicCurveTo({339.873, 122.672}, {339.819, 122.804},
664 {339.75, 122.938})
665 .CubicCurveTo({339.747, 122.944}, {339.743, 122.949},
666 {339.74, 122.955})
667 .CubicCurveTo({339.674, 123.08}, {339.593, 123.205},
668 {339.501, 123.328})
669 .CubicCurveTo({339.473, 123.366}, {339.441, 123.401},
670 {339.41, 123.438})
671 .CubicCurveTo({339.332, 123.534}, {339.243, 123.625},
672 {339.145, 123.714})
673 .CubicCurveTo({339.105, 123.75}, {339.068, 123.786},
674 {339.025, 123.821})
675 .CubicCurveTo({338.881, 123.937}, {338.724, 124.048},
676 {338.539, 124.143})
677 .CubicCurveTo({338.532, 123.959}, {338.554, 123.79},
678 {338.58, 123.626})
679 .CubicCurveTo({338.58, 123.625}, {338.58, 123.625}, {338.58, 123.625})
680 .CubicCurveTo({338.607, 123.455}, {338.65, 123.299},
681 {338.704, 123.151})
682 .CubicCurveTo({338.708, 123.14}, {338.71, 123.127},
683 {338.714, 123.117})
684 .CubicCurveTo({338.769, 122.971}, {338.833, 122.838},
685 {338.905, 122.712})
686 .CubicCurveTo({338.911, 122.702}, {338.916, 122.69200000000001},
687 {338.922, 122.682})
688 .CubicCurveTo({338.996, 122.557}, {339.072, 122.444},
689 {339.155, 122.34})
690 .CubicCurveTo({339.161, 122.333}, {339.166, 122.326},
691 {339.172, 122.319})
692 .CubicCurveTo({339.256, 122.215}, {339.339, 122.12},
693 {339.425, 122.037})
694 .CubicCurveTo({339.428, 122.033}, {339.431, 122.03},
695 {339.435, 122.027})
696 .CubicCurveTo({339.785, 121.687}, {340.106, 121.511},
697 {340.106, 121.511})
698 .CubicCurveTo({340.106, 121.511}, {340.107, 121.645},
699 {340.082, 121.849})
700 .Close()
701 .MoveTo({340.678, 113.245})
702 .CubicCurveTo({340.594, 113.488}, {340.356, 113.655},
703 {340.135, 113.775})
704 .CubicCurveTo({339.817, 113.948}, {339.465, 114.059},
705 {339.115, 114.151})
706 .CubicCurveTo({338.251, 114.379}, {337.34, 114.516},
707 {336.448, 114.516})
708 .CubicCurveTo({335.761, 114.516}, {335.072, 114.527},
709 {334.384, 114.513})
710 .CubicCurveTo({334.125, 114.508}, {333.862, 114.462},
711 {333.605, 114.424})
712 .CubicCurveTo({332.865, 114.318}, {332.096, 114.184},
713 {331.41, 113.883})
714 .CubicCurveTo({330.979, 113.695}, {330.442, 113.34},
715 {330.672, 112.813})
716 .CubicCurveTo({331.135, 111.755}, {333.219, 112.946},
717 {334.526, 113.833})
718 .CubicCurveTo({334.54, 113.816}, {334.554, 113.8}, {334.569, 113.784})
719 .CubicCurveTo({333.38, 112.708}, {331.749, 110.985},
720 {332.76, 110.402})
721 .CubicCurveTo({333.769, 109.82}, {334.713, 111.93},
722 {335.228, 113.395})
723 .CubicCurveTo({334.915, 111.889}, {334.59, 109.636},
724 {335.661, 109.592})
725 .CubicCurveTo({336.733, 109.636}, {336.408, 111.889},
726 {336.07, 113.389})
727 .CubicCurveTo({336.609, 111.93}, {337.553, 109.82},
728 {338.563, 110.402})
729 .CubicCurveTo({339.574, 110.984}, {337.942, 112.708},
730 {336.753, 113.784})
731 .CubicCurveTo({336.768, 113.8}, {336.782, 113.816},
732 {336.796, 113.833})
733 .CubicCurveTo({338.104, 112.946}, {340.187, 111.755},
734 {340.65, 112.813})
735 .CubicCurveTo({340.71, 112.95}, {340.728, 113.102},
736 {340.678, 113.245})
737 .Close()
738 .MoveTo({346.357, 106.771})
739 .CubicCurveTo({346.295, 104.987}, {347.924, 104.139},
740 {347.924, 104.139})
741 .CubicCurveTo({347.924, 104.139}, {347.982, 105.931},
742 {346.357, 106.771})
743 .Close()
744 .MoveTo({347.56, 106.771})
745 .CubicCurveTo({347.498, 104.987}, {349.127, 104.139},
746 {349.127, 104.139})
747 .CubicCurveTo({349.127, 104.139}, {349.185, 105.931},
748 {347.56, 106.771})
749 .Close()
750 .TakePath();
751 Entity entity;
752 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
753 entity.SetContents(SolidColorContents::Make(std::move(path), Color::Red()));
754 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
755 }
756
757 TEST_P(EntityTest, SolidColorContentsStrokeSetStrokeCapsAndJoins) {
758 {
759 auto geometry = Geometry::MakeStrokePath(Path{});
760 auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
761 // Defaults.
762 ASSERT_EQ(path_geometry->GetStrokeCap(), Cap::kButt);
763 ASSERT_EQ(path_geometry->GetStrokeJoin(), Join::kMiter);
764 }
765
766 {
767 auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, 4.0, Cap::kSquare);
768 auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
769 ASSERT_EQ(path_geometry->GetStrokeCap(), Cap::kSquare);
770 }
771
772 {
773 auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, 4.0, Cap::kRound);
774 auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
775 ASSERT_EQ(path_geometry->GetStrokeCap(), Cap::kRound);
776 }
777 }
778
779 TEST_P(EntityTest, SolidColorContentsStrokeSetMiterLimit) {
780 {
781 auto geometry = Geometry::MakeStrokePath(Path{});
782 auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
783 ASSERT_FLOAT_EQ(path_geometry->GetMiterLimit(), 4);
784 }
785
786 {
787 auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, /*miter_limit=*/8.0);
788 auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
789 ASSERT_FLOAT_EQ(path_geometry->GetMiterLimit(), 8);
790 }
791
792 {
793 auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, /*miter_limit=*/-1.0);
794 auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
795 ASSERT_FLOAT_EQ(path_geometry->GetMiterLimit(), 4);
796 }
797 }
798
799 TEST_P(EntityTest, BlendingModeOptions) {
800 std::vector<const char*> blend_mode_names;
801 std::vector<BlendMode> blend_mode_values;
802 {
803 // Force an exhausiveness check with a switch. When adding blend modes,
804 // update this switch with a new name/value to make it selectable in the
805 // test GUI.
806
807 const BlendMode b{};
808 static_assert(b == BlendMode::kClear); // Ensure the first item in
809 // the switch is the first
810 // item in the enum.
811 static_assert(Entity::kLastPipelineBlendMode == BlendMode::kModulate);
812 switch (b) {
813 case BlendMode::kClear:
814 blend_mode_names.push_back("Clear");
815 blend_mode_values.push_back(BlendMode::kClear);
816 case BlendMode::kSource:
817 blend_mode_names.push_back("Source");
818 blend_mode_values.push_back(BlendMode::kSource);
819 case BlendMode::kDestination:
820 blend_mode_names.push_back("Destination");
821 blend_mode_values.push_back(BlendMode::kDestination);
822 case BlendMode::kSourceOver:
823 blend_mode_names.push_back("SourceOver");
824 blend_mode_values.push_back(BlendMode::kSourceOver);
825 case BlendMode::kDestinationOver:
826 blend_mode_names.push_back("DestinationOver");
827 blend_mode_values.push_back(BlendMode::kDestinationOver);
828 case BlendMode::kSourceIn:
829 blend_mode_names.push_back("SourceIn");
830 blend_mode_values.push_back(BlendMode::kSourceIn);
831 case BlendMode::kDestinationIn:
832 blend_mode_names.push_back("DestinationIn");
833 blend_mode_values.push_back(BlendMode::kDestinationIn);
834 case BlendMode::kSourceOut:
835 blend_mode_names.push_back("SourceOut");
836 blend_mode_values.push_back(BlendMode::kSourceOut);
837 case BlendMode::kDestinationOut:
838 blend_mode_names.push_back("DestinationOut");
839 blend_mode_values.push_back(BlendMode::kDestinationOut);
840 case BlendMode::kSourceATop:
841 blend_mode_names.push_back("SourceATop");
842 blend_mode_values.push_back(BlendMode::kSourceATop);
843 case BlendMode::kDestinationATop:
844 blend_mode_names.push_back("DestinationATop");
845 blend_mode_values.push_back(BlendMode::kDestinationATop);
846 case BlendMode::kXor:
847 blend_mode_names.push_back("Xor");
848 blend_mode_values.push_back(BlendMode::kXor);
849 case BlendMode::kPlus:
850 blend_mode_names.push_back("Plus");
851 blend_mode_values.push_back(BlendMode::kPlus);
852 case BlendMode::kModulate:
853 blend_mode_names.push_back("Modulate");
854 blend_mode_values.push_back(BlendMode::kModulate);
855 };
856 }
857
858 auto callback = [&](ContentContext& context, RenderPass& pass) {
859 auto world_matrix = Matrix::MakeScale(GetContentScale());
860 auto draw_rect = [&context, &pass, &world_matrix](
861 Rect rect, Color color, BlendMode blend_mode) -> bool {
862 using VS = SolidFillPipeline::VertexShader;
863
864 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
865 {
866 auto r = rect.GetLTRB();
867 vtx_builder.AddVertices({
868 {Point(r[0], r[1])},
869 {Point(r[2], r[1])},
870 {Point(r[2], r[3])},
871 {Point(r[0], r[1])},
872 {Point(r[2], r[3])},
873 {Point(r[0], r[3])},
874 });
875 }
876
877 Command cmd;
878 DEBUG_COMMAND_INFO(cmd, "Blended Rectangle");
879 auto options = OptionsFromPass(pass);
880 options.blend_mode = blend_mode;
881 options.primitive_type = PrimitiveType::kTriangle;
882 cmd.pipeline = context.GetSolidFillPipeline(options);
883 cmd.BindVertices(
884 vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
885
886 VS::FrameInfo frame_info;
887 frame_info.mvp = pass.GetOrthographicTransform() * world_matrix;
888 frame_info.color = color.Premultiply();
889 VS::BindFrameInfo(cmd,
890 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
891
892 return pass.AddCommand(std::move(cmd));
893 };
894
895 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
896 static Color color1(1, 0, 0, 0.5), color2(0, 1, 0, 0.5);
897 ImGui::ColorEdit4("Color 1", reinterpret_cast<float*>(&color1));
898 ImGui::ColorEdit4("Color 2", reinterpret_cast<float*>(&color2));
899 static int current_blend_index = 3;
900 ImGui::ListBox("Blending mode", &current_blend_index,
901 blend_mode_names.data(), blend_mode_names.size());
902 ImGui::End();
903
904 BlendMode selected_mode = blend_mode_values[current_blend_index];
905
906 Point a, b, c, d;
907 std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(
908 Point(400, 100), Point(200, 300), 20, Color::White(), Color::White());
909 std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(
910 Point(470, 190), Point(270, 390), 20, Color::White(), Color::White());
911
912 bool result = true;
913 result = result &&
914 draw_rect(Rect::MakeXYWH(0, 0, pass.GetRenderTargetSize().width,
915 pass.GetRenderTargetSize().height),
916 Color(), BlendMode::kClear);
917 result = result && draw_rect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), color1,
918 BlendMode::kSourceOver);
919 result = result && draw_rect(Rect::MakeLTRB(c.x, c.y, d.x, d.y), color2,
920 selected_mode);
921 return result;
922 };
923 ASSERT_TRUE(OpenPlaygroundHere(callback));
924 }
925
926 TEST_P(EntityTest, BezierCircleScaled) {
927 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
928 static float scale = 20;
929
930 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
931 ImGui::SliderFloat("Scale", &scale, 1, 100);
932 ImGui::End();
933
934 Entity entity;
935 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
936 auto path = PathBuilder{}
937 .MoveTo({97.325, 34.818})
938 .CubicCurveTo({98.50862885295136, 34.81812293973836},
939 {99.46822048142015, 33.85863261475589},
940 {99.46822048142015, 32.67499810206613})
941 .CubicCurveTo({99.46822048142015, 31.491363589376355},
942 {98.50862885295136, 30.53187326439389},
943 {97.32499434685802, 30.531998226542708})
944 .CubicCurveTo({96.14153655073771, 30.532123170035373},
945 {95.18222070648729, 31.491540299350355},
946 {95.18222070648729, 32.67499810206613})
947 .CubicCurveTo({95.18222070648729, 33.85845590478189},
948 {96.14153655073771, 34.81787303409686},
949 {97.32499434685802, 34.81799797758954})
950 .Close()
951 .TakePath();
952 entity.SetTransform(
953 Matrix::MakeScale({scale, scale, 1.0}).Translate({-90, -20, 0}));
954 entity.SetContents(SolidColorContents::Make(std::move(path), Color::Red()));
955 return entity.Render(context, pass);
956 };
957 ASSERT_TRUE(OpenPlaygroundHere(callback));
958 }
959
960 TEST_P(EntityTest, Filters) {
961 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
962 auto boston = CreateTextureForFixture("boston.jpg");
963 auto kalimba = CreateTextureForFixture("kalimba.jpg");
964 ASSERT_TRUE(bridge && boston && kalimba);
965
966 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
967 auto fi_bridge = FilterInput::Make(bridge);
968 auto fi_boston = FilterInput::Make(boston);
969 auto fi_kalimba = FilterInput::Make(kalimba);
970
971 std::shared_ptr<FilterContents> blend0 = ColorFilterContents::MakeBlend(
972 BlendMode::kModulate, {fi_kalimba, fi_boston});
973
974 auto blend1 = ColorFilterContents::MakeBlend(
975 BlendMode::kScreen,
976 {FilterInput::Make(blend0), fi_bridge, fi_bridge, fi_bridge});
977
978 Entity entity;
979 entity.SetTransform(Matrix::MakeScale(GetContentScale()) *
980 Matrix::MakeTranslation({500, 300}) *
981 Matrix::MakeScale(Vector2{0.5, 0.5}));
982 entity.SetContents(blend1);
983 return entity.Render(context, pass);
984 };
985 ASSERT_TRUE(OpenPlaygroundHere(callback));
986 }
987
988 TEST_P(EntityTest, GaussianBlurFilter) {
989 auto boston = CreateTextureForFixture("boston.jpg");
990 ASSERT_TRUE(boston);
991
992 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
993 const char* input_type_names[] = {"Texture", "Solid Color"};
994 const char* blur_type_names[] = {"Image blur", "Mask blur"};
995 const char* pass_variation_names[] = {"New", "2D Directional",
996 "Directional"};
997 const char* blur_style_names[] = {"Normal", "Solid", "Outer", "Inner"};
998 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
999 const FilterContents::BlurStyle blur_styles[] = {
1000 FilterContents::BlurStyle::kNormal, FilterContents::BlurStyle::kSolid,
1001 FilterContents::BlurStyle::kOuter, FilterContents::BlurStyle::kInner};
1002 const Entity::TileMode tile_modes[] = {
1003 Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
1004 Entity::TileMode::kMirror, Entity::TileMode::kDecal};
1005
1006 // UI state.
1007 static int selected_input_type = 0;
1008 static Color input_color = Color::Black();
1009 static int selected_blur_type = 0;
1010 static int selected_pass_variation = 0;
1011 static bool combined_sigma = false;
1012 static float blur_amount_coarse[2] = {0, 0};
1013 static float blur_amount_fine[2] = {10, 10};
1014 static int selected_blur_style = 0;
1015 static int selected_tile_mode = 3;
1016 static Color cover_color(1, 0, 0, 0.2);
1017 static Color bounds_color(0, 1, 0, 0.1);
1018 static float offset[2] = {500, 400};
1019 static float rotation = 0;
1020 static float scale[2] = {0.65, 0.65};
1021 static float skew[2] = {0, 0};
1022 static float path_rect[4] = {0, 0,
1023 static_cast<float>(boston->GetSize().width),
1024 static_cast<float>(boston->GetSize().height)};
1025
1026 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1027 {
1028 ImGui::Combo("Input type", &selected_input_type, input_type_names,
1029 sizeof(input_type_names) / sizeof(char*));
1030 if (selected_input_type == 0) {
1031 ImGui::SliderFloat("Input opacity", &input_color.alpha, 0, 1);
1032 } else {
1033 ImGui::ColorEdit4("Input color",
1034 reinterpret_cast<float*>(&input_color));
1035 }
1036 ImGui::Combo("Blur type", &selected_blur_type, blur_type_names,
1037 sizeof(blur_type_names) / sizeof(char*));
1038 if (selected_blur_type == 0) {
1039 ImGui::Combo("Pass variation", &selected_pass_variation,
1040 pass_variation_names,
1041 sizeof(pass_variation_names) / sizeof(char*));
1042 }
1043 ImGui::Checkbox("Combined sigma", &combined_sigma);
1044 if (combined_sigma) {
1045 ImGui::SliderFloat("Sigma (coarse)", blur_amount_coarse, 0, 1000);
1046 ImGui::SliderFloat("Sigma (fine)", blur_amount_fine, 0, 10);
1047 blur_amount_coarse[1] = blur_amount_coarse[0];
1048 blur_amount_fine[1] = blur_amount_fine[0];
1049 } else {
1050 ImGui::SliderFloat2("Sigma (coarse)", blur_amount_coarse, 0, 1000);
1051 ImGui::SliderFloat2("Sigma (fine)", blur_amount_fine, 0, 10);
1052 }
1053 ImGui::Combo("Blur style", &selected_blur_style, blur_style_names,
1054 sizeof(blur_style_names) / sizeof(char*));
1055 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
1056 sizeof(tile_mode_names) / sizeof(char*));
1057 ImGui::ColorEdit4("Cover color", reinterpret_cast<float*>(&cover_color));
1058 ImGui::ColorEdit4("Bounds color",
1059 reinterpret_cast<float*>(&bounds_color));
1060 ImGui::SliderFloat2("Translation", offset, 0,
1061 pass.GetRenderTargetSize().width);
1062 ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2);
1063 ImGui::SliderFloat2("Scale", scale, 0, 3);
1064 ImGui::SliderFloat2("Skew", skew, -3, 3);
1065 ImGui::SliderFloat4("Path XYWH", path_rect, -1000, 1000);
1066 }
1067 ImGui::End();
1068
1069 auto blur_sigma_x = Sigma{blur_amount_coarse[0] + blur_amount_fine[0]};
1070 auto blur_sigma_y = Sigma{blur_amount_coarse[1] + blur_amount_fine[1]};
1071
1072 std::shared_ptr<Contents> input;
1073 Size input_size;
1074
1075 auto input_rect =
1076 Rect::MakeXYWH(path_rect[0], path_rect[1], path_rect[2], path_rect[3]);
1077 if (selected_input_type == 0) {
1078 auto texture = std::make_shared<TextureContents>();
1079 texture->SetSourceRect(Rect::MakeSize(boston->GetSize()));
1080 texture->SetDestinationRect(input_rect);
1081 texture->SetTexture(boston);
1082 texture->SetOpacity(input_color.alpha);
1083
1084 input = texture;
1085 input_size = input_rect.GetSize();
1086 } else {
1087 auto fill = std::make_shared<SolidColorContents>();
1088 fill->SetColor(input_color);
1089 fill->SetGeometry(
1090 Geometry::MakeFillPath(PathBuilder{}.AddRect(input_rect).TakePath()));
1091
1092 input = fill;
1093 input_size = input_rect.GetSize();
1094 }
1095
1096 std::shared_ptr<FilterContents> blur;
1097 switch (selected_pass_variation) {
1098 case 0:
1099 blur = std::make_shared<GaussianBlurFilterContents>(
1100 blur_sigma_x.sigma, blur_sigma_y.sigma,
1101 tile_modes[selected_tile_mode]);
1102 blur->SetInputs({FilterInput::Make(input)});
1103 break;
1104 case 1:
1105 blur = FilterContents::MakeGaussianBlur(
1106 FilterInput::Make(input), blur_sigma_x, blur_sigma_y,
1107 blur_styles[selected_blur_style], tile_modes[selected_tile_mode]);
1108 break;
1109 case 2: {
1110 Vector2 blur_vector(blur_sigma_x.sigma, blur_sigma_y.sigma);
1111 blur = FilterContents::MakeDirectionalGaussianBlur(
1112 FilterInput::Make(input), Sigma{blur_vector.GetLength()},
1113 blur_vector.Normalize());
1114 break;
1115 }
1116 };
1117 FML_CHECK(blur);
1118
1119 auto mask_blur = FilterContents::MakeBorderMaskBlur(
1120 FilterInput::Make(input), blur_sigma_x, blur_sigma_y,
1121 blur_styles[selected_blur_style]);
1122
1123 auto ctm = Matrix::MakeScale(GetContentScale()) *
1124 Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
1125 Matrix::MakeRotationZ(Radians(rotation)) *
1126 Matrix::MakeScale(Vector2(scale[0], scale[1])) *
1127 Matrix::MakeSkew(skew[0], skew[1]) *
1128 Matrix::MakeTranslation(-Point(input_size) / 2);
1129
1130 auto target_contents = selected_blur_type == 0 ? blur : mask_blur;
1131
1132 Entity entity;
1133 entity.SetContents(target_contents);
1134 entity.SetTransform(ctm);
1135
1136 entity.Render(context, pass);
1137
1138 // Renders a red "cover" rectangle that shows the original position of the
1139 // unfiltered input.
1140 Entity cover_entity;
1141 cover_entity.SetContents(SolidColorContents::Make(
1142 PathBuilder{}.AddRect(input_rect).TakePath(), cover_color));
1143 cover_entity.SetTransform(ctm);
1144
1145 cover_entity.Render(context, pass);
1146
1147 // Renders a green bounding rect of the target filter.
1148 Entity bounds_entity;
1149 std::optional<Rect> target_contents_coverage =
1150 target_contents->GetCoverage(entity);
1151 if (target_contents_coverage.has_value()) {
1152 bounds_entity.SetContents(SolidColorContents::Make(
1153 PathBuilder{}
1154 .AddRect(target_contents->GetCoverage(entity).value())
1155 .TakePath(),
1156 bounds_color));
1157 bounds_entity.SetTransform(Matrix());
1158
1159 bounds_entity.Render(context, pass);
1160 }
1161
1162 return true;
1163 };
1164 ASSERT_TRUE(OpenPlaygroundHere(callback));
1165 }
1166
1167 TEST_P(EntityTest, MorphologyFilter) {
1168 auto boston = CreateTextureForFixture("boston.jpg");
1169 ASSERT_TRUE(boston);
1170
1171 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
1172 const char* morphology_type_names[] = {"Dilate", "Erode"};
1173 const FilterContents::MorphType morphology_types[] = {
1174 FilterContents::MorphType::kDilate, FilterContents::MorphType::kErode};
1175 static Color input_color = Color::Black();
1176 // UI state.
1177 static int selected_morphology_type = 0;
1178 static float radius[2] = {20, 20};
1179 static Color cover_color(1, 0, 0, 0.2);
1180 static Color bounds_color(0, 1, 0, 0.1);
1181 static float offset[2] = {500, 400};
1182 static float rotation = 0;
1183 static float scale[2] = {0.65, 0.65};
1184 static float skew[2] = {0, 0};
1185 static float path_rect[4] = {0, 0,
1186 static_cast<float>(boston->GetSize().width),
1187 static_cast<float>(boston->GetSize().height)};
1188 static float effect_transform_scale = 1;
1189
1190 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1191 {
1192 ImGui::Combo("Morphology type", &selected_morphology_type,
1193 morphology_type_names,
1194 sizeof(morphology_type_names) / sizeof(char*));
1195 ImGui::SliderFloat2("Radius", radius, 0, 200);
1196 ImGui::SliderFloat("Input opacity", &input_color.alpha, 0, 1);
1197 ImGui::ColorEdit4("Cover color", reinterpret_cast<float*>(&cover_color));
1198 ImGui::ColorEdit4("Bounds color",
1199 reinterpret_cast<float*>(&bounds_color));
1200 ImGui::SliderFloat2("Translation", offset, 0,
1201 pass.GetRenderTargetSize().width);
1202 ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2);
1203 ImGui::SliderFloat2("Scale", scale, 0, 3);
1204 ImGui::SliderFloat2("Skew", skew, -3, 3);
1205 ImGui::SliderFloat4("Path XYWH", path_rect, -1000, 1000);
1206 ImGui::SliderFloat("Effect transform scale", &effect_transform_scale, 0,
1207 3);
1208 }
1209 ImGui::End();
1210
1211 std::shared_ptr<Contents> input;
1212 Size input_size;
1213
1214 auto input_rect =
1215 Rect::MakeXYWH(path_rect[0], path_rect[1], path_rect[2], path_rect[3]);
1216 auto texture = std::make_shared<TextureContents>();
1217 texture->SetSourceRect(Rect::MakeSize(boston->GetSize()));
1218 texture->SetDestinationRect(input_rect);
1219 texture->SetTexture(boston);
1220 texture->SetOpacity(input_color.alpha);
1221
1222 input = texture;
1223 input_size = input_rect.GetSize();
1224
1225 auto contents = FilterContents::MakeMorphology(
1226 FilterInput::Make(input), Radius{radius[0]}, Radius{radius[1]},
1227 morphology_types[selected_morphology_type]);
1228 contents->SetEffectTransform(Matrix::MakeScale(
1229 Vector2{effect_transform_scale, effect_transform_scale}));
1230
1231 auto ctm = Matrix::MakeScale(GetContentScale()) *
1232 Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
1233 Matrix::MakeRotationZ(Radians(rotation)) *
1234 Matrix::MakeScale(Vector2(scale[0], scale[1])) *
1235 Matrix::MakeSkew(skew[0], skew[1]) *
1236 Matrix::MakeTranslation(-Point(input_size) / 2);
1237
1238 Entity entity;
1239 entity.SetContents(contents);
1240 entity.SetTransform(ctm);
1241
1242 entity.Render(context, pass);
1243
1244 // Renders a red "cover" rectangle that shows the original position of the
1245 // unfiltered input.
1246 Entity cover_entity;
1247 cover_entity.SetContents(SolidColorContents::Make(
1248 PathBuilder{}.AddRect(input_rect).TakePath(), cover_color));
1249 cover_entity.SetTransform(ctm);
1250
1251 cover_entity.Render(context, pass);
1252
1253 // Renders a green bounding rect of the target filter.
1254 Entity bounds_entity;
1255 bounds_entity.SetContents(SolidColorContents::Make(
1256 PathBuilder{}.AddRect(contents->GetCoverage(entity).value()).TakePath(),
1257 bounds_color));
1258 bounds_entity.SetTransform(Matrix());
1259
1260 bounds_entity.Render(context, pass);
1261
1262 return true;
1263 };
1264 ASSERT_TRUE(OpenPlaygroundHere(callback));
1265 }
1266
1267 TEST_P(EntityTest, SetBlendMode) {
1268 Entity entity;
1269 ASSERT_EQ(entity.GetBlendMode(), BlendMode::kSourceOver);
1270 entity.SetBlendMode(BlendMode::kClear);
1271 ASSERT_EQ(entity.GetBlendMode(), BlendMode::kClear);
1272 }
1273
1274 TEST_P(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) {
1275 Entity entity;
1276 entity.SetContents(std::make_shared<SolidColorContents>());
1277 ASSERT_FALSE(entity.GetCoverage().has_value());
1278 }
1279
1280 TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) {
1281 {
1282 auto geometry = Geometry::MakeStrokePath(
1283 PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath(), 4.0, 4.0,
1284 Cap::kButt, Join::kBevel);
1285
1286 Entity entity;
1287 auto contents = std::make_unique<SolidColorContents>();
1288 contents->SetGeometry(std::move(geometry));
1289 contents->SetColor(Color::Black());
1290 entity.SetContents(std::move(contents));
1291 auto actual = entity.GetCoverage();
1292 auto expected = Rect::MakeLTRB(-2, -2, 12, 12);
1293 ASSERT_TRUE(actual.has_value());
1294 ASSERT_RECT_NEAR(actual.value(), expected);
1295 }
1296
1297 // Cover the Cap::kSquare case.
1298 {
1299 auto geometry = Geometry::MakeStrokePath(
1300 PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath(), 4.0, 4.0,
1301 Cap::kSquare, Join::kBevel);
1302
1303 Entity entity;
1304 auto contents = std::make_unique<SolidColorContents>();
1305 contents->SetGeometry(std::move(geometry));
1306 contents->SetColor(Color::Black());
1307 entity.SetContents(std::move(contents));
1308 auto actual = entity.GetCoverage();
1309 auto expected =
1310 Rect::MakeLTRB(-sqrt(8), -sqrt(8), 10 + sqrt(8), 10 + sqrt(8));
1311 ASSERT_TRUE(actual.has_value());
1312 ASSERT_RECT_NEAR(actual.value(), expected);
1313 }
1314
1315 // Cover the Join::kMiter case.
1316 {
1317 auto geometry = Geometry::MakeStrokePath(
1318 PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath(), 4.0, 2.0,
1319 Cap::kSquare, Join::kMiter);
1320
1321 Entity entity;
1322 auto contents = std::make_unique<SolidColorContents>();
1323 contents->SetGeometry(std::move(geometry));
1324 contents->SetColor(Color::Black());
1325 entity.SetContents(std::move(contents));
1326 auto actual = entity.GetCoverage();
1327 auto expected = Rect::MakeLTRB(-4, -4, 14, 14);
1328 ASSERT_TRUE(actual.has_value());
1329 ASSERT_RECT_NEAR(actual.value(), expected);
1330 }
1331 }
1332
1333 TEST_P(EntityTest, BorderMaskBlurCoverageIsCorrect) {
1334 auto fill = std::make_shared<SolidColorContents>();
1335 fill->SetGeometry(Geometry::MakeFillPath(
1336 PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
1337 fill->SetColor(Color::CornflowerBlue());
1338 auto border_mask_blur = FilterContents::MakeBorderMaskBlur(
1339 FilterInput::Make(fill), Radius{3}, Radius{4});
1340
1341 {
1342 Entity e;
1343 e.SetTransform(Matrix());
1344 auto actual = border_mask_blur->GetCoverage(e);
1345 auto expected = Rect::MakeXYWH(-3, -4, 306, 408);
1346 ASSERT_TRUE(actual.has_value());
1347 ASSERT_RECT_NEAR(actual.value(), expected);
1348 }
1349
1350 {
1351 Entity e;
1352 e.SetTransform(Matrix::MakeRotationZ(Radians{kPi / 4}));
1353 auto actual = border_mask_blur->GetCoverage(e);
1354 auto expected = Rect::MakeXYWH(-287.792, -4.94975, 504.874, 504.874);
1355 ASSERT_TRUE(actual.has_value());
1356 ASSERT_RECT_NEAR(actual.value(), expected);
1357 }
1358 }
1359
1360 TEST_P(EntityTest, DrawAtlasNoColor) {
1361 // Draws the image as four squares stiched together.
1362 auto atlas = CreateTextureForFixture("bay_bridge.jpg");
1363 auto size = atlas->GetSize();
1364 // Divide image into four quadrants.
1365 Scalar half_width = size.width / 2;
1366 Scalar half_height = size.height / 2;
1367 std::vector<Rect> texture_coordinates = {
1368 Rect::MakeLTRB(0, 0, half_width, half_height),
1369 Rect::MakeLTRB(half_width, 0, size.width, half_height),
1370 Rect::MakeLTRB(0, half_height, half_width, size.height),
1371 Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
1372 // Position quadrants adjacent to eachother.
1373 std::vector<Matrix> transforms = {
1374 Matrix::MakeTranslation({0, 0, 0}),
1375 Matrix::MakeTranslation({half_width, 0, 0}),
1376 Matrix::MakeTranslation({0, half_height, 0}),
1377 Matrix::MakeTranslation({half_width, half_height, 0})};
1378 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1379
1380 contents->SetTransforms(std::move(transforms));
1381 contents->SetTextureCoordinates(std::move(texture_coordinates));
1382 contents->SetTexture(atlas);
1383 contents->SetBlendMode(BlendMode::kSource);
1384
1385 Entity e;
1386 e.SetTransform(Matrix::MakeScale(GetContentScale()));
1387 e.SetContents(contents);
1388
1389 ASSERT_TRUE(OpenPlaygroundHere(std::move(e)));
1390 }
1391
1392 TEST_P(EntityTest, DrawAtlasWithColorAdvanced) {
1393 // Draws the image as four squares stiched together.
1394 auto atlas = CreateTextureForFixture("bay_bridge.jpg");
1395 auto size = atlas->GetSize();
1396 // Divide image into four quadrants.
1397 Scalar half_width = size.width / 2;
1398 Scalar half_height = size.height / 2;
1399 std::vector<Rect> texture_coordinates = {
1400 Rect::MakeLTRB(0, 0, half_width, half_height),
1401 Rect::MakeLTRB(half_width, 0, size.width, half_height),
1402 Rect::MakeLTRB(0, half_height, half_width, size.height),
1403 Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
1404 // Position quadrants adjacent to eachother.
1405 std::vector<Matrix> transforms = {
1406 Matrix::MakeTranslation({0, 0, 0}),
1407 Matrix::MakeTranslation({half_width, 0, 0}),
1408 Matrix::MakeTranslation({0, half_height, 0}),
1409 Matrix::MakeTranslation({half_width, half_height, 0})};
1410 std::vector<Color> colors = {Color::Red(), Color::Green(), Color::Blue(),
1411 Color::Yellow()};
1412 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1413
1414 contents->SetTransforms(std::move(transforms));
1415 contents->SetTextureCoordinates(std::move(texture_coordinates));
1416 contents->SetTexture(atlas);
1417 contents->SetColors(colors);
1418 contents->SetBlendMode(BlendMode::kModulate);
1419
1420 Entity e;
1421 e.SetTransform(Matrix::MakeScale(GetContentScale()));
1422 e.SetContents(contents);
1423
1424 ASSERT_TRUE(OpenPlaygroundHere(std::move(e)));
1425 }
1426
1427 TEST_P(EntityTest, DrawAtlasWithColorSimple) {
1428 // Draws the image as four squares stiched together. Because blend modes
1429 // aren't implented this ends up as four solid color blocks.
1430 auto atlas = CreateTextureForFixture("bay_bridge.jpg");
1431 auto size = atlas->GetSize();
1432 // Divide image into four quadrants.
1433 Scalar half_width = size.width / 2;
1434 Scalar half_height = size.height / 2;
1435 std::vector<Rect> texture_coordinates = {
1436 Rect::MakeLTRB(0, 0, half_width, half_height),
1437 Rect::MakeLTRB(half_width, 0, size.width, half_height),
1438 Rect::MakeLTRB(0, half_height, half_width, size.height),
1439 Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
1440 // Position quadrants adjacent to eachother.
1441 std::vector<Matrix> transforms = {
1442 Matrix::MakeTranslation({0, 0, 0}),
1443 Matrix::MakeTranslation({half_width, 0, 0}),
1444 Matrix::MakeTranslation({0, half_height, 0}),
1445 Matrix::MakeTranslation({half_width, half_height, 0})};
1446 std::vector<Color> colors = {Color::Red(), Color::Green(), Color::Blue(),
1447 Color::Yellow()};
1448 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1449
1450 contents->SetTransforms(std::move(transforms));
1451 contents->SetTextureCoordinates(std::move(texture_coordinates));
1452 contents->SetTexture(atlas);
1453 contents->SetColors(colors);
1454 contents->SetBlendMode(BlendMode::kSourceATop);
1455
1456 Entity e;
1457 e.SetTransform(Matrix::MakeScale(GetContentScale()));
1458 e.SetContents(contents);
1459
1460 ASSERT_TRUE(OpenPlaygroundHere(std::move(e)));
1461 }
1462
1463 TEST_P(EntityTest, DrawAtlasUsesProvidedCullRectForCoverage) {
1464 auto atlas = CreateTextureForFixture("bay_bridge.jpg");
1465 auto size = atlas->GetSize();
1466
1467 Scalar half_width = size.width / 2;
1468 Scalar half_height = size.height / 2;
1469 std::vector<Rect> texture_coordinates = {
1470 Rect::MakeLTRB(0, 0, half_width, half_height),
1471 Rect::MakeLTRB(half_width, 0, size.width, half_height),
1472 Rect::MakeLTRB(0, half_height, half_width, size.height),
1473 Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
1474 std::vector<Matrix> transforms = {
1475 Matrix::MakeTranslation({0, 0, 0}),
1476 Matrix::MakeTranslation({half_width, 0, 0}),
1477 Matrix::MakeTranslation({0, half_height, 0}),
1478 Matrix::MakeTranslation({half_width, half_height, 0})};
1479
1480 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1481
1482 contents->SetTransforms(std::move(transforms));
1483 contents->SetTextureCoordinates(std::move(texture_coordinates));
1484 contents->SetTexture(atlas);
1485 contents->SetBlendMode(BlendMode::kSource);
1486
1487 auto transform = Matrix::MakeScale(GetContentScale());
1488 Entity e;
1489 e.SetTransform(transform);
1490 e.SetContents(contents);
1491
1492 ASSERT_EQ(contents->GetCoverage(e).value(),
1493 Rect::MakeSize(size).TransformBounds(transform));
1494
1495 contents->SetCullRect(Rect::MakeLTRB(0, 0, 10, 10));
1496
1497 ASSERT_EQ(contents->GetCoverage(e).value(),
1498 Rect::MakeLTRB(0, 0, 10, 10).TransformBounds(transform));
1499 }
1500
1501 TEST_P(EntityTest, DrawAtlasWithOpacity) {
1502 // Draws the image as four squares stiched together slightly
1503 // opaque
1504 auto atlas = CreateTextureForFixture("bay_bridge.jpg");
1505 auto size = atlas->GetSize();
1506 // Divide image into four quadrants.
1507 Scalar half_width = size.width / 2;
1508 Scalar half_height = size.height / 2;
1509 std::vector<Rect> texture_coordinates = {
1510 Rect::MakeLTRB(0, 0, half_width, half_height),
1511 Rect::MakeLTRB(half_width, 0, size.width, half_height),
1512 Rect::MakeLTRB(0, half_height, half_width, size.height),
1513 Rect::MakeLTRB(half_width, half_height, size.width, size.height)};
1514 // Position quadrants adjacent to eachother.
1515 std::vector<Matrix> transforms = {
1516 Matrix::MakeTranslation({0, 0, 0}),
1517 Matrix::MakeTranslation({half_width, 0, 0}),
1518 Matrix::MakeTranslation({0, half_height, 0}),
1519 Matrix::MakeTranslation({half_width, half_height, 0})};
1520
1521 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1522
1523 contents->SetTransforms(std::move(transforms));
1524 contents->SetTextureCoordinates(std::move(texture_coordinates));
1525 contents->SetTexture(atlas);
1526 contents->SetBlendMode(BlendMode::kSource);
1527 contents->SetAlpha(0.5);
1528
1529 Entity e;
1530 e.SetTransform(Matrix::MakeScale(GetContentScale()));
1531 e.SetContents(contents);
1532
1533 ASSERT_TRUE(OpenPlaygroundHere(std::move(e)));
1534 }
1535
1536 TEST_P(EntityTest, DrawAtlasNoColorFullSize) {
1537 auto atlas = CreateTextureForFixture("bay_bridge.jpg");
1538 auto size = atlas->GetSize();
1539 std::vector<Rect> texture_coordinates = {
1540 Rect::MakeLTRB(0, 0, size.width, size.height)};
1541 std::vector<Matrix> transforms = {Matrix::MakeTranslation({0, 0, 0})};
1542 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1543
1544 contents->SetTransforms(std::move(transforms));
1545 contents->SetTextureCoordinates(std::move(texture_coordinates));
1546 contents->SetTexture(atlas);
1547 contents->SetBlendMode(BlendMode::kSource);
1548
1549 Entity e;
1550 e.SetTransform(Matrix::MakeScale(GetContentScale()));
1551 e.SetContents(contents);
1552
1553 ASSERT_TRUE(OpenPlaygroundHere(std::move(e)));
1554 }
1555
1556 TEST_P(EntityTest, SolidFillCoverageIsCorrect) {
1557 // No transform
1558 {
1559 auto fill = std::make_shared<SolidColorContents>();
1560 fill->SetColor(Color::CornflowerBlue());
1561 auto expected = Rect::MakeLTRB(100, 110, 200, 220);
1562 fill->SetGeometry(
1563 Geometry::MakeFillPath(PathBuilder{}.AddRect(expected).TakePath()));
1564
1565 auto coverage = fill->GetCoverage({});
1566 ASSERT_TRUE(coverage.has_value());
1567 ASSERT_RECT_NEAR(coverage.value(), expected);
1568 }
1569
1570 // Entity transform
1571 {
1572 auto fill = std::make_shared<SolidColorContents>();
1573 fill->SetColor(Color::CornflowerBlue());
1574 fill->SetGeometry(Geometry::MakeFillPath(
1575 PathBuilder{}.AddRect(Rect::MakeLTRB(100, 110, 200, 220)).TakePath()));
1576
1577 Entity entity;
1578 entity.SetTransform(Matrix::MakeTranslation(Vector2(4, 5)));
1579 entity.SetContents(std::move(fill));
1580
1581 auto coverage = entity.GetCoverage();
1582 auto expected = Rect::MakeLTRB(104, 115, 204, 225);
1583 ASSERT_TRUE(coverage.has_value());
1584 ASSERT_RECT_NEAR(coverage.value(), expected);
1585 }
1586
1587 // No coverage for fully transparent colors
1588 {
1589 auto fill = std::make_shared<SolidColorContents>();
1590 fill->SetColor(Color::WhiteTransparent());
1591 fill->SetGeometry(Geometry::MakeFillPath(
1592 PathBuilder{}.AddRect(Rect::MakeLTRB(100, 110, 200, 220)).TakePath()));
1593
1594 auto coverage = fill->GetCoverage({});
1595 ASSERT_FALSE(coverage.has_value());
1596 }
1597 }
1598
1599 TEST_P(EntityTest, SolidFillShouldRenderIsCorrect) {
1600 // No path.
1601 {
1602 auto fill = std::make_shared<SolidColorContents>();
1603 fill->SetColor(Color::CornflowerBlue());
1604 ASSERT_FALSE(fill->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
1605 ASSERT_FALSE(
1606 fill->ShouldRender(Entity{}, Rect::MakeLTRB(-100, -100, -50, -50)));
1607 }
1608
1609 // With path.
1610 {
1611 auto fill = std::make_shared<SolidColorContents>();
1612 fill->SetColor(Color::CornflowerBlue());
1613 fill->SetGeometry(Geometry::MakeFillPath(
1614 PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath()));
1615 ASSERT_TRUE(fill->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
1616 ASSERT_FALSE(
1617 fill->ShouldRender(Entity{}, Rect::MakeLTRB(-100, -100, -50, -50)));
1618 }
1619
1620 // With paint cover.
1621 {
1622 auto fill = std::make_shared<SolidColorContents>();
1623 fill->SetColor(Color::CornflowerBlue());
1624 fill->SetGeometry(Geometry::MakeCover());
1625 ASSERT_TRUE(fill->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
1626 ASSERT_TRUE(
1627 fill->ShouldRender(Entity{}, Rect::MakeLTRB(-100, -100, -50, -50)));
1628 }
1629 }
1630
1631 TEST_P(EntityTest, DoesNotCullEntitiesByDefault) {
1632 auto fill = std::make_shared<SolidColorContents>();
1633 fill->SetColor(Color::CornflowerBlue());
1634 fill->SetGeometry(
1635 Geometry::MakeRect(Rect::MakeLTRB(-1000, -1000, -900, -900)));
1636
1637 Entity entity;
1638 entity.SetContents(fill);
1639
1640 // Even though the entity is offscreen, this should still render because we do
1641 // not compute the coverage intersection by default.
1642 EXPECT_TRUE(entity.ShouldRender(Rect::MakeLTRB(0, 0, 100, 100)));
1643 }
1644
1645 TEST_P(EntityTest, ClipContentsShouldRenderIsCorrect) {
1646 // For clip ops, `ShouldRender` should always return true.
1647
1648 // Clip.
1649 {
1650 auto clip = std::make_shared<ClipContents>();
1651 ASSERT_TRUE(clip->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
1652 clip->SetGeometry(Geometry::MakeFillPath(
1653 PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath()));
1654 ASSERT_TRUE(clip->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
1655 ASSERT_TRUE(
1656 clip->ShouldRender(Entity{}, Rect::MakeLTRB(-100, -100, -50, -50)));
1657 }
1658
1659 // Clip restore.
1660 {
1661 auto restore = std::make_shared<ClipRestoreContents>();
1662 ASSERT_TRUE(
1663 restore->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
1664 ASSERT_TRUE(
1665 restore->ShouldRender(Entity{}, Rect::MakeLTRB(-100, -100, -50, -50)));
1666 }
1667 }
1668
1669 TEST_P(EntityTest, ClipContentsGetClipCoverageIsCorrect) {
1670 // Intersection: No stencil coverage, no geometry.
1671 {
1672 auto clip = std::make_shared<ClipContents>();
1673 clip->SetClipOperation(Entity::ClipOperation::kIntersect);
1674 auto result = clip->GetClipCoverage(Entity{}, Rect{});
1675
1676 ASSERT_FALSE(result.coverage.has_value());
1677 }
1678
1679 // Intersection: No stencil coverage, with geometry.
1680 {
1681 auto clip = std::make_shared<ClipContents>();
1682 clip->SetClipOperation(Entity::ClipOperation::kIntersect);
1683 clip->SetGeometry(Geometry::MakeFillPath(
1684 PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath()));
1685 auto result = clip->GetClipCoverage(Entity{}, Rect{});
1686
1687 ASSERT_FALSE(result.coverage.has_value());
1688 }
1689
1690 // Intersection: With stencil coverage, no geometry.
1691 {
1692 auto clip = std::make_shared<ClipContents>();
1693 clip->SetClipOperation(Entity::ClipOperation::kIntersect);
1694 auto result =
1695 clip->GetClipCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100));
1696
1697 ASSERT_FALSE(result.coverage.has_value());
1698 }
1699
1700 // Intersection: With stencil coverage, with geometry.
1701 {
1702 auto clip = std::make_shared<ClipContents>();
1703 clip->SetClipOperation(Entity::ClipOperation::kIntersect);
1704 clip->SetGeometry(Geometry::MakeFillPath(
1705 PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 50, 50)).TakePath()));
1706 auto result =
1707 clip->GetClipCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100));
1708
1709 ASSERT_TRUE(result.coverage.has_value());
1710 ASSERT_RECT_NEAR(result.coverage.value(), Rect::MakeLTRB(0, 0, 50, 50));
1711 ASSERT_EQ(result.type, Contents::ClipCoverage::Type::kAppend);
1712 }
1713
1714 // Difference: With stencil coverage, with geometry.
1715 {
1716 auto clip = std::make_shared<ClipContents>();
1717 clip->SetClipOperation(Entity::ClipOperation::kDifference);
1718 clip->SetGeometry(Geometry::MakeFillPath(
1719 PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 50, 50)).TakePath()));
1720 auto result =
1721 clip->GetClipCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100));
1722
1723 ASSERT_TRUE(result.coverage.has_value());
1724 ASSERT_RECT_NEAR(result.coverage.value(), Rect::MakeLTRB(0, 0, 100, 100));
1725 ASSERT_EQ(result.type, Contents::ClipCoverage::Type::kAppend);
1726 }
1727 }
1728
1729 TEST_P(EntityTest, RRectShadowTest) {
1730 auto callback = [&](ContentContext& context, RenderPass& pass) {
1731 static Color color = Color::Red();
1732 static float corner_radius = 100;
1733 static float blur_radius = 100;
1734 static bool show_coverage = false;
1735 static Color coverage_color = Color::Green().WithAlpha(0.2);
1736
1737 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1738 ImGui::SliderFloat("Corner radius", &corner_radius, 0, 300);
1739 ImGui::SliderFloat("Blur radius", &blur_radius, 0, 300);
1740 ImGui::ColorEdit4("Color", reinterpret_cast<Scalar*>(&color));
1741 ImGui::Checkbox("Show coverage", &show_coverage);
1742 if (show_coverage) {
1743 ImGui::ColorEdit4("Coverage color",
1744 reinterpret_cast<Scalar*>(&coverage_color));
1745 }
1746 ImGui::End();
1747
1748 auto [top_left, bottom_right] = IMPELLER_PLAYGROUND_LINE(
1749 Point(200, 200), Point(600, 400), 30, Color::White(), Color::White());
1750 auto rect =
1751 Rect::MakeLTRB(top_left.x, top_left.y, bottom_right.x, bottom_right.y);
1752
1753 auto contents = std::make_unique<SolidRRectBlurContents>();
1754 contents->SetRRect(rect, {corner_radius, corner_radius});
1755 contents->SetColor(color);
1756 contents->SetSigma(Radius(blur_radius));
1757
1758 Entity entity;
1759 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
1760 entity.SetContents(std::move(contents));
1761 entity.Render(context, pass);
1762
1763 auto coverage = entity.GetCoverage();
1764 if (show_coverage && coverage.has_value()) {
1765 auto bounds_contents = std::make_unique<SolidColorContents>();
1766 bounds_contents->SetGeometry(Geometry::MakeFillPath(
1767 PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()));
1768 bounds_contents->SetColor(coverage_color.Premultiply());
1769 Entity bounds_entity;
1770 bounds_entity.SetContents(std::move(bounds_contents));
1771 bounds_entity.Render(context, pass);
1772 }
1773
1774 return true;
1775 };
1776 ASSERT_TRUE(OpenPlaygroundHere(callback));
1777 }
1778
1779 TEST_P(EntityTest, ColorMatrixFilterCoverageIsCorrect) {
1780 // Set up a simple color background.
1781 auto fill = std::make_shared<SolidColorContents>();
1782 fill->SetGeometry(Geometry::MakeFillPath(
1783 PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
1784 fill->SetColor(Color::Coral());
1785
1786 // Set the color matrix filter.
1787 ColorMatrix matrix = {
1788 1, 1, 1, 1, 1, //
1789 1, 1, 1, 1, 1, //
1790 1, 1, 1, 1, 1, //
1791 1, 1, 1, 1, 1, //
1792 };
1793
1794 auto filter =
1795 ColorFilterContents::MakeColorMatrix(FilterInput::Make(fill), matrix);
1796
1797 Entity e;
1798 e.SetTransform(Matrix());
1799
1800 // Confirm that the actual filter coverage matches the expected coverage.
1801 auto actual = filter->GetCoverage(e);
1802 auto expected = Rect::MakeXYWH(0, 0, 300, 400);
1803
1804 ASSERT_TRUE(actual.has_value());
1805 ASSERT_RECT_NEAR(actual.value(), expected);
1806 }
1807
1808 TEST_P(EntityTest, ColorMatrixFilterEditable) {
1809 auto bay_bridge = CreateTextureForFixture("bay_bridge.jpg");
1810 ASSERT_TRUE(bay_bridge);
1811
1812 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
1813 // UI state.
1814 static ColorMatrix color_matrix = {
1815 1, 0, 0, 0, 0, //
1816 0, 3, 0, 0, 0, //
1817 0, 0, 1, 0, 0, //
1818 0, 0, 0, 1, 0, //
1819 };
1820 static float offset[2] = {500, 400};
1821 static float rotation = 0;
1822 static float scale[2] = {0.65, 0.65};
1823 static float skew[2] = {0, 0};
1824
1825 // Define the ImGui
1826 ImGui::Begin("Color Matrix", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1827 {
1828 std::string label = "##1";
1829 for (int i = 0; i < 20; i += 5) {
1830 ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
1831 &(color_matrix.array[i]), 5, nullptr, nullptr,
1832 "%.2f", 0);
1833 label[2]++;
1834 }
1835
1836 ImGui::SliderFloat2("Translation", &offset[0], 0,
1837 pass.GetRenderTargetSize().width);
1838 ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2);
1839 ImGui::SliderFloat2("Scale", &scale[0], 0, 3);
1840 ImGui::SliderFloat2("Skew", &skew[0], -3, 3);
1841 }
1842 ImGui::End();
1843
1844 // Set the color matrix filter.
1845 auto filter = ColorFilterContents::MakeColorMatrix(
1846 FilterInput::Make(bay_bridge), color_matrix);
1847
1848 // Define the entity with the color matrix filter.
1849 Entity entity;
1850 entity.SetTransform(
1851 Matrix::MakeScale(GetContentScale()) *
1852 Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
1853 Matrix::MakeRotationZ(Radians(rotation)) *
1854 Matrix::MakeScale(Vector2(scale[0], scale[1])) *
1855 Matrix::MakeSkew(skew[0], skew[1]) *
1856 Matrix::MakeTranslation(-Point(bay_bridge->GetSize()) / 2));
1857 entity.SetContents(filter);
1858 entity.Render(context, pass);
1859
1860 return true;
1861 };
1862
1863 ASSERT_TRUE(OpenPlaygroundHere(callback));
1864 }
1865
1866 TEST_P(EntityTest, LinearToSrgbFilterCoverageIsCorrect) {
1867 // Set up a simple color background.
1868 auto fill = std::make_shared<SolidColorContents>();
1869 fill->SetGeometry(Geometry::MakeFillPath(
1870 PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
1871 fill->SetColor(Color::MintCream());
1872
1873 auto filter =
1874 ColorFilterContents::MakeLinearToSrgbFilter(FilterInput::Make(fill));
1875
1876 Entity e;
1877 e.SetTransform(Matrix());
1878
1879 // Confirm that the actual filter coverage matches the expected coverage.
1880 auto actual = filter->GetCoverage(e);
1881 auto expected = Rect::MakeXYWH(0, 0, 300, 400);
1882
1883 ASSERT_TRUE(actual.has_value());
1884 ASSERT_RECT_NEAR(actual.value(), expected);
1885 }
1886
1887 TEST_P(EntityTest, LinearToSrgbFilter) {
1888 auto image = CreateTextureForFixture("kalimba.jpg");
1889 ASSERT_TRUE(image);
1890
1891 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
1892 auto filtered =
1893 ColorFilterContents::MakeLinearToSrgbFilter(FilterInput::Make(image));
1894
1895 // Define the entity that will serve as the control image as a Gaussian blur
1896 // filter with no filter at all.
1897 Entity entity_left;
1898 entity_left.SetTransform(Matrix::MakeScale(GetContentScale()) *
1899 Matrix::MakeTranslation({100, 300}) *
1900 Matrix::MakeScale(Vector2{0.5, 0.5}));
1901 auto unfiltered = FilterContents::MakeGaussianBlur(FilterInput::Make(image),
1902 Sigma{0}, Sigma{0});
1903 entity_left.SetContents(unfiltered);
1904
1905 // Define the entity that will be filtered from linear to sRGB.
1906 Entity entity_right;
1907 entity_right.SetTransform(Matrix::MakeScale(GetContentScale()) *
1908 Matrix::MakeTranslation({500, 300}) *
1909 Matrix::MakeScale(Vector2{0.5, 0.5}));
1910 entity_right.SetContents(filtered);
1911 return entity_left.Render(context, pass) &&
1912 entity_right.Render(context, pass);
1913 };
1914
1915 ASSERT_TRUE(OpenPlaygroundHere(callback));
1916 }
1917
1918 TEST_P(EntityTest, SrgbToLinearFilterCoverageIsCorrect) {
1919 // Set up a simple color background.
1920 auto fill = std::make_shared<SolidColorContents>();
1921 fill->SetGeometry(Geometry::MakeFillPath(
1922 PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
1923 fill->SetColor(Color::DeepPink());
1924
1925 auto filter =
1926 ColorFilterContents::MakeSrgbToLinearFilter(FilterInput::Make(fill));
1927
1928 Entity e;
1929 e.SetTransform(Matrix());
1930
1931 // Confirm that the actual filter coverage matches the expected coverage.
1932 auto actual = filter->GetCoverage(e);
1933 auto expected = Rect::MakeXYWH(0, 0, 300, 400);
1934
1935 ASSERT_TRUE(actual.has_value());
1936 ASSERT_RECT_NEAR(actual.value(), expected);
1937 }
1938
1939 TEST_P(EntityTest, SrgbToLinearFilter) {
1940 auto image = CreateTextureForFixture("embarcadero.jpg");
1941 ASSERT_TRUE(image);
1942
1943 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
1944 auto filtered =
1945 ColorFilterContents::MakeSrgbToLinearFilter(FilterInput::Make(image));
1946
1947 // Define the entity that will serve as the control image as a Gaussian blur
1948 // filter with no filter at all.
1949 Entity entity_left;
1950 entity_left.SetTransform(Matrix::MakeScale(GetContentScale()) *
1951 Matrix::MakeTranslation({100, 300}) *
1952 Matrix::MakeScale(Vector2{0.5, 0.5}));
1953 auto unfiltered = FilterContents::MakeGaussianBlur(FilterInput::Make(image),
1954 Sigma{0}, Sigma{0});
1955 entity_left.SetContents(unfiltered);
1956
1957 // Define the entity that will be filtered from sRGB to linear.
1958 Entity entity_right;
1959 entity_right.SetTransform(Matrix::MakeScale(GetContentScale()) *
1960 Matrix::MakeTranslation({500, 300}) *
1961 Matrix::MakeScale(Vector2{0.5, 0.5}));
1962 entity_right.SetContents(filtered);
1963 return entity_left.Render(context, pass) &&
1964 entity_right.Render(context, pass);
1965 };
1966
1967 ASSERT_TRUE(OpenPlaygroundHere(callback));
1968 }
1969
1970 TEST_P(EntityTest, AtlasContentsSubAtlas) {
1971 auto boston = CreateTextureForFixture("boston.jpg");
1972
1973 {
1974 auto contents = std::make_shared<AtlasContents>();
1975 contents->SetBlendMode(BlendMode::kSourceOver);
1976 contents->SetTexture(boston);
1977 contents->SetColors({
1978 Color::Red(),
1979 Color::Red(),
1980 Color::Red(),
1981 });
1982 contents->SetTextureCoordinates({
1983 Rect::MakeLTRB(0, 0, 10, 10),
1984 Rect::MakeLTRB(0, 0, 10, 10),
1985 Rect::MakeLTRB(0, 0, 10, 10),
1986 });
1987 contents->SetTransforms({
1988 Matrix::MakeTranslation(Vector2(0, 0)),
1989 Matrix::MakeTranslation(Vector2(100, 100)),
1990 Matrix::MakeTranslation(Vector2(200, 200)),
1991 });
1992
1993 // Since all colors and sample rects are the same, there should
1994 // only be a single entry in the sub atlas.
1995 auto subatlas = contents->GenerateSubAtlas();
1996 ASSERT_EQ(subatlas->sub_texture_coords.size(), 1u);
1997 }
1998
1999 {
2000 auto contents = std::make_shared<AtlasContents>();
2001 contents->SetBlendMode(BlendMode::kSourceOver);
2002 contents->SetTexture(boston);
2003 contents->SetColors({
2004 Color::Red(),
2005 Color::Green(),
2006 Color::Blue(),
2007 });
2008 contents->SetTextureCoordinates({
2009 Rect::MakeLTRB(0, 0, 10, 10),
2010 Rect::MakeLTRB(0, 0, 10, 10),
2011 Rect::MakeLTRB(0, 0, 10, 10),
2012 });
2013 contents->SetTransforms({
2014 Matrix::MakeTranslation(Vector2(0, 0)),
2015 Matrix::MakeTranslation(Vector2(100, 100)),
2016 Matrix::MakeTranslation(Vector2(200, 200)),
2017 });
2018
2019 // Since all colors are different, there are three entires.
2020 auto subatlas = contents->GenerateSubAtlas();
2021 ASSERT_EQ(subatlas->sub_texture_coords.size(), 3u);
2022
2023 // The translations are kept but the sample rects point into
2024 // different parts of the sub atlas.
2025 ASSERT_EQ(subatlas->result_texture_coords[0], Rect::MakeXYWH(0, 0, 10, 10));
2026 ASSERT_EQ(subatlas->result_texture_coords[1],
2027 Rect::MakeXYWH(11, 0, 10, 10));
2028 ASSERT_EQ(subatlas->result_texture_coords[2],
2029 Rect::MakeXYWH(22, 0, 10, 10));
2030 }
2031 }
2032
2033 static Vector3 RGBToYUV(Vector3 rgb, YUVColorSpace yuv_color_space) {
2034 Vector3 yuv;
2035 switch (yuv_color_space) {
2036 case YUVColorSpace::kBT601FullRange:
2037 yuv.x = rgb.x * 0.299 + rgb.y * 0.587 + rgb.z * 0.114;
2038 yuv.y = rgb.x * -0.169 + rgb.y * -0.331 + rgb.z * 0.5 + 0.5;
2039 yuv.z = rgb.x * 0.5 + rgb.y * -0.419 + rgb.z * -0.081 + 0.5;
2040 break;
2041 case YUVColorSpace::kBT601LimitedRange:
2042 yuv.x = rgb.x * 0.257 + rgb.y * 0.516 + rgb.z * 0.100 + 0.063;
2043 yuv.y = rgb.x * -0.145 + rgb.y * -0.291 + rgb.z * 0.439 + 0.5;
2044 yuv.z = rgb.x * 0.429 + rgb.y * -0.368 + rgb.z * -0.071 + 0.5;
2045 break;
2046 }
2047 return yuv;
2048 }
2049
2050 static std::vector<std::shared_ptr<Texture>> CreateTestYUVTextures(
2051 Context* context,
2052 YUVColorSpace yuv_color_space) {
2053 Vector3 red = {244.0 / 255.0, 67.0 / 255.0, 54.0 / 255.0};
2054 Vector3 green = {76.0 / 255.0, 175.0 / 255.0, 80.0 / 255.0};
2055 Vector3 blue = {33.0 / 255.0, 150.0 / 255.0, 243.0 / 255.0};
2056 Vector3 white = {1.0, 1.0, 1.0};
2057 Vector3 red_yuv = RGBToYUV(red, yuv_color_space);
2058 Vector3 green_yuv = RGBToYUV(green, yuv_color_space);
2059 Vector3 blue_yuv = RGBToYUV(blue, yuv_color_space);
2060 Vector3 white_yuv = RGBToYUV(white, yuv_color_space);
2061 std::vector<Vector3> yuvs{red_yuv, green_yuv, blue_yuv, white_yuv};
2062 std::vector<uint8_t> y_data;
2063 std::vector<uint8_t> uv_data;
2064 for (int i = 0; i < 4; i++) {
2065 auto yuv = yuvs[i];
2066 uint8_t y = std::round(yuv.x * 255.0);
2067 uint8_t u = std::round(yuv.y * 255.0);
2068 uint8_t v = std::round(yuv.z * 255.0);
2069 for (int j = 0; j < 16; j++) {
2070 y_data.push_back(y);
2071 }
2072 for (int j = 0; j < 8; j++) {
2073 uv_data.push_back(j % 2 == 0 ? u : v);
2074 }
2075 }
2076 impeller::TextureDescriptor y_texture_descriptor;
2077 y_texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
2078 y_texture_descriptor.format = PixelFormat::kR8UNormInt;
2079 y_texture_descriptor.size = {8, 8};
2080 auto y_texture =
2081 context->GetResourceAllocator()->CreateTexture(y_texture_descriptor);
2082 auto y_mapping = std::make_shared<fml::DataMapping>(y_data);
2083 if (!y_texture->SetContents(y_mapping)) {
2084 FML_DLOG(ERROR) << "Could not copy contents into Y texture.";
2085 }
2086
2087 impeller::TextureDescriptor uv_texture_descriptor;
2088 uv_texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
2089 uv_texture_descriptor.format = PixelFormat::kR8G8UNormInt;
2090 uv_texture_descriptor.size = {4, 4};
2091 auto uv_texture =
2092 context->GetResourceAllocator()->CreateTexture(uv_texture_descriptor);
2093 auto uv_mapping = std::make_shared<fml::DataMapping>(uv_data);
2094 if (!uv_texture->SetContents(uv_mapping)) {
2095 FML_DLOG(ERROR) << "Could not copy contents into UV texture.";
2096 }
2097
2098 return {y_texture, uv_texture};
2099 }
2100
2101 TEST_P(EntityTest, YUVToRGBFilter) {
2102 if (GetParam() == PlaygroundBackend::kOpenGLES) {
2103 // TODO(114588) : Support YUV to RGB filter on OpenGLES backend.
2104 GTEST_SKIP_("YUV to RGB filter is not supported on OpenGLES backend yet.");
2105 }
2106
2107 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2108 YUVColorSpace yuv_color_space_array[2]{YUVColorSpace::kBT601FullRange,
2109 YUVColorSpace::kBT601LimitedRange};
2110 for (int i = 0; i < 2; i++) {
2111 auto yuv_color_space = yuv_color_space_array[i];
2112 auto textures =
2113 CreateTestYUVTextures(GetContext().get(), yuv_color_space);
2114 auto filter_contents = FilterContents::MakeYUVToRGBFilter(
2115 textures[0], textures[1], yuv_color_space);
2116 Entity filter_entity;
2117 filter_entity.SetContents(filter_contents);
2118 auto snapshot = filter_contents->RenderToSnapshot(context, filter_entity);
2119
2120 Entity entity;
2121 auto contents = TextureContents::MakeRect(Rect::MakeLTRB(0, 0, 256, 256));
2122 contents->SetTexture(snapshot->texture);
2123 contents->SetSourceRect(Rect::MakeSize(snapshot->texture->GetSize()));
2124 entity.SetContents(contents);
2125 entity.SetTransform(
2126 Matrix::MakeTranslation({static_cast<Scalar>(100 + 400 * i), 300}));
2127 entity.Render(context, pass);
2128 }
2129 return true;
2130 };
2131 ASSERT_TRUE(OpenPlaygroundHere(callback));
2132 }
2133
2134 TEST_P(EntityTest, RuntimeEffect) {
2135 if (!BackendSupportsFragmentProgram()) {
2136 GTEST_SKIP_("This backend doesn't support runtime effects.");
2137 }
2138
2139 auto runtime_stages =
2140 OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
2141 auto runtime_stage =
2142 runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2143 ASSERT_TRUE(runtime_stage);
2144 ASSERT_TRUE(runtime_stage->IsDirty());
2145
2146 bool first_frame = true;
2147 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2148 if (first_frame) {
2149 first_frame = false;
2150 } else {
2151 assert(runtime_stage->IsDirty() == false);
2152 }
2153
2154 auto contents = std::make_shared<RuntimeEffectContents>();
2155 contents->SetGeometry(Geometry::MakeCover());
2156
2157 contents->SetRuntimeStage(runtime_stage);
2158
2159 struct FragUniforms {
2160 Vector2 iResolution;
2161 Scalar iTime;
2162 } frag_uniforms = {
2163 .iResolution = Vector2(GetWindowSize().width, GetWindowSize().height),
2164 .iTime = static_cast<Scalar>(GetSecondsElapsed()),
2165 };
2166 auto uniform_data = std::make_shared<std::vector<uint8_t>>();
2167 uniform_data->resize(sizeof(FragUniforms));
2168 memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
2169 contents->SetUniformData(uniform_data);
2170
2171 Entity entity;
2172 entity.SetContents(contents);
2173 return contents->Render(context, entity, pass);
2174 };
2175 ASSERT_TRUE(OpenPlaygroundHere(callback));
2176 }
2177
2178 TEST_P(EntityTest, InheritOpacityTest) {
2179 Entity entity;
2180
2181 // Texture contents can always accept opacity.
2182 auto texture_contents = std::make_shared<TextureContents>();
2183 texture_contents->SetOpacity(0.5);
2184 ASSERT_TRUE(texture_contents->CanInheritOpacity(entity));
2185
2186 texture_contents->SetInheritedOpacity(0.5);
2187 ASSERT_EQ(texture_contents->GetOpacity(), 0.25);
2188 texture_contents->SetInheritedOpacity(0.5);
2189 ASSERT_EQ(texture_contents->GetOpacity(), 0.25);
2190
2191 // Solid color contents can accept opacity if their geometry
2192 // doesn't overlap.
2193 auto solid_color = std::make_shared<SolidColorContents>();
2194 solid_color->SetGeometry(
2195 Geometry::MakeRect(Rect::MakeLTRB(100, 100, 200, 200)));
2196 solid_color->SetColor(Color::Blue().WithAlpha(0.5));
2197
2198 ASSERT_TRUE(solid_color->CanInheritOpacity(entity));
2199
2200 solid_color->SetInheritedOpacity(0.5);
2201 ASSERT_EQ(solid_color->GetColor().alpha, 0.25);
2202 solid_color->SetInheritedOpacity(0.5);
2203 ASSERT_EQ(solid_color->GetColor().alpha, 0.25);
2204
2205 // Color source contents can accept opacity if their geometry
2206 // doesn't overlap.
2207 auto tiled_texture = std::make_shared<TiledTextureContents>();
2208 tiled_texture->SetGeometry(
2209 Geometry::MakeRect(Rect::MakeLTRB(100, 100, 200, 200)));
2210 tiled_texture->SetOpacityFactor(0.5);
2211
2212 ASSERT_TRUE(tiled_texture->CanInheritOpacity(entity));
2213
2214 tiled_texture->SetInheritedOpacity(0.5);
2215 ASSERT_EQ(tiled_texture->GetOpacityFactor(), 0.25);
2216 tiled_texture->SetInheritedOpacity(0.5);
2217 ASSERT_EQ(tiled_texture->GetOpacityFactor(), 0.25);
2218
2219 // Text contents can accept opacity if the text frames do not
2220 // overlap
2221 SkFont font = flutter::testing::CreateTestFontOfSize(30);
2222 auto blob = SkTextBlob::MakeFromString("A", font);
2223 auto frame = MakeTextFrameFromTextBlobSkia(blob);
2224 auto lazy_glyph_atlas =
2225 std::make_shared<LazyGlyphAtlas>(TypographerContextSkia::Make());
2226 lazy_glyph_atlas->AddTextFrame(*frame, 1.0f);
2227
2228 auto text_contents = std::make_shared<TextContents>();
2229 text_contents->SetTextFrame(frame);
2230 text_contents->SetColor(Color::Blue().WithAlpha(0.5));
2231
2232 ASSERT_TRUE(text_contents->CanInheritOpacity(entity));
2233
2234 text_contents->SetInheritedOpacity(0.5);
2235 ASSERT_EQ(text_contents->GetColor().alpha, 0.25);
2236 text_contents->SetInheritedOpacity(0.5);
2237 ASSERT_EQ(text_contents->GetColor().alpha, 0.25);
2238
2239 // Clips and restores trivially accept opacity.
2240 ASSERT_TRUE(ClipContents().CanInheritOpacity(entity));
2241 ASSERT_TRUE(ClipRestoreContents().CanInheritOpacity(entity));
2242
2243 // Runtime effect contents can't accept opacity.
2244 auto runtime_effect = std::make_shared<RuntimeEffectContents>();
2245 ASSERT_FALSE(runtime_effect->CanInheritOpacity(entity));
2246 }
2247
2248 TEST_P(EntityTest, ColorFilterWithForegroundColorAdvancedBlend) {
2249 auto image = CreateTextureForFixture("boston.jpg");
2250 auto filter = ColorFilterContents::MakeBlend(
2251 BlendMode::kColorBurn, FilterInput::Make({image}), Color::Red());
2252
2253 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2254 Entity entity;
2255 entity.SetTransform(Matrix::MakeScale(GetContentScale()) *
2256 Matrix::MakeTranslation({500, 300}) *
2257 Matrix::MakeScale(Vector2{0.5, 0.5}));
2258 entity.SetContents(filter);
2259 return entity.Render(context, pass);
2260 };
2261 ASSERT_TRUE(OpenPlaygroundHere(callback));
2262 }
2263
2264 TEST_P(EntityTest, ColorFilterWithForegroundColorClearBlend) {
2265 auto image = CreateTextureForFixture("boston.jpg");
2266 auto filter = ColorFilterContents::MakeBlend(
2267 BlendMode::kClear, FilterInput::Make({image}), Color::Red());
2268
2269 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2270 Entity entity;
2271 entity.SetTransform(Matrix::MakeScale(GetContentScale()) *
2272 Matrix::MakeTranslation({500, 300}) *
2273 Matrix::MakeScale(Vector2{0.5, 0.5}));
2274 entity.SetContents(filter);
2275 return entity.Render(context, pass);
2276 };
2277 ASSERT_TRUE(OpenPlaygroundHere(callback));
2278 }
2279
2280 TEST_P(EntityTest, ColorFilterWithForegroundColorSrcBlend) {
2281 auto image = CreateTextureForFixture("boston.jpg");
2282 auto filter = ColorFilterContents::MakeBlend(
2283 BlendMode::kSource, FilterInput::Make({image}), Color::Red());
2284
2285 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2286 Entity entity;
2287 entity.SetTransform(Matrix::MakeScale(GetContentScale()) *
2288 Matrix::MakeTranslation({500, 300}) *
2289 Matrix::MakeScale(Vector2{0.5, 0.5}));
2290 entity.SetContents(filter);
2291 return entity.Render(context, pass);
2292 };
2293 ASSERT_TRUE(OpenPlaygroundHere(callback));
2294 }
2295
2296 TEST_P(EntityTest, ColorFilterWithForegroundColorDstBlend) {
2297 auto image = CreateTextureForFixture("boston.jpg");
2298 auto filter = ColorFilterContents::MakeBlend(
2299 BlendMode::kDestination, FilterInput::Make({image}), Color::Red());
2300
2301 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2302 Entity entity;
2303 entity.SetTransform(Matrix::MakeScale(GetContentScale()) *
2304 Matrix::MakeTranslation({500, 300}) *
2305 Matrix::MakeScale(Vector2{0.5, 0.5}));
2306 entity.SetContents(filter);
2307 return entity.Render(context, pass);
2308 };
2309 ASSERT_TRUE(OpenPlaygroundHere(callback));
2310 }
2311
2312 TEST_P(EntityTest, ColorFilterWithForegroundColorSrcInBlend) {
2313 auto image = CreateTextureForFixture("boston.jpg");
2314 auto filter = ColorFilterContents::MakeBlend(
2315 BlendMode::kSourceIn, FilterInput::Make({image}), Color::Red());
2316
2317 auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2318 Entity entity;
2319 entity.SetTransform(Matrix::MakeScale(GetContentScale()) *
2320 Matrix::MakeTranslation({500, 300}) *
2321 Matrix::MakeScale(Vector2{0.5, 0.5}));
2322 entity.SetContents(filter);
2323 return entity.Render(context, pass);
2324 };
2325 ASSERT_TRUE(OpenPlaygroundHere(callback));
2326 }
2327
2328 TEST_P(EntityTest, CoverageForStrokePathWithNegativeValuesInTransform) {
2329 auto arrow_head = PathBuilder{}
2330 .MoveTo({50, 120})
2331 .LineTo({120, 190})
2332 .LineTo({190, 120})
2333 .TakePath();
2334 auto geometry = Geometry::MakeStrokePath(std::move(arrow_head), 15.0, 4.0,
2335 Cap::kRound, Join::kRound);
2336
2337 auto transform = Matrix::MakeTranslation({300, 300}) *
2338 Matrix::MakeRotationZ(Radians(kPiOver2));
2339 EXPECT_LT(transform.e[0][0], 0.f);
2340 auto coverage = geometry->GetCoverage(transform);
2341 ASSERT_RECT_NEAR(coverage.value(), Rect::MakeXYWH(102.5, 342.5, 85, 155));
2342 }
2343
2344 TEST_P(EntityTest, SolidColorContentsIsOpaque) {
2345 SolidColorContents contents;
2346 contents.SetColor(Color::CornflowerBlue());
2347 ASSERT_TRUE(contents.IsOpaque());
2348 contents.SetColor(Color::CornflowerBlue().WithAlpha(0.5));
2349 ASSERT_FALSE(contents.IsOpaque());
2350 }
2351
2352 TEST_P(EntityTest, ConicalGradientContentsIsOpaque) {
2353 ConicalGradientContents contents;
2354 contents.SetColors({Color::CornflowerBlue()});
2355 ASSERT_FALSE(contents.IsOpaque());
2356 contents.SetColors({Color::CornflowerBlue().WithAlpha(0.5)});
2357 ASSERT_FALSE(contents.IsOpaque());
2358 }
2359
2360 TEST_P(EntityTest, LinearGradientContentsIsOpaque) {
2361 LinearGradientContents contents;
2362 contents.SetColors({Color::CornflowerBlue()});
2363 ASSERT_TRUE(contents.IsOpaque());
2364 contents.SetColors({Color::CornflowerBlue().WithAlpha(0.5)});
2365 ASSERT_FALSE(contents.IsOpaque());
2366 contents.SetColors({Color::CornflowerBlue()});
2367 contents.SetTileMode(Entity::TileMode::kDecal);
2368 ASSERT_FALSE(contents.IsOpaque());
2369 }
2370
2371 TEST_P(EntityTest, RadialGradientContentsIsOpaque) {
2372 RadialGradientContents contents;
2373 contents.SetColors({Color::CornflowerBlue()});
2374 ASSERT_TRUE(contents.IsOpaque());
2375 contents.SetColors({Color::CornflowerBlue().WithAlpha(0.5)});
2376 ASSERT_FALSE(contents.IsOpaque());
2377 contents.SetColors({Color::CornflowerBlue()});
2378 contents.SetTileMode(Entity::TileMode::kDecal);
2379 ASSERT_FALSE(contents.IsOpaque());
2380 }
2381
2382 TEST_P(EntityTest, SweepGradientContentsIsOpaque) {
2383 RadialGradientContents contents;
2384 contents.SetColors({Color::CornflowerBlue()});
2385 ASSERT_TRUE(contents.IsOpaque());
2386 contents.SetColors({Color::CornflowerBlue().WithAlpha(0.5)});
2387 ASSERT_FALSE(contents.IsOpaque());
2388 contents.SetColors({Color::CornflowerBlue()});
2389 contents.SetTileMode(Entity::TileMode::kDecal);
2390 ASSERT_FALSE(contents.IsOpaque());
2391 }
2392
2393 TEST_P(EntityTest, TiledTextureContentsIsOpaque) {
2394 auto bay_bridge = CreateTextureForFixture("bay_bridge.jpg");
2395 TiledTextureContents contents;
2396 contents.SetTexture(bay_bridge);
2397 // This is a placeholder test. Images currently never decompress as opaque
2398 // (whether in Flutter or the playground), and so this should currently always
2399 // return false in practice.
2400 ASSERT_FALSE(contents.IsOpaque());
2401 }
2402
2403 TEST_P(EntityTest, PointFieldGeometryDivisions) {
2404 // Square always gives 4 divisions.
2405 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(24.0, false), 4u);
2406 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(2.0, false), 4u);
2407 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(200.0, false), 4u);
2408
2409 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(0.5, true), 4u);
2410 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(1.5, true), 8u);
2411 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(5.5, true), 24u);
2412 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(12.5, true), 34u);
2413 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(22.3, true), 22u);
2414 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(40.5, true), 40u);
2415 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(100.0, true), 100u);
2416 // Caps at 140.
2417 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(1000.0, true), 140u);
2418 ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(20000.0, true), 140u);
2419 }
2420
2421 TEST_P(EntityTest, PointFieldGeometryCoverage) {
2422 std::vector<Point> points = {{10, 20}, {100, 200}};
2423 auto geometry = Geometry::MakePointField(points, 5.0, false);
2424 ASSERT_EQ(*geometry->GetCoverage(Matrix()), Rect::MakeLTRB(5, 15, 105, 205));
2425 ASSERT_EQ(*geometry->GetCoverage(Matrix::MakeTranslation({30, 0, 0})),
2426 Rect::MakeLTRB(35, 15, 135, 205));
2427 }
2428
2429 TEST_P(EntityTest, PointFieldCanUseCompute) {
2430 EXPECT_EQ(PointFieldGeometry::CanUseCompute(*GetContentContext()),
2431 GetContext()->GetBackendType() == Context::BackendType::kMetal);
2432 }
2433
2434 TEST_P(EntityTest, ColorFilterContentsWithLargeGeometry) {
2435 Entity entity;
2436 entity.SetTransform(Matrix::MakeScale(GetContentScale()));
2437 auto src_contents = std::make_shared<SolidColorContents>();
2438 src_contents->SetGeometry(
2439 Geometry::MakeRect(Rect::MakeLTRB(-300, -500, 30000, 50000)));
2440 src_contents->SetColor(Color::Red());
2441
2442 auto dst_contents = std::make_shared<SolidColorContents>();
2443 dst_contents->SetGeometry(
2444 Geometry::MakeRect(Rect::MakeLTRB(300, 500, 20000, 30000)));
2445 dst_contents->SetColor(Color::Blue());
2446
2447 auto contents = ColorFilterContents::MakeBlend(
2448 BlendMode::kSourceOver, {FilterInput::Make(dst_contents, false),
2449 FilterInput::Make(src_contents, false)});
2450 entity.SetContents(std::move(contents));
2451 ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
2452 }
2453
2454 TEST_P(EntityTest, TextContentsCeilsGlyphScaleToDecimal) {
2455 ASSERT_EQ(TextFrame::RoundScaledFontSize(0.4321111f, 12), 0.43f);
2456 ASSERT_EQ(TextFrame::RoundScaledFontSize(0.5321111f, 12), 0.53f);
2457 ASSERT_EQ(TextFrame::RoundScaledFontSize(2.1f, 12), 2.1f);
2458 ASSERT_EQ(TextFrame::RoundScaledFontSize(0.0f, 12), 0.0f);
2459 }
2460
2461 class TestRenderTargetAllocator : public RenderTargetAllocator {
2462 public:
2463 explicit TestRenderTargetAllocator(std::shared_ptr<Allocator> allocator)
2464 : RenderTargetAllocator(std::move(allocator)) {}
2465
2466 ~TestRenderTargetAllocator() = default;
2467
2468 std::shared_ptr<Texture> CreateTexture(
2469 const TextureDescriptor& desc) override {
2470 allocated_.push_back(desc);
2471 return RenderTargetAllocator::CreateTexture(desc);
2472 }
2473
2474 void Start() override { RenderTargetAllocator::Start(); }
2475
2476 void End() override { RenderTargetAllocator::End(); }
2477
2478 std::vector<TextureDescriptor> GetDescriptors() const { return allocated_; }
2479
2480 private:
2481 std::vector<TextureDescriptor> allocated_;
2482 };
2483
2484 TEST_P(EntityTest, AdvancedBlendCoverageHintIsNotResetByEntityPass) {
2485 if (GetContext()->GetCapabilities()->SupportsFramebufferFetch()) {
2486 GTEST_SKIP() << "Backends that support framebuffer fetch dont use coverage "
2487 "for advanced blends.";
2488 }
2489
2490 auto contents = std::make_shared<SolidColorContents>();
2491 contents->SetGeometry(Geometry::MakeRect(Rect::MakeXYWH(100, 100, 100, 100)));
2492 contents->SetColor(Color::Red());
2493
2494 Entity entity;
2495 entity.SetTransform(Matrix::MakeScale(Vector3(2, 2, 1)));
2496 entity.SetBlendMode(BlendMode::kColorBurn);
2497 entity.SetContents(contents);
2498
2499 auto coverage = entity.GetCoverage();
2500 EXPECT_TRUE(coverage.has_value());
2501
2502 auto pass = std::make_unique<EntityPass>();
2503 auto test_allocator = std::make_shared<TestRenderTargetAllocator>(
2504 GetContext()->GetResourceAllocator());
2505 auto stencil_config = RenderTarget::AttachmentConfig{
2506 .storage_mode = StorageMode::kDevicePrivate,
2507 .load_action = LoadAction::kClear,
2508 .store_action = StoreAction::kDontCare,
2509 .clear_color = Color::BlackTransparent()};
2510 auto rt = RenderTarget::CreateOffscreen(
2511 *GetContext(), *test_allocator, ISize::MakeWH(1000, 1000), "Offscreen",
2512 RenderTarget::kDefaultColorAttachmentConfig, stencil_config);
2513 auto content_context = ContentContext(
2514 GetContext(), TypographerContextSkia::Make(), test_allocator);
2515 pass->AddEntity(std::move(entity));
2516
2517 EXPECT_TRUE(pass->Render(content_context, rt));
2518
2519 if (test_allocator->GetDescriptors().size() == 6u) {
2520 EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000));
2521 EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000));
2522
2523 EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(200, 200));
2524 EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(200, 200));
2525 EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(200, 200));
2526 EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200));
2527 } else if (test_allocator->GetDescriptors().size() == 9u) {
2528 // Onscreen render target.
2529 EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000));
2530 EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000));
2531 EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(1000, 1000));
2532 EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(1000, 1000));
2533 EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(1000, 1000));
2534
2535 EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200));
2536 EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200));
2537 EXPECT_EQ(test_allocator->GetDescriptors()[6].size, ISize(200, 200));
2538 EXPECT_EQ(test_allocator->GetDescriptors()[7].size, ISize(200, 200));
2539 } else {
2540 EXPECT_TRUE(false);
2541 }
2542 }
2543
2544 TEST_P(EntityTest, SpecializationConstantsAreAppliedToVariants) {
2545 auto content_context =
2546 ContentContext(GetContext(), TypographerContextSkia::Make());
2547
2548 auto default_color_burn = content_context.GetBlendColorBurnPipeline(
2549 {.has_stencil_attachment = false});
2550 auto alt_color_burn = content_context.GetBlendColorBurnPipeline(
2551 {.has_stencil_attachment = true});
2552
2553 ASSERT_NE(default_color_burn, alt_color_burn);
2554 ASSERT_EQ(default_color_burn->GetDescriptor().GetSpecializationConstants(),
2555 alt_color_burn->GetDescriptor().GetSpecializationConstants());
2556
2557 auto decal_supported = static_cast<Scalar>(
2558 GetContext()->GetCapabilities()->SupportsDecalSamplerAddressMode());
2559 std::vector<Scalar> expected_constants = {5, decal_supported};
2560 ASSERT_EQ(default_color_burn->GetDescriptor().GetSpecializationConstants(),
2561 expected_constants);
2562 }
2563
2564 TEST_P(EntityTest, DecalSpecializationAppliedToMorphologyFilter) {
2565 auto content_context =
2566 ContentContext(GetContext(), TypographerContextSkia::Make());
2567
2568 auto default_color_burn = content_context.GetMorphologyFilterPipeline({});
2569
2570 auto decal_supported = static_cast<Scalar>(
2571 GetContext()->GetCapabilities()->SupportsDecalSamplerAddressMode());
2572 std::vector<Scalar> expected_constants = {decal_supported};
2573 ASSERT_EQ(default_color_burn->GetDescriptor().GetSpecializationConstants(),
2574 expected_constants);
2575 }
2576
2577 TEST_P(EntityTest, FramebufferFetchPipelinesDeclareUsage) {
2578 auto content_context =
2579 ContentContext(GetContext(), TypographerContextSkia::Make());
2580 if (!content_context.GetDeviceCapabilities().SupportsFramebufferFetch()) {
2581 GTEST_SKIP() << "Framebuffer fetch not supported.";
2582 }
2583
2584 ContentContextOptions options;
2585 options.color_attachment_pixel_format = PixelFormat::kR8G8B8A8UNormInt;
2586 auto color_burn =
2587 content_context.GetFramebufferBlendColorBurnPipeline(options);
2588
2589 EXPECT_TRUE(color_burn->GetDescriptor().UsesSubpassInput());
2590 }
2591
2592 TEST_P(EntityTest, PipelineDescriptorEqAndHash) {
2593 auto desc_1 = std::make_shared<PipelineDescriptor>();
2594 auto desc_2 = std::make_shared<PipelineDescriptor>();
2595
2596 EXPECT_TRUE(desc_1->IsEqual(*desc_2));
2597 EXPECT_EQ(desc_1->GetHash(), desc_2->GetHash());
2598
2599 desc_1->SetUseSubpassInput(UseSubpassInput::kYes);
2600
2601 EXPECT_FALSE(desc_1->IsEqual(*desc_2));
2602 EXPECT_NE(desc_1->GetHash(), desc_2->GetHash());
2603
2604 desc_2->SetUseSubpassInput(UseSubpassInput::kYes);
2605
2606 EXPECT_TRUE(desc_1->IsEqual(*desc_2));
2607 EXPECT_EQ(desc_1->GetHash(), desc_2->GetHash());
2608 }
2609
2610 // This doesn't really tell you if the hashes will have frequent
2611 // collisions, but since this type is only used to hash a bounded
2612 // set of options, we can just compare benchmarks.
2613 TEST_P(EntityTest, ContentContextOptionsHasReasonableHashFunctions) {
2614 ContentContextOptions opts;
2615 auto hash_a = ContentContextOptions::Hash{}(opts);
2616
2617 opts.blend_mode = BlendMode::kColorBurn;
2618 auto hash_b = ContentContextOptions::Hash{}(opts);
2619
2620 opts.has_stencil_attachment = false;
2621 auto hash_c = ContentContextOptions::Hash{}(opts);
2622
2623 opts.primitive_type = PrimitiveType::kPoint;
2624 auto hash_d = ContentContextOptions::Hash{}(opts);
2625
2626 EXPECT_NE(hash_a, hash_b);
2627 EXPECT_NE(hash_b, hash_c);
2628 EXPECT_NE(hash_c, hash_d);
2629 }
2630
2631 #ifdef FML_OS_LINUX
2632 TEST_P(EntityTest, FramebufferFetchVulkanBindingOffsetIsTheSame) {
2633 // Using framebuffer fetch on Vulkan requires that we maintain a subpass input
2634 // binding that we don't have a good route for configuring with the current
2635 // metadata approach. This test verifies that the binding value doesn't change
2636 // from the expected constant.
2637 // See also:
2638 // * impeller/renderer/backend/vulkan/binding_helpers_vk.cc
2639 // * impeller/entity/shaders/blending/framebuffer_blend.frag
2640 // This test only works on Linux because macOS hosts incorrectly populate the
2641 // Vulkan descriptor sets based on the MSL compiler settings.
2642
2643 bool expected_layout = false;
2644 for (const DescriptorSetLayout& layout : FramebufferBlendColorBurnPipeline::
2645 FragmentShader::kDescriptorSetLayouts) {
2646 if (layout.binding == 64 &&
2647 layout.descriptor_type == DescriptorType::kInputAttachment) {
2648 expected_layout = true;
2649 }
2650 }
2651 EXPECT_TRUE(expected_layout);
2652 }
2653 #endif
2654
2655 } // namespace testing
2656 } // namespace impeller
2657
2658 // NOLINTEND(bugprone-unchecked-optional-access)
text_contents.h
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:117
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:268
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
impeller::TiledTextureContents::IsOpaque
bool IsOpaque() const override
Whether this Contents only emits opaque source colors from the fragment stage. This value does not ac...
Definition: tiled_texture_contents.cc:101
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:20
impeller::Command
An object used to specify work to the GPU along with references to resources the GPU will used when d...
Definition: command.h:92
impeller::Color::DeepPink
static constexpr Color DeepPink()
Definition: color.h:426
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:28
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:182
impeller::testing::TestPassDelegate
Definition: entity_unittests.cc:71
impeller::Cap::kRound
@ kRound
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:23
impeller::SolidColorContents::IsOpaque
bool IsOpaque() const override
Whether this Contents only emits opaque source colors from the fragment stage. This value does not ac...
Definition: solid_color_contents.cc:31
impeller::DescriptorSetLayout
Definition: shader_types.h:162
impeller::Cap::kSquare
@ kSquare
impeller::BlendMode::kDestinationATop
@ kDestinationATop
contents.h
point.h
impeller::FilterContents::MorphType::kErode
@ kErode
entity_pass_delegate.h
impeller::Geometry::MakePointField
static std::shared_ptr< Geometry > MakePointField(std::vector< Point > points, Scalar radius, bool round)
Definition: geometry.cc:156
impeller::TPoint::y
Type y
Definition: point.h:26
impeller::Entity::ClipOperation::kDifference
@ kDifference
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::EntityPass::GetSubpassCoverage
std::optional< Rect > GetSubpassCoverage(const EntityPass &subpass, std::optional< Rect > coverage_limit) const
Computes the coverage of a given subpass. This is used to determine the texture size of a given subpa...
Definition: entity_pass.cc:173
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:101
impeller::RenderTarget::kDefaultColorAttachmentConfig
static constexpr AttachmentConfig kDefaultColorAttachmentConfig
Definition: render_target.h:66
texture_contents.h
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:49
geometry_asserts.h
stroke_path_geometry.h
entity.h
impeller::RenderTargetAllocator::Start
virtual void Start()
Mark the beginning of a frame workload.
Definition: render_target.cc:21
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:34
impeller::PixelFormat::kR8UNormInt
@ kR8UNormInt
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::FilterContents::BlurStyle
BlurStyle
Definition: filter_contents.h:23
solid_color_contents.h
impeller::Color
Definition: color.h:124
impeller::FilterInput::Make
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
impeller::ConicalGradientContents::SetColors
void SetColors(std::vector< Color > colors)
Definition: conical_gradient_contents.cc:30
point_field_geometry.h
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:40
impeller::testing::TestRenderTargetAllocator::TestRenderTargetAllocator
TestRenderTargetAllocator(std::shared_ptr< Allocator > allocator)
Definition: entity_unittests.cc:2463
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::BlendMode::kSource
@ kSource
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::EntityPass::AddSubpass
EntityPass * AddSubpass(std::unique_ptr< EntityPass > pass)
Appends a given pass as a subpass.
Definition: entity_pass.cc:205
impeller::BlendMode::kDestination
@ kDestination
texture_descriptor.h
impeller::testing::TestPassDelegate::TestPassDelegate
TestPassDelegate(bool collapse=false)
Definition: entity_unittests.cc:73
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:264
formats.h
impeller::PathBuilder
Definition: path_builder.h:14
impeller::ContentContextOptions::Hash
Definition: content_context.h:287
tiled_texture_contents.h
impeller::Vector2
Point Vector2
Definition: point.h:312
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:195
impeller::kPi
constexpr float kPi
Definition: constants.h:26
impeller::Color::alpha
Scalar alpha
Definition: color.h:143
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kPlus
@ kPlus
impeller::StoreAction::kDontCare
@ kDontCare
impeller::Color::MintCream
static constexpr Color MintCream()
Definition: color.h:650
playground.h
impeller::ContentContextOptions::blend_mode
BlendMode blend_mode
Definition: content_context.h:278
IMPELLER_PLAYGROUND_POINT
#define IMPELLER_PLAYGROUND_POINT(default_position, radius, color)
Definition: widgets.h:15
impeller::Color::CornflowerBlue
static constexpr Color CornflowerBlue()
Definition: color.h:334
impeller::FilterContents::MakeDirectionalGaussianBlur
static std::shared_ptr< FilterContents > MakeDirectionalGaussianBlur(FilterInput::Ref input, Sigma sigma, Vector2 direction, BlurStyle blur_style=BlurStyle::kNormal, Entity::TileMode tile_mode=Entity::TileMode::kDecal, bool is_second_pass=false, Sigma secondary_sigma={})
Definition: filter_contents.cc:34
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:70
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:149
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:834
gaussian_blur_filter_contents.h
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:84
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::EntityPass::GetElementsCoverage
std::optional< Rect > GetElementsCoverage(std::optional< Rect > coverage_limit) const
Definition: entity_pass.cc:97
impeller::Cap::kButt
@ kButt
impeller::TextFrame::RoundScaledFontSize
static Scalar RoundScaledFontSize(Scalar scale, Scalar point_size)
Definition: text_frame.cc:66
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::EntityPassDelegate
Definition: entity_pass_delegate.h:20
impeller::RadialGradientContents::IsOpaque
bool IsOpaque() const override
Whether this Contents only emits opaque source colors from the fragment stage. This value does not ac...
Definition: radial_gradient_contents.cc:46
impeller::PlaygroundBackendToRuntimeStageBackend
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:35
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:18
impeller::BlendMode::kModulate
@ kModulate
impeller::Geometry::MakeStrokePath
static std::shared_ptr< Geometry > MakeStrokePath(Path path, Scalar stroke_width=0.0, Scalar miter_limit=4.0, Cap stroke_cap=Cap::kButt, Join stroke_join=Join::kMiter)
Definition: geometry.cc:162
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:116
impeller::Join::kMiter
@ kMiter
runtime_effect_contents.h
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::BlendMode::kSourceOut
@ kSourceOut
typographer_context_skia.h
impeller::RenderTarget::AttachmentConfig
Definition: render_target.h:51
ASSERT_RECT_NEAR
#define ASSERT_RECT_NEAR(a, b)
Definition: geometry_asserts.h:158
impeller::testing::TestRenderTargetAllocator
Definition: entity_unittests.cc:2461
impeller::Entity::TileMode::kMirror
@ kMirror
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:81
path_builder.h
impeller::LinearGradientContents::SetColors
void SetColors(std::vector< Color > colors)
Definition: linear_gradient_contents.cc:25
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
impeller::StrokePathGeometry
A geometry that is created from a stroked path object.
Definition: stroke_path_geometry.h:13
impeller::PrimitiveType::kTriangle
@ kTriangle
impeller::LinearGradientContents::SetTileMode
void SetTileMode(Entity::TileMode tile_mode)
Definition: linear_gradient_contents.cc:41
impeller::Entity
Definition: entity.h:21
command.h
impeller::ConicalGradientContents
Definition: conical_gradient_contents.h:22
text_frame_skia.h
impeller::TSize< Scalar >
impeller::LoadAction::kClear
@ kClear
impeller::Entity::ShouldRender
bool ShouldRender(const std::optional< Rect > &clip_coverage) const
Definition: entity.cc:73
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::Point
TPoint< Scalar > Point
Definition: point.h:308
impeller::FilterContents::MakeGaussianBlur
static std::shared_ptr< FilterContents > MakeGaussianBlur(const FilterInput::Ref &input, Sigma sigma_x, Sigma sigma_y, BlurStyle blur_style=BlurStyle::kNormal, Entity::TileMode tile_mode=Entity::TileMode::kDecal)
Definition: filter_contents.cc:53
impeller::ContentContextOptions::color_attachment_pixel_format
PixelFormat color_attachment_pixel_format
Definition: content_context.h:282
filter_contents.h
render_pass.h
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::testing::RGBToYUV
static Vector3 RGBToYUV(Vector3 rgb, YUVColorSpace yuv_color_space)
Definition: entity_unittests.cc:2033
impeller::Entity::Render
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:159
impeller::testing::TestPassDelegate::WithImageFilter
std::shared_ptr< FilterContents > WithImageFilter(const FilterInput::Variant &input, const Matrix &effect_transform) const override
Definition: entity_unittests.cc:94
sigma.h
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
impeller::FilterContents::BlurStyle::kSolid
@ kSolid
Solid inside, blurred outside.
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:55
widgets.h
impeller::PrimitiveType::kPoint
@ kPoint
Draws a point at each input vertex.
impeller::PixelFormat::kR8G8UNormInt
@ kR8G8UNormInt
conical_gradient_contents.h
impeller::BlendMode::kClear
@ kClear
impeller::EntityPass
Definition: entity_pass.h:25
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:24
impeller::YUVColorSpace::kBT601FullRange
@ kBT601FullRange
impeller::testing::TestPassDelegate::~TestPassDelegate
~TestPassDelegate() override=default
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::PointFieldGeometry::CanUseCompute
static bool CanUseCompute(const ContentContext &renderer)
If the platform can use compute safely.
Definition: point_field_geometry.cc:277
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:270
impeller::SolidColorContents
Definition: solid_color_contents.h:25
impeller::testing::EntityTest
EntityPlayground EntityTest
Definition: checkerboard_contents_unittests.cc:19
impeller::testing::TestRenderTargetAllocator::~TestRenderTargetAllocator
~TestRenderTargetAllocator()=default
impeller::ColorFilterContents::MakeSrgbToLinearFilter
static std::shared_ptr< ColorFilterContents > MakeSrgbToLinearFilter(FilterInput::Ref input)
Definition: color_filter_contents.cc:75
impeller::ColorMatrix::array
Scalar array[20]
Definition: color.h:118
geometry.h
impeller::Command::BindVertices
bool BindVertices(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: command.cc:15
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:51
impeller::ContentContext::GetSolidFillPipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetSolidFillPipeline(ContentContextOptions opts) const
Definition: content_context.h:405
impeller::PathBuilder::AddCubicCurve
PathBuilder & AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
Definition: path_builder.cc:107
impeller::Color::White
static constexpr Color White()
Definition: color.h:256
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Radians
Definition: scalar.h:38
impeller::RenderTarget::AttachmentConfig::storage_mode
StorageMode storage_mode
Definition: render_target.h:52
impeller::RenderTargetAllocator::CreateTexture
virtual std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Create a new render target texture, or recycle a previously allocated render target texture.
Definition: render_target.cc:25
impeller::PathBuilder::AddLine
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
Definition: path_builder.cc:370
clip_contents.h
impeller::FilterInput::Variant
std::variant< std::shared_ptr< FilterContents >, std::shared_ptr< Contents >, std::shared_ptr< Texture >, Rect > Variant
Definition: filter_input.h:37
impeller::testing::CreateTestYUVTextures
static std::vector< std::shared_ptr< Texture > > CreateTestYUVTextures(Context *context, YUVColorSpace yuv_color_space)
Definition: entity_unittests.cc:2050
entity_pass.h
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:266
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
filter_input.h
impeller::ContentContextOptions::has_stencil_attachment
bool has_stencil_attachment
Definition: content_context.h:283
impeller::Join::kRound
@ kRound
impeller::FilterContents::MakeBorderMaskBlur
static std::shared_ptr< FilterContents > MakeBorderMaskBlur(FilterInput::Ref input, Sigma sigma_x, Sigma sigma_y, BlurStyle blur_style=BlurStyle::kNormal)
Definition: filter_contents.cc:93
impeller::LinearGradientContents::IsOpaque
bool IsOpaque() const override
Whether this Contents only emits opaque source colors from the fragment stage. This value does not ac...
Definition: linear_gradient_contents.cc:45
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::FilterContents::BlurStyle::kInner
@ kInner
Blurred inside, nothing outside.
impeller::ClipRestoreContents
Definition: clip_contents.h:60
impeller::RenderTargetAllocator
a wrapper around the impeller [Allocator] instance that can be used to provide caching of allocated r...
Definition: render_target.h:23
color_filter_contents.h
impeller::YUVColorSpace::kBT601LimitedRange
@ kBT601LimitedRange
impeller::Entity::TileMode
TileMode
Definition: entity.h:40
impeller::testing::TestRenderTargetAllocator::End
void End() override
Mark the end of a frame workload.
Definition: entity_unittests.cc:2476
impeller::ColorFilterContents::MakeLinearToSrgbFilter
static std::shared_ptr< ColorFilterContents > MakeLinearToSrgbFilter(FilterInput::Ref input)
Definition: color_filter_contents.cc:68
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:105
impeller::testing::CreatePassWithRectPath
auto CreatePassWithRectPath(Rect rect, std::optional< Rect > bounds_hint, bool collapse=false)
Definition: entity_unittests.cc:105
impeller::MakeTextFrameFromTextBlobSkia
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
Definition: text_frame_skia.cc:41
impeller::TPoint::x
Type x
Definition: point.h:25
impeller::FilterContents::MakeYUVToRGBFilter
static std::shared_ptr< FilterContents > MakeYUVToRGBFilter(std::shared_ptr< Texture > y_texture, std::shared_ptr< Texture > uv_texture, YUVColorSpace yuv_color_space)
Definition: filter_contents.cc:150
impeller::ContentContextOptions::primitive_type
PrimitiveType primitive_type
Definition: content_context.h:281
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:84
impeller::Matrix::IsIdentity
constexpr bool IsIdentity() const
Definition: matrix.h:347
impeller::Geometry::MakeFillPath
static std::shared_ptr< Geometry > MakeFillPath(Path path, std::optional< Rect > inner_rect=std::nullopt)
Definition: geometry.cc:150
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:36
impeller::testing::TestRenderTargetAllocator::Start
void Start() override
Mark the beginning of a frame workload.
Definition: entity_unittests.cc:2474
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:29
impeller::Geometry::MakeRect
static std::shared_ptr< Geometry > MakeRect(const Rect &rect)
Definition: geometry.cc:179
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:41
impeller::RenderTarget::CreateOffscreen
static RenderTarget CreateOffscreen(const Context &context, RenderTargetAllocator &allocator, ISize size, const std::string &label="Offscreen", AttachmentConfig color_attachment_config=kDefaultColorAttachmentConfig, std::optional< AttachmentConfig > stencil_attachment_config=kDefaultStencilAttachmentConfig)
Definition: render_target.cc:223
atlas_contents.h
impeller::FilterContents::MorphType
MorphType
Definition: filter_contents.h:34
impeller::BlendMode::kDestinationIn
@ kDestinationIn
impeller::Join::kBevel
@ kBevel
content_context.h
impeller::BlendMode::kDestinationOut
@ kDestinationOut
impeller::EntityPlayground
Definition: entity_playground.h:18
impeller::FilterContents::BlurStyle::kOuter
@ kOuter
Nothing inside, blurred outside.
impeller::Entity::GetCoverage
std::optional< Rect > GetCoverage() const
Definition: entity.cc:57
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:53
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:209
impeller::FilterContents::MakeMorphology
static std::shared_ptr< FilterContents > MakeMorphology(FilterInput::Ref input, Radius radius_x, Radius radius_y, MorphType morph_type)
Definition: filter_contents.cc:118
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:44
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:22
vector.h
impeller::Join
Join
Definition: path.h:24
impeller::TiledTextureContents
Definition: tiled_texture_contents.h:22
impeller::Context::BackendType::kMetal
@ kMetal
impeller::Context
To do anything rendering related with Impeller, you need a context.
Definition: context.h:47
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:44
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:201
std
Definition: comparable.h:95
impeller::FilterContents::MorphType::kDilate
@ kDilate
impeller::RadialGradientContents::SetTileMode
void SetTileMode(Entity::TileMode tile_mode)
Definition: radial_gradient_contents.cc:26
impeller::TPoint< Scalar >
impeller::RenderTargetAllocator::End
virtual void End()
Mark the end of a frame workload.
Definition: render_target.cc:23
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:37
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:262
impeller::SolidColorContents::SetColor
void SetColor(Color color)
Definition: solid_color_contents.cc:19
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:258
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::PointFieldGeometry::ComputeCircleDivisions
static size_t ComputeCircleDivisions(Scalar scaled_radius, bool round)
Compute the number of vertices to divide each circle into.
Definition: point_field_geometry.cc:247
impeller::RenderPipelineT
Definition: pipeline.h:90
impeller::BlendMode::kScreen
@ kScreen
impeller::ClipContents
Definition: clip_contents.h:19
impeller::YUVColorSpace
YUVColorSpace
Definition: color.h:55
impeller::ColorFilterContents::MakeColorMatrix
static std::shared_ptr< ColorFilterContents > MakeColorMatrix(FilterInput::Ref input, const ColorMatrix &color_matrix)
Definition: color_filter_contents.cc:58
impeller::Contents::ClipCoverage::Type::kAppend
@ kAppend
color.h
impeller::LinearGradientContents
Definition: linear_gradient_contents.h:23
impeller::Color::WhiteTransparent
static constexpr Color WhiteTransparent()
Definition: color.h:260
impeller::testing::TestRenderTargetAllocator::GetDescriptors
std::vector< TextureDescriptor > GetDescriptors() const
Definition: entity_unittests.cc:2478
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:38
impeller::UseSubpassInput::kYes
@ kYes
radial_gradient_contents.h
impeller::testing::TestPassDelegate::CanElide
bool CanElide() override
Definition: entity_unittests.cc:79
impeller::DescriptorType::kInputAttachment
@ kInputAttachment
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:27
pipeline_descriptor.h
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:37
impeller::Command::pipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > pipeline
Definition: command.h:96
impeller::BlendMode::kXor
@ kXor
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderLinearGradientClamp)
Definition: aiks_gradient_unittests.cc:48
impeller::Convexity::kConvex
@ kConvex
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:203
impeller::Contents::IsOpaque
virtual bool IsOpaque() const
Whether this Contents only emits opaque source colors from the fragment stage. This value does not ac...
Definition: contents.cc:46
impeller::Color::Coral
static constexpr Color Coral()
Definition: color.h:330
impeller::TSize< int64_t >::MakeWH
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:34
impeller::RenderPipelineT::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:92
impeller::ColorMatrix
Definition: color.h:117
impeller::ColorFilterContents::MakeBlend
static std::shared_ptr< ColorFilterContents > MakeBlend(BlendMode blend_mode, FilterInput::Vector inputs, std::optional< Color > foreground_color=std::nullopt)
the [inputs] are expected to be in the order of dst, src.
Definition: color_filter_contents.cc:17
impeller::ContentContextOptions
Definition: content_context.h:276
impeller::TextureContents::MakeRect
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
A common case factory that marks the texture contents as having a destination rectangle....
Definition: texture_contents.cc:27
impeller::RadialGradientContents
Definition: radial_gradient_contents.h:22
IMPELLER_PLAYGROUND_LINE
#define IMPELLER_PLAYGROUND_LINE(default_position_a, default_position_b, radius, color_a, color_b)
Definition: widgets.h:56
impeller::testing::TestPassDelegate::CanCollapseIntoParentPass
bool CanCollapseIntoParentPass(EntityPass *entity_pass) override
Whether or not this entity pass can be collapsed into the parent. If true, this method may modify the...
Definition: entity_unittests.cc:82
impeller::RadialGradientContents::SetColors
void SetColors(std::vector< Color > colors)
Definition: radial_gradient_contents.cc:30
impeller
Definition: aiks_context.cc:10
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::SolidColorContents::Make
static std::unique_ptr< SolidColorContents > Make(Path path, Color color)
Definition: solid_color_contents.cc:89
impeller::testing::TestPassDelegate::CreateContentsForSubpassTarget
std::shared_ptr< Contents > CreateContentsForSubpassTarget(std::shared_ptr< Texture > target, const Matrix &transform) override
Definition: entity_unittests.cc:87
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::Context::GetResourceAllocator
virtual std::shared_ptr< Allocator > GetResourceAllocator() const =0
Returns the allocator used to create textures and buffers on the device.
solid_rrect_blur_contents.h
impeller::ContentContext
Definition: content_context.h:332
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:214
impeller::TypographerContextSkia::Make
static std::shared_ptr< TypographerContext > Make()
Definition: typographer_context_skia.cc:30
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Cap
Cap
Definition: path.h:18
impeller::Geometry::MakeCover
static std::shared_ptr< Geometry > MakeCover()
Definition: geometry.cc:175
impeller::EntityPass::AddEntity
void AddEntity(Entity entity)
Add an entity to the current entity pass.
Definition: entity_pass.cc:70
entity_playground.h
vertex_buffer_builder.h
linear_gradient_contents.h
impeller::TiledTextureContents::SetTexture
void SetTexture(std::shared_ptr< Texture > texture)
Definition: tiled_texture_contents.cc:43
impeller::testing::TestRenderTargetAllocator::CreateTexture
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc) override
Create a new render target texture, or recycle a previously allocated render target texture.
Definition: entity_unittests.cc:2468