| Flutter Impeller |

Classes | Public Types | Public Member Functions | Static 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 }

## Public Member Functions
| Path ()
| ~Path ()
size_t | GetComponentCount (std::optional< ComponentType > type={}) const
FillType | GetFillType () const
bool | IsConvex () const
bool | IsEmpty () const
bool | IsSingleContour () const
| Whether the line contains a single contour. More...
ComponentType | GetComponentTypeAtIndex (size_t index) 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
void | EndContour (size_t storage_offset, Polyline &polyline, size_t component_index, std::vector< PolylineContour::Component > &poly_components) const
std::optional< Rect > | GetBoundingBox () const
std::optional< Rect > | GetTransformedBoundingBox (const Matrix &transform) const
void | WritePolyline (Scalar scale, VertexWriter &writer) const
std::pair< size_t, size_t > | CountStorage (Scalar scale) const
| Determine required storage for points and number of contours. More...

## Static Public Member Functions
static constexpr size_t | VerbToOffset (Path::ComponentType verb)

## 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 53 of file path.h.

## Member Enumeration Documentation

## ◆ ComponentType

enum impeller::Path::ComponentType
| strong

Enumerator
kLinear |
kQuadratic |
kCubic |
kContour |

Definition at line 55 of file path.h.

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

## 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

## ◆ CountStorage()

std::pair< size_t, size_t > impeller::Path::CountStorage | ( | Scalar | scale | ) | const

Determine required storage for points and number of contours.

Determine required storage for points and indices.

Definition at line 67 of file path.cc.

67 {
68 size_t points = 0;
69 size_t contours = 0;
70
71 auto& path_components = data_->components;
72 auto& path_points = data_->points;
73
74 size_t storage_offset = 0u;
75 for (size_t component_i = 0; component_i < path_components.size();
76 component_i++) {
77 const auto& path_component = path_components[component_i];
78 switch (path_component) {
79 case ComponentType::kLinear: {
80 points += 2;
81 break;
82 }
83 case ComponentType::kQuadratic: {
84 const QuadraticPathComponent* quad =
85 reinterpret_cast<const QuadraticPathComponent*>(
86 &path_points[storage_offset]);
87 points += quad->CountLinearPathComponents(scale);
88 break;
89 }
90 case ComponentType::kCubic: {
91 const CubicPathComponent* cubic =
92 reinterpret_cast<const CubicPathComponent*>(
93 &path_points[storage_offset]);
94 points += cubic->CountLinearPathComponents(scale);
95 break;
96 }
97 case Path::ComponentType::kContour:
98 contours++;
99 }
100 storage_offset += VerbToOffset(path_component);
101 }
102 return std::make_pair(points, contours);
103 }

References impeller::QuadraticPathComponent::CountLinearPathComponents(), impeller::CubicPathComponent::CountLinearPathComponents(), kContour, kCubic, kLinear, kQuadratic, scale, and VerbToOffset().

Referenced by impeller::Tessellator::TessellateConvex(), and impeller::testing::TEST().

## ◆ 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 338 of file path.cc.

