Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
aiks_dl_primitive_shape_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
7
15#include "imgui.h"
17
18// The tests in this file check the primitive rendering operations to make
19// sure that they produce correct output, particularly for strokes and
20// especially for hairline strokes, under a variety of transforms.
21
22namespace {
23
31using flutter::DlRect;
33
34enum class RenderType {
35 kSquare,
36 kRectangle,
37 kCircle,
38 kOval,
39 kLine,
40
41 // kValidCount casts to the number of valid enum values.
42 kValidCount,
43 // kInvalid is used as a default initializer to force construction override.
44 kInvalid,
45};
46
47struct RenderParameters {
48 RenderType render_type =
49 RenderType::kInvalid; // Will cause errors unless explicitly replaced.
51 DlScalar stroke_width = 0.0f;
52 DlScalar scale_x = 1.0f;
53 DlScalar scale_y = 1.0f;
54 DlScalar skew_x = 0.0f;
55 DlScalar skew_y = 0.0f;
56 DlScalar degrees = 0.0f;
57};
58
59void RenderPrimitiveWithStroke(DisplayListBuilder& builder,
60 const RenderParameters& params) {
61 builder.Save();
62
63 builder.Translate(params.center.x, params.center.y);
64 builder.Rotate(params.degrees);
65 builder.Scale(params.scale_x, params.scale_y);
66 builder.Skew(params.skew_x, params.skew_y);
67 builder.Translate(-params.center.x, -params.center.y);
68
69 // We describe the base size of the shape to draw in terms of the radius
70 // of a circle that would fill the space.
71 const DlScalar base_radius = 100.0f;
72 const DlScalar fill_radius = base_radius + params.stroke_width;
73 const DlScalar stroke_radius = base_radius - 10.0f;
74
75 auto draw_shape = [&](DlScalar radius, DlColor color,
76 DlScalar stroke_width = -1) -> void {
77 DlPaint paint;
78 paint.setColor(color);
79 if (stroke_width < 0) {
80 paint.setDrawStyle(DlDrawStyle::kFill);
81 } else {
82 paint.setDrawStyle(DlDrawStyle::kStroke);
83 paint.setStrokeWidth(stroke_width);
84 }
85
86 // Square bounds to match the overall size of the circle described.
87 DlRect square_bounds = DlRect::MakeCircleBounds(params.center, radius);
88 // Rectangular bounds slightly elongated from the square bounds.
89 DlRect rect_bounds = square_bounds.Expand(20.0f, 0.0f);
90
91 switch (params.render_type) {
92 case RenderType::kSquare:
93 builder.DrawRect(square_bounds, paint);
94 break;
95 case RenderType::kRectangle:
96 builder.DrawRect(rect_bounds, paint);
97 break;
98 case RenderType::kCircle:
99 builder.DrawCircle(params.center, radius, paint);
100 break;
101 case RenderType::kOval:
102 builder.DrawOval(rect_bounds, paint);
103 break;
104 case RenderType::kLine: {
105 // Drawline can't "fill" the outer shape since it is just a line and
106 // has no fillable interior. So, instead we reinterpret the radius as
107 // a larger line width, but only accepting a radius outside of the
108 // typical stroke_radius. The length of the line always extends across
109 // the diameter of what would have been a circle of that radius.
110 if (stroke_width < 0) {
111 if (radius <= stroke_radius) {
112 // This must be an interior fill, but the line has no interior.
113 break;
114 }
115 paint.setDrawStyle(DlDrawStyle::kStroke);
116 paint.setStrokeWidth((radius - stroke_radius) * 2.0f);
117 }
118 // Extend the line by the radius amount in both directions.
119 DlPoint fill_offset(radius, 0.0f);
120 builder.DrawLine(params.center - fill_offset, //
121 params.center + fill_offset, //
122 paint);
123 break;
124 }
125 case RenderType::kValidCount:
126 case RenderType::kInvalid:
128 }
129 };
130
131 draw_shape(fill_radius, DlColor::kBlue());
132 draw_shape(stroke_radius, DlColor::ARGB(1.0f, 0.0f, 0.0f, 0.5f));
133 draw_shape(stroke_radius, DlColor::kWhite(), params.stroke_width);
134
135 builder.Restore();
136}
137
138} // namespace
139
140namespace impeller {
141namespace testing {
142
143// This playground tests the effects of scaling, rotation and skew transforms
144// on the consistency of a stroke (particularly hairlines). The math used to
145// estimate the pixel size can get complicated in a shader that is performing
146// some of its operations on local space values and some in device space.
147TEST_P(AiksTest, PrimitiveShapePlayground) {
148 if (IsGoldenTest()) {
149 GTEST_SKIP() << "PrimitiveShapePlayground does not produce a golden image";
150 }
151
152 RenderParameters params{
153 .render_type = RenderType::kRectangle,
154 .center = GetWindowBounds().GetCenter(),
155 };
156 // The ImGui controls need a pointer to an int and casting a pointer to
157 // an enum field to an int pointer is frowned upon, so we instead create
158 // an int variable and then fill in the params enum from it later.
159 int render_type_index = static_cast<int>(RenderType::kRectangle);
160
161 auto callback = [&]() -> sk_sp<DisplayList> {
162 if (AiksTest::ImGuiBegin("Controls", nullptr,
163 ImGuiWindowFlags_AlwaysAutoResize)) {
164 ImGui::SliderFloat("Stroke", &params.stroke_width, 0.0f, 30.0f);
165 ImGui::SliderFloat("X Scale", &params.scale_x, 1.0f, 3.0f);
166 ImGui::SliderFloat("Y Scale", &params.scale_y, 1.0f, 3.0f);
167 ImGui::SliderFloat("X Skew", &params.skew_x, 0.0f, 1.0f);
168 ImGui::SliderFloat("Y Skew", &params.skew_y, 0.0f, 1.0f);
169 ImGui::SliderFloat("Rotation", &params.degrees, 0.0f, 360.0f);
170 ImGui::ListBox(
171 "Shape Type", &render_type_index,
172 [](void* data, int index) {
173 switch (static_cast<RenderType>(index)) {
174 case RenderType::kSquare:
175 return "Square";
176 case RenderType::kRectangle:
177 return "Rectangle";
178 case RenderType::kCircle:
179 return "Circle";
180 case RenderType::kOval:
181 return "Oval";
182 case RenderType::kLine:
183 return "Line";
184 case RenderType::kValidCount:
185 case RenderType::kInvalid:
187 }
188 },
189 nullptr, static_cast<int>(RenderType::kValidCount), -1);
190 ImGui::End();
191 }
192
193 // Translate our "Gui int variable" to the appropriate enum field value.
194 params.render_type = static_cast<RenderType>(render_type_index);
195
196 DisplayListBuilder builder;
197 builder.Scale(GetContentScale().x, GetContentScale().y);
198 builder.DrawColor(DlColor::kBlack(), DlBlendMode::kSrc);
199 RenderPrimitiveWithStroke(builder, params);
200 return builder.Build();
201 };
202
203 ASSERT_TRUE(OpenPlaygroundHere(callback));
204}
205
206TEST_P(AiksTest, CanRenderSkewedCircleHairline) {
207 DisplayListBuilder builder;
208 builder.Scale(GetContentScale().x, GetContentScale().y);
209 builder.DrawColor(DlColor::kBlack(), DlBlendMode::kSrc);
210
211 RenderParameters params{
212 .render_type = RenderType::kCircle,
213 .center = GetWindowBounds().GetCenter(),
214 .skew_x = 0.75f,
215 .skew_y = 0.75f,
216 };
217 RenderPrimitiveWithStroke(builder, params);
218
219 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
220}
221
222} // namespace testing
223} // namespace impeller
void DrawColor(DlColor color, DlBlendMode mode) override
void Scale(DlScalar sx, DlScalar sy) override
sk_sp< DisplayList > Build()
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
int32_t x
const EmbeddedViewParams * params
FlutterDesktopBinaryReply callback
#define FML_UNREACHABLE()
Definition logging.h:128
double y
impeller::Scalar DlScalar
@ kSquare
adds square
impeller::Rect DlRect
DlDrawStyle
Definition dl_paint.h:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
impeller::Point DlPoint
TEST_P(AiksTest, DrawAtlasNoColor)
BlendMode
Definition color.h:58
flutter::DlColor DlColor
flutter::DlPaint DlPaint
static constexpr DlColor kBlack()
Definition dl_color.h:69
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:652