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