341 {
342 Polyline polyline(std::move(point_buffer), std::move(reclaim));
343
344 auto& path_components = data_->components;
345 auto& path_points = data_->points;
346 std::optional<Vector2> start_direction;
347 std::vector<PolylineContour::Component> poly_components;
348 size_t storage_offset = 0u;
349 size_t component_i = 0;
350
351 for (; component_i < path_components.size(); component_i++) {
352 auto path_component = path_components[component_i];
353 switch (path_component) {
354 case ComponentType::kLinear: {
355 poly_components.push_back({
356 .component_start_index = polyline.points->size() - 1,
357 .is_curve = false,
358 });
359 auto* linear = reinterpret_cast<const LinearPathComponent*>(
360 &path_points[storage_offset]);
361 linear->AppendPolylinePoints(*polyline.points);
362 if (!start_direction.has_value()) {
363 start_direction = linear->GetStartDirection();
364 }
365 break;
366 }
367 case ComponentType::kQuadratic: {
368 poly_components.push_back({
369 .component_start_index = polyline.points->size() - 1,
370 .is_curve = true,
371 });
372 auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
373 &path_points[storage_offset]);
374 quad->AppendPolylinePoints(scale, *polyline.points);
375 if (!start_direction.has_value()) {
376 start_direction = quad->GetStartDirection();
377 }
378 break;
379 }
380 case ComponentType::kCubic: {
381 poly_components.push_back({
382 .component_start_index = polyline.points->size() - 1,
383 .is_curve = true,
384 });
385 auto* cubic = reinterpret_cast<const CubicPathComponent*>(
386 &path_points[storage_offset]);
387 cubic->AppendPolylinePoints(scale, *polyline.points);
388 if (!start_direction.has_value()) {
389 start_direction = cubic->GetStartDirection();
390 }
391 break;
392 }
393 case ComponentType::kContour:
394 if (component_i == path_components.size() - 1) {
395 // If the last component is a contour, that means it's an empty
396 // contour, so skip it.
397 break;
398 }
399 if (!polyline.contours.empty()) {
400 polyline.contours.back().start_direction =
401 start_direction.value_or(Vector2(0, -1));
402 start_direction = std::nullopt;
403 }
404 EndContour(storage_offset, polyline, component_i, poly_components);
405
406 auto* contour = reinterpret_cast<const ContourComponent*>(
407 &path_points[storage_offset]);
408 polyline.contours.push_back(PolylineContour{
409 .start_index = polyline.points->size(), //
410 .is_closed = contour->IsClosed(), //
411 .start_direction = Vector2(0, -1), //
412 .components = poly_components //
413 });
414
415 polyline.points->push_back(contour->destination);
416 break;
417 }
418 storage_offset += VerbToOffset(path_component);
419 }
420
421 // Subtract the last storage offset increment so that the storage lookup is
422 // correct, including potentially an empty contour as well.
423 if (component_i > 0 && path_components.back() == ComponentType::kContour) {
424 storage_offset -= VerbToOffset(ComponentType::kContour);
425 component_i--;
426 }
427
428 if (!polyline.contours.empty()) {
429 polyline.contours.back().start_direction =
430 start_direction.value_or(Vector2(0, -1));
431 }
432 EndContour(storage_offset, polyline, component_i, poly_components);
433 return polyline;
434 }

References impeller::LinearPathComponent::AppendPolylinePoints(), impeller::QuadraticPathComponent::AppendPolylinePoints(), impeller::CubicPathComponent::AppendPolylinePoints(), EndContour(), kContour, kCubic, kLinear, kQuadratic, polyline, scale, impeller::Path::PolylineContour::start_index, and VerbToOffset().

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

## ◆ EndContour()

void impeller::Path::EndContour | ( | size_t | storage_offset,
|  | Polyline & | polyline,
|  | size_t | component_index,
|  | std::vector< PolylineContour::Component > & | poly_components
| ) |  | const

Definition at line 274 of file path.cc.

278 {
279 auto& path_components = data_->components;
280 auto& path_points = data_->points;
281 // Whenever a contour has ended, extract the exact end direction from
282 // the last component.
283 if (polyline.contours.empty() || component_index == 0) {
284 return;
285 }
286
287 auto& contour = polyline.contours.back();
288 contour.end_direction = Vector2(0, 1);
289 contour.components = poly_components;
290 poly_components.clear();
291
292 size_t previous_index = component_index - 1;
293 storage_offset -= VerbToOffset(path_components[previous_index]);
294
295 while (previous_index >= 0 && storage_offset >= 0) {
296 const auto& path_component = path_components[previous_index];
297 switch (path_component) {
298 case ComponentType::kLinear: {
299 auto* linear = reinterpret_cast<const LinearPathComponent*>(
300 &path_points[storage_offset]);
301 auto maybe_end = linear->GetEndDirection();
302 if (maybe_end.has_value()) {
303 contour.end_direction = maybe_end.value();
304 return;
305 }
306 break;
307 }
308 case ComponentType::kQuadratic: {
309 auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
310 &path_points[storage_offset]);
311 auto maybe_end = quad->GetEndDirection();
312 if (maybe_end.has_value()) {
313 contour.end_direction = maybe_end.value();
314 return;
315 }
316 break;
317 }
318 case ComponentType::kCubic: {
319 auto* cubic = reinterpret_cast<const CubicPathComponent*>(
320 &path_points[storage_offset]);
321 auto maybe_end = cubic->GetEndDirection();
322 if (maybe_end.has_value()) {
323 contour.end_direction = maybe_end.value();
324 return;
325 }
326 break;
327 }
328 case ComponentType::kContour: {
329 // Hit previous contour, return.
330 return;
331 };
332 }
333 storage_offset -= VerbToOffset(path_component);
334 previous_index--;
335 }
336 };

