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