| Flutter Impeller |

Classes | Public Types | Public Member Functions | Friends | List of all members
impeller::Path Class Reference

Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments. These segments may be broken up by move commands, which are effectively linear commands that pick up the pen rather than continuing to draw. More...

`#include <path.h>`

## Classes
struct | Polyline
struct | PolylineContour

## Public Types
enum | ComponentType { ComponentType::kLinear, ComponentType::kQuadratic, ComponentType::kCubic, ComponentType::kContour }
template<class T >
using | Applier = std::function< void(size_t index, const T &component)>

## Public Member Functions
| Path ()
| ~Path ()
size_t | GetComponentCount (std::optional< ComponentType > type={}) const
FillType | GetFillType () const
bool | IsConvex () const
bool | IsEmpty () const
void | EnumerateComponents (const Applier< LinearPathComponent > &linear_applier, const Applier< QuadraticPathComponent > &quad_applier, const Applier< CubicPathComponent > &cubic_applier, const Applier< ContourComponent > &contour_applier) const
bool | GetLinearComponentAtIndex (size_t index, LinearPathComponent &linear) const
bool | GetQuadraticComponentAtIndex (size_t index, QuadraticPathComponent &quadratic) const
bool | GetCubicComponentAtIndex (size_t index, CubicPathComponent &cubic) const
bool | GetContourComponentAtIndex (size_t index, ContourComponent &contour) const
Polyline | CreatePolyline (Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
std::optional< Rect > | GetBoundingBox () const
std::optional< Rect > | GetTransformedBoundingBox (const Matrix &transform) const
void | WritePolyline (Scalar scale, VertexWriter &writer) const

## Friends
class | PathBuilder

## Detailed Description

Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments. These segments may be broken up by move commands, which are effectively linear commands that pick up the pen rather than continuing to draw.

All shapes supported by Impeller are paths either directly or via approximation (in the case of circles).

Paths are externally immutable once created, Creating paths must be done using a path builder.

Definition at line 52 of file path.h.

## Member Typedef Documentation

## ◆ Applier

template<class T >

using impeller::Path::Applier = std::function<void(size_t index, const T& component)>

Definition at line 142 of file path.h.

## Member Enumeration Documentation

## ◆ ComponentType

enum impeller::Path::ComponentType
| strong

Enumerator
kLinear |
kQuadratic |
kCubic |
kContour |

Definition at line 54 of file path.h.

54 {
55 kLinear,
56 kQuadratic,
57 kCubic,
58 kContour,
59 };

## Constructor & Destructor Documentation

## ◆ Path()

impeller::Path::Path | ( |  | ) |

Definition at line 16 of file path.cc.

16 : data_(new Data()) {}

## ◆ ~Path()

impeller::Path::~Path | ( |  | ) |
| default

## Member Function Documentation

## ◆ CreatePolyline()

Path::Polyline impeller::Path::CreatePolyline | ( | Scalar | scale,
|  | Polyline::PointBufferPtr | point_buffer = `std::make_unique<std::vector<Point>>()`,
|  | Polyline::ReclaimPointBufferCallback | reclaim = `nullptr`
| ) |  | const

Callers must provide the scale factor for how this path will be transformed.

It is suitable to use the max basis length of the matrix used to transform the path. If the provided scale is 0, curves will revert to straight lines.

Definition at line 264 of file path.cc.

267 {
268 Polyline polyline(std::move(point_buffer), std::move(reclaim));
269
270 auto& path_components = data_->components;
271 auto& path_points = data_->points;
272
273 auto get_path_component = [&path_components, &path_points](
274 size_t component_i) -> PathComponentVariant {
275 if (component_i >= path_components.size()) {
276 return std::monostate{};
277 }
278 const auto& component = path_components[component_i];
279 switch (component.type) {
280 case ComponentType::kLinear:
281 return reinterpret_cast<const LinearPathComponent*>(
282 &path_points[component.index]);
283 case ComponentType::kQuadratic:
284 return reinterpret_cast<const QuadraticPathComponent*>(
285 &path_points[component.index]);
286 case ComponentType::kCubic:
287 return reinterpret_cast<const CubicPathComponent*>(
288 &path_points[component.index]);
289 case ComponentType::kContour:
290 return std::monostate{};
291 }
292 };
293
294 auto compute_contour_start_direction =
295 [&get_path_component](size_t current_path_component_index) {
296 size_t next_component_index = current_path_component_index + 1;
297 while (!std::holds_alternative<std::monostate>(
298 get_path_component(next_component_index))) {
299 auto next_component = get_path_component(next_component_index);
300 auto maybe_vector =
301 std::visit(PathComponentStartDirectionVisitor(), next_component);
302 if (maybe_vector.has_value()) {
303 return maybe_vector.value();
304 } else {
305 next_component_index++;
306 }
307 }
308 return Vector2(0, -1);
309 };
310
311 std::vector<PolylineContour::Component> poly_components;
312 std::optional<size_t> previous_path_component_index;
313 auto end_contour = [&polyline, &previous_path_component_index,
314 &get_path_component, &poly_components]() {
315 // Whenever a contour has ended, extract the exact end direction from
316 // the last component.
317 if (polyline.contours.empty()) {
318 return;
319 }
320
321 if (!previous_path_component_index.has_value()) {
322 return;
323 }
324
325 auto& contour = polyline.contours.back();
326 contour.end_direction = Vector2(0, 1);
327 contour.components = poly_components;
328 poly_components.clear();
329
330 size_t previous_index = previous_path_component_index.value();
331 while (!std::holds_alternative<std::monostate>(
332 get_path_component(previous_index))) {
333 auto previous_component = get_path_component(previous_index);
334 auto maybe_vector =
335 std::visit(PathComponentEndDirectionVisitor(), previous_component);
336 if (maybe_vector.has_value()) {
337 contour.end_direction = maybe_vector.value();
338 break;
339 } else {
340 if (previous_index == 0) {
341 break;
342 }
343 previous_index--;
344 }
345 }
346 };
347
348 for (size_t component_i = 0; component_i < path_components.size();
349 component_i++) {
350 const auto& path_component = path_components[component_i];
351 switch (path_component.type) {
352 case ComponentType::kLinear:
353 poly_components.push_back({
354 .component_start_index = polyline.points->size() - 1,
355 .is_curve = false,
356 });
357 reinterpret_cast<const LinearPathComponent*>(
358 &path_points[path_component.index])
359 ->AppendPolylinePoints(*polyline.points);
360 previous_path_component_index = component_i;
361 break;
362 case ComponentType::kQuadratic:
363 poly_components.push_back({
364 .component_start_index = polyline.points->size() - 1,
365 .is_curve = true,
366 });
367 reinterpret_cast<const QuadraticPathComponent*>(
368 &path_points[path_component.index])
369 ->AppendPolylinePoints(scale, *polyline.points);
370 previous_path_component_index = component_i;
371 break;
372 case ComponentType::kCubic:
373 poly_components.push_back({
374 .component_start_index = polyline.points->size() - 1,
375 .is_curve = true,
376 });
377 reinterpret_cast<const CubicPathComponent*>(
378 &path_points[path_component.index])
379 ->AppendPolylinePoints(scale, *polyline.points);
380 previous_path_component_index = component_i;
381 break;
382 case ComponentType::kContour:
383 if (component_i == path_components.size() - 1) {
384 // If the last component is a contour, that means it's an empty
385 // contour, so skip it.
386 continue;
387 }
388 end_contour();
389
390 Vector2 start_direction = compute_contour_start_direction(component_i);
391 const auto& contour = data_->contours[path_component.index];
392 polyline.contours.push_back({.start_index = polyline.points->size(),
393 .is_closed = contour.is_closed,
394 .start_direction = start_direction,
395 .components = poly_components});
396
397 polyline.points->push_back(contour.destination);
398 break;
399 }
400 }
401 end_contour();
402 return polyline;
403 }

References kContour, kCubic, kLinear, kQuadratic, polyline, and scale.

Referenced by impeller::Tessellator::CreateTempPolyline(), impeller::TessellatorLibtess::Tessellate(), and impeller::testing::TEST().

## ◆ EnumerateComponents()

void impeller::Path::EnumerateComponents | ( | const Applier< LinearPathComponent > & | linear_applier,
|  | const Applier< QuadraticPathComponent > & | quad_applier,
|  | const Applier< CubicPathComponent > & | cubic_applier,
|  | const Applier< ContourComponent > & | contour_applier
| ) |  | const

Definition at line 63 of file path.cc.

67 {
68 auto& points = data_->points;
69 size_t currentIndex = 0;
70 for (const auto& component : data_->components) {
71 switch (component.type) {
72 case ComponentType::kLinear:
73 if (linear_applier) {
74 linear_applier(currentIndex,
75 LinearPathComponent(points[component.index],
76 points[component.index + 1]));
77 }
78 break;
79 case ComponentType::kQuadratic:
80 if (quad_applier) {
81 quad_applier(currentIndex,
82 QuadraticPathComponent(points[component.index],
83 points[component.index + 1],
84 points[component.index + 2]));
85 }
86 break;
87 case ComponentType::kCubic:
88 if (cubic_applier) {
89 cubic_applier(currentIndex,
90 CubicPathComponent(points[component.index],
91 points[component.index + 1],
92 points[component.index + 2],
93 points[component.index + 3]));
94 }
95 break;
96 case ComponentType::kContour:
97 if (contour_applier) {
98 contour_applier(currentIndex, data_->contours[component.index]);
99 }
100 break;
101 }
102 currentIndex++;
103 }
104 }

References kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::PathBuilder::AddPath().

## ◆ GetBoundingBox()

std::optional< Rect > impeller::Path::GetBoundingBox | ( |  | ) | const

Definition at line 405 of file path.cc.

405 {
406 return data_->bounds;
407 }

Referenced by impeller::Canvas::ClipPath(), GetTransformedBoundingBox(), impeller::testing::TEST(), and impeller::testing::TEST_P().

## ◆ GetComponentCount()

size_t impeller::Path::GetComponentCount | ( | std::optional< ComponentType > | type = `{}` | ) | const

Definition at line 34 of file path.cc.

34 {
35 if (!type.has_value()) {
36 return data_->components.size();
37 }
38 auto type_value = type.value();
39 if (type_value == ComponentType::kContour) {
40 return data_->contours.size();
41 }
42 size_t count = 0u;
43 for (const auto& component : data_->components) {
44 if (component.type == type_value) {
45 count++;
46 }
47 }
48 return count;
49 }

References kContour, and type.

Referenced by impeller::testing::TEST().

## ◆ GetContourComponentAtIndex()

bool impeller::Path::GetContourComponentAtIndex | ( | size_t | index,
|  | ContourComponent & | contour
| ) |  | const

Definition at line 229 of file path.cc.

230 {
231 auto& components = data_->components;
232
233 if (index >= components.size()) {
234 return false;
235 }
236
237 if (components[index].type != ComponentType::kContour) {
238 return false;
239 }
240
241 move = data_->contours[components[index].index];
242 return true;
243 }

References kContour, and type.

Referenced by impeller::testing::TEST().

## ◆ GetCubicComponentAtIndex()

bool impeller::Path::GetCubicComponentAtIndex | ( | size_t | index,
|  | CubicPathComponent & | cubic
| ) |  | const

Definition at line 210 of file path.cc.

211 {
212 auto& components = data_->components;
213
214 if (index >= components.size()) {
215 return false;
216 }
217
218 if (components[index].type != ComponentType::kCubic) {
219 return false;
220 }
221
222 auto& points = data_->points;
223 auto point_index = components[index].index;
224 cubic = CubicPathComponent(points[point_index], points[point_index + 1],
225 points[point_index + 2], points[point_index + 3]);
226 return true;
227 }

References kCubic, and type.

## ◆ GetFillType()

FillType impeller::Path::GetFillType | ( |  | ) | const

Definition at line 51 of file path.cc.

51 {
52 return data_->fill;
53 }

Referenced by impeller::TessellatorLibtess::Tessellate().

## ◆ GetLinearComponentAtIndex()

bool impeller::Path::GetLinearComponentAtIndex | ( | size_t | index,
|  | LinearPathComponent & | linear
| ) |  | const

Definition at line 172 of file path.cc.

173 {
174 auto& components = data_->components;
175
176 if (index >= components.size()) {
177 return false;
178 }
179
180 if (components[index].type != ComponentType::kLinear) {
181 return false;
182 }
183
184 auto& points = data_->points;
185 auto point_index = components[index].index;
186 linear = LinearPathComponent(points[point_index], points[point_index + 1]);
187 return true;
188 }

References kLinear, and type.

Referenced by impeller::testing::TEST().

## ◆ GetQuadraticComponentAtIndex()

bool impeller::Path::GetQuadraticComponentAtIndex | ( | size_t | index,
|  | QuadraticPathComponent & | quadratic
| ) |  | const

Definition at line 190 of file path.cc.

192 {
193 auto& components = data_->components;
194
195 if (index >= components.size()) {
196 return false;
197 }
198
199 if (components[index].type != ComponentType::kQuadratic) {
200 return false;
201 }
202
203 auto& points = data_->points;
204 auto point_index = components[index].index;
205 quadratic = QuadraticPathComponent(
206 points[point_index], points[point_index + 1], points[point_index + 2]);
207 return true;
208 }

References kQuadratic, and type.

## ◆ GetTransformedBoundingBox()

std::optional< Rect > impeller::Path::GetTransformedBoundingBox | ( | const Matrix & | transform | ) | const

Definition at line 409 of file path.cc.

410 {
411 auto bounds = GetBoundingBox();
412 if (!bounds.has_value()) {
413 return std::nullopt;
414 }
415 return bounds->TransformBounds(transform);
416 }

References GetBoundingBox(), and transform.

## ◆ IsConvex()

bool impeller::Path::IsConvex | ( |  | ) | const

Definition at line 55 of file path.cc.

55 {
56 return data_->convexity == Convexity::kConvex;
57 }

References impeller::kConvex.

## ◆ IsEmpty()

bool impeller::Path::IsEmpty | ( |  | ) | const

Definition at line 59 of file path.cc.

59 {
60 return data_->points.empty();
61 }

## ◆ WritePolyline()

void impeller::Path::WritePolyline | ( | Scalar | scale,
|  | VertexWriter & | writer
| ) |  | const

Generate a polyline into the temporary storage held by the [writer].

It is suitable to use the max basis length of the matrix used to transform the path. If the provided scale is 0, curves will revert to straight lines.

Definition at line 106 of file path.cc.

106 {
107 auto& path_components = data_->components;
108 auto& path_points = data_->points;
109 bool started_contour = false;
110 bool first_point = true;
111
112 for (size_t component_i = 0; component_i < path_components.size();
113 component_i++) {
114 const auto& path_component = path_components[component_i];
115 switch (path_component.type) {
116 case ComponentType::kLinear: {
117 const LinearPathComponent* linear =
118 reinterpret_cast<const LinearPathComponent*>(
119 &path_points[path_component.index]);
120 if (first_point) {
121 writer.Write(linear->p1);
122 first_point = false;
123 }
124 writer.Write(linear->p2);
125 break;
126 }
127 case ComponentType::kQuadratic: {
128 const QuadraticPathComponent* quad =
129 reinterpret_cast<const QuadraticPathComponent*>(
130 &path_points[path_component.index]);
131 if (first_point) {
132 writer.Write(quad->p1);
133 first_point = false;
134 }
135 quad->ToLinearPathComponents(scale, writer);
136 break;
137 }
138 case ComponentType::kCubic: {
139 const CubicPathComponent* cubic =
140 reinterpret_cast<const CubicPathComponent*>(
141 &path_points[path_component.index]);
142 if (first_point) {
143 writer.Write(cubic->p1);
144 first_point = false;
145 }
146 cubic->ToLinearPathComponents(scale, writer);
147 break;
148 }
149 case ComponentType::kContour:
150 if (component_i == path_components.size() - 1) {
151 // If the last component is a contour, that means it's an empty
152 // contour, so skip it.
153 continue;
154 }
155 // The contour component type is the first segment in a contour.
156 // Since this should contain the destination (if closed), we
157 // can start with this point. If there was already an open
158 // contour, or we've reached the end of the verb list, we
159 // also close the contour.
160 if (started_contour) {
161 writer.EndContour();
162 }
163 started_contour = true;
164 first_point = true;
165 }
166 }
167 if (started_contour) {
168 writer.EndContour();
169 }
170 }

References impeller::VertexWriter::EndContour(), kContour, kCubic, kLinear, kQuadratic, impeller::LinearPathComponent::p1, impeller::QuadraticPathComponent::p1, impeller::CubicPathComponent::p1, impeller::LinearPathComponent::p2, scale, impeller::QuadraticPathComponent::ToLinearPathComponents(), impeller::CubicPathComponent::ToLinearPathComponents(), and impeller::VertexWriter::Write().

Referenced by impeller::Tessellator::TessellateConvexInternal().

## Friends And Related Function Documentation

## ◆ PathBuilder

friend class PathBuilder
| friend

Definition at line 184 of file path.h.

The documentation for this class was generated from the following files:

- impeller/geometry/path.h
- impeller/geometry/path.cc

polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:303
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:326
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:405
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:231
impeller::MinMagFilter::kLinear
@ kLinear
type
GLenum type
Definition: blit_command_gles.cc:126
scale
const Scalar scale
Definition: stroke_path_geometry.cc:308
impeller::PathComponentVariant
std::variant< std::monostate, const LinearPathComponent *, const QuadraticPathComponent *, const CubicPathComponent * > PathComponentVariant
Definition: path_component.h:168
impeller::Convexity::kConvex
@ kConvex
impeller::Path::ComponentType::kContour
@ kContour