References impeller::LinearPathComponent::GetEndDirection(), impeller::QuadraticPathComponent::GetEndDirection(), impeller::CubicPathComponent::GetEndDirection(), kContour, kCubic, kLinear, kQuadratic, polyline, and VerbToOffset().

Referenced by CreatePolyline().

## ◆ GetBoundingBox()

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

Definition at line 436 of file path.cc.

436 {
437 return data_->bounds;
438 }

Referenced by 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 size_t count = 0u;
40 for (const auto& component : data_->components) {
41 if (component == type_value) {
42 count++;
43 }
44 }
45 return count;
46 }

References type.

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

## ◆ GetComponentTypeAtIndex()

Path::ComponentType impeller::Path::GetComponentTypeAtIndex | ( | size_t | index | ) | const

Definition at line 173 of file path.cc.

173 {
174 auto& components = data_->components;
175 return components[index];
176 }

## ◆ GetContourComponentAtIndex()

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

Definition at line 237 of file path.cc.

238 {
239 auto& components = data_->components;
240 if (index >= components.size() ||
241 components[index] != ComponentType::kContour) {
242 return false;
243 }
244
245 size_t storage_offset = 0u;
246 for (auto i = 0u; i < index; i++) {
247 storage_offset += VerbToOffset(components[i]);
248 }
249 auto& points = data_->points;
250
251 move = ContourComponent(points[storage_offset], points[storage_offset + 1]);
252 return true;
253 }

References kContour, and VerbToOffset().

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

## ◆ GetCubicComponentAtIndex()

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

Definition at line 217 of file path.cc.

218 {
219 auto& components = data_->components;
220 if (index >= components.size() ||
221 components[index] != ComponentType::kCubic) {
222 return false;
223 }
224
225 size_t storage_offset = 0u;
226 for (auto i = 0u; i < index; i++) {
227 storage_offset += VerbToOffset(components[i]);
228 }
229 auto& points = data_->points;
230
231 cubic = CubicPathComponent(points[storage_offset], points[storage_offset + 1],
232 points[storage_offset + 2],
233 points[storage_offset + 3]);
234 return true;
235 }

References kCubic, and VerbToOffset().

## ◆ GetFillType()

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

Definition at line 48 of file path.cc.

48 {
49 return data_->fill;
50 }

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

## ◆ GetLinearComponentAtIndex()

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

Definition at line 178 of file path.cc.

179 {
180 auto& components = data_->components;
181 if (index >= components.size() ||
182 components[index] != ComponentType::kLinear) {
183 return false;
184 }
185
186 size_t storage_offset = 0u;
187 for (auto i = 0u; i < index; i++) {
188 storage_offset += VerbToOffset(components[i]);
189 }
190 auto& points = data_->points;
191 linear =
192 LinearPathComponent(points[storage_offset], points[storage_offset + 1]);
193 return true;
194 }

References kLinear, and VerbToOffset().

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

## ◆ GetQuadraticComponentAtIndex()

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

Definition at line 196 of file path.cc.

198 {
199 auto& components = data_->components;
200 if (index >= components.size() ||
201 components[index] != ComponentType::kQuadratic) {
202 return false;
203 }
204
205 size_t storage_offset = 0u;
206 for (auto i = 0u; i < index; i++) {
207 storage_offset += VerbToOffset(components[i]);
208 }
209 auto& points = data_->points;
210
211 quadratic =
212 QuadraticPathComponent(points[storage_offset], points[storage_offset + 1],
213 points[storage_offset + 2]);
214 return true;
215 }

References kQuadratic, and VerbToOffset().

## ◆ GetTransformedBoundingBox()

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

Definition at line 440 of file path.cc.

441 {
442 auto bounds = GetBoundingBox();
443 if (!bounds.has_value()) {
444 return std::nullopt;
445 }
446 return bounds->TransformBounds(transform);
447 }

References GetBoundingBox(), and transform.

## ◆ IsConvex()

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

Definition at line 52 of file path.cc.

52 {
53 return data_->convexity == Convexity::kConvex;
54 }

References impeller::kConvex.

## ◆ IsEmpty()

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

Definition at line 56 of file path.cc.

56 {
57 return data_->points.empty() ||
58 (data_->components.size() == 1 &&
59 data_->components[0] == ComponentType::kContour);
60 }

References kContour.

## ◆ IsSingleContour()

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

Whether the line contains a single contour.

Definition at line 62 of file path.cc.

62 {
63 return data_->single_countour;
64 }

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

## ◆ VerbToOffset()

static constexpr size_t impeller::Path::VerbToOffset | ( | Path::ComponentType | verb | ) |
| inline static constexpr

Definition at line 62 of file path.h.

62 {
63 switch (verb) {
64 case Path::ComponentType::kLinear:
65 return 2u;
66 case Path::ComponentType::kQuadratic:
67 return 3u;
68 case Path::ComponentType::kCubic:
69 return 4u;
70 case Path::ComponentType::kContour:
71 return 2u;
72 break;
73 }
74 FML_UNREACHABLE();
75 }

References kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::PathBuilder::AddPath(), CountStorage(), CreatePolyline(), EndContour(), GetContourComponentAtIndex(), GetCubicComponentAtIndex(), GetLinearComponentAtIndex(), GetQuadraticComponentAtIndex(), impeller::PathBuilder::Shift(), and WritePolyline().

## ◆ 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 105 of file path.cc.

105 {
106 auto& path_components = data_->components;
107 auto& path_points = data_->points;
108 bool started_contour = false;
109 bool first_point = true;
110
111 size_t storage_offset = 0u;
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) {
116 case ComponentType::kLinear: {
117 const LinearPathComponent* linear =
118 reinterpret_cast<const LinearPathComponent*>(
119 &path_points[storage_offset]);
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[storage_offset]);
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[storage_offset]);
142 if (first_point) {
143 writer.Write(cubic->p1);
144 first_point = false;
145 }
146 cubic->ToLinearPathComponents(scale, writer);
147 break;
148 }
149 case Path::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 storage_offset += VerbToOffset(path_component);
167 }
168 if (started_contour) {
169 writer.EndContour();
170 }
171 }

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(), VerbToOffset(), and impeller::VertexWriter::Write().

Referenced by impeller::Tessellator::GenerateLineStrip(), impeller::Tessellator::TessellateConvex(), impeller::Tessellator::TessellateConvexInternal(), and impeller::testing::TEST().

## Friends And Related Function Documentation

## ◆ PathBuilder

friend class PathBuilder
| friend

Definition at line 206 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:311
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:436
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::EndContour
void EndContour(size_t storage_offset, Polyline &polyline, size_t component_index, std::vector< PolylineContour::Component > &poly_components) const
Definition: path.cc:274
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
type
GLenum type
Definition: blit_command_gles.cc:151
impeller::MinMagFilter::kLinear
@ kLinear
scale
const Scalar scale
Definition: stroke_path_geometry.cc:316
impeller::Path::VerbToOffset
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:62
impeller::Convexity::kConvex
@ kConvex
impeller::Path::ComponentType::kContour
@ kContour