| 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
class | ComponentIterator
struct | Polyline
struct | PolylineContour

## Public Types
enum class | ComponentType { kLinear , kQuadratic , kConic , kCubic , kContour }

## Public Member Functions
| Path ()
| ~Path ()
size_t | GetComponentCount (std::optional< ComponentType > type={}) const
size_t | GetPointCount () const
FillType | GetFillType () const
bool | IsConvex () const
bool | IsEmpty () const
bool | IsSingleContour () const
| Whether the line contains a single contour. More...
ComponentIterator | begin () const
ComponentIterator | end () 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 54 of file path.h.

## Member Enumeration Documentation

## ◆ ComponentType

enum impeller::Path::ComponentType
| strong

Enumerator
kLinear |
kQuadratic |
kConic |
kCubic |
kContour |

Definition at line 56 of file path.h.

56 {
57 kLinear,
58 kQuadratic,
59 kConic,
60 kCubic,
61 kContour,
62 };
impeller::MinMagFilter::kLinear
@ kLinear

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

## ◆ begin()

Path::ComponentIterator impeller::Path::begin | ( |  | ) | const

Definition at line 253 of file path.cc.

253 {
254 return ComponentIterator(*this, 0u, 0u);
255 }

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

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

129 {
130 size_t points = 0;
131 size_t contours = 0;
132
133 auto& path_components = data_->components;
134 auto& path_points = data_->points;
135
136 size_t storage_offset = 0u;
137 for (size_t component_i = 0; component_i < path_components.size();
138 component_i++) {
139 const auto& path_component = path_components[component_i];
140 switch (path_component) {
141 case ComponentType::kLinear: {
142 points += 2;
143 break;
144 }
145 case ComponentType::kQuadratic: {
146 const QuadraticPathComponent* quad =
147 reinterpret_cast<const QuadraticPathComponent*>(
148 &path_points[storage_offset]);
149 points += quad->CountLinearPathComponents(scale);
150 break;
151 }
152 case ComponentType::kConic: {
153 const ConicPathComponent* conic =
154 reinterpret_cast<const ConicPathComponent*>(
155 &path_points[storage_offset]);
156 points += conic->CountLinearPathComponents(scale);
157 break;
158 }
159 case ComponentType::kCubic: {
160 const CubicPathComponent* cubic =
161 reinterpret_cast<const CubicPathComponent*>(
162 &path_points[storage_offset]);
163 points += cubic->CountLinearPathComponents(scale);
164 break;
165 }
166 case Path::ComponentType::kContour:
167 contours++;
168 }
169 storage_offset += VerbToOffset(path_component);
170 }
171 return std::make_pair(points, contours);
172 }
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kConic
@ kConic
impeller::Path::ComponentType::kContour
@ kContour
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::VerbToOffset
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:95
scale
const Scalar scale
Definition: stroke_path_geometry.cc:319

References impeller::QuadraticPathComponent::CountLinearPathComponents(), impeller::ConicPathComponent::CountLinearPathComponents(), impeller::CubicPathComponent::CountLinearPathComponents(), kConic, 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 355 of file path.cc.

358 {
359 Polyline polyline(std::move(point_buffer), std::move(reclaim));
360
361 auto& path_components = data_->components;
362 auto& path_points = data_->points;
363 std::optional<Vector2> start_direction;
364 std::vector<PolylineContour::Component> poly_components;
365 size_t storage_offset = 0u;
366 size_t component_i = 0;
367
368 for (; component_i < path_components.size(); component_i++) {
369 auto path_component = path_components[component_i];
370 switch (path_component) {
371 case ComponentType::kLinear: {
372 poly_components.push_back({
373 .component_start_index = polyline.points->size() - 1,
374 .is_curve = false,
375 });
376 auto* linear = reinterpret_cast<const LinearPathComponent*>(
377 &path_points[storage_offset]);
378 linear->AppendPolylinePoints(*polyline.points);
379 if (!start_direction.has_value()) {
380 start_direction = linear->GetStartDirection();
381 }
382 break;
383 }
384 case ComponentType::kQuadratic: {
385 poly_components.push_back({
386 .component_start_index = polyline.points->size() - 1,
387 .is_curve = true,
388 });
389 auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
390 &path_points[storage_offset]);
391 quad->AppendPolylinePoints(scale, *polyline.points);
392 if (!start_direction.has_value()) {
393 start_direction = quad->GetStartDirection();
394 }
395 break;
396 }
397 case ComponentType::kConic: {
398 poly_components.push_back({
399 .component_start_index = polyline.points->size() - 1,
400 .is_curve = true,
401 });
402 auto* conic = reinterpret_cast<const ConicPathComponent*>(
403 &path_points[storage_offset]);
404 conic->AppendPolylinePoints(scale, *polyline.points);
405 if (!start_direction.has_value()) {
406 start_direction = conic->GetStartDirection();
407 }
408 break;
409 }
410 case ComponentType::kCubic: {
411 poly_components.push_back({
412 .component_start_index = polyline.points->size() - 1,
413 .is_curve = true,
414 });
415 auto* cubic = reinterpret_cast<const CubicPathComponent*>(
416 &path_points[storage_offset]);
417 cubic->AppendPolylinePoints(scale, *polyline.points);
418 if (!start_direction.has_value()) {
419 start_direction = cubic->GetStartDirection();
420 }
421 break;
422 }
423 case ComponentType::kContour:
424 if (component_i == path_components.size() - 1) {
425 // If the last component is a contour, that means it's an empty
426 // contour, so skip it.
427 break;
428 }
429 if (!polyline.contours.empty()) {
430 polyline.contours.back().start_direction =
431 start_direction.value_or(Vector2(0, -1));
432 start_direction = std::nullopt;
433 }
434 EndContour(storage_offset, polyline, component_i, poly_components);
435
436 auto* contour = reinterpret_cast<const ContourComponent*>(
437 &path_points[storage_offset]);
438 polyline.contours.push_back(PolylineContour{
439 .start_index = polyline.points->size(), //
440 .is_closed = contour->IsClosed(), //
441 .start_direction = Vector2(0, -1), //
442 .components = poly_components //
443 });
444
445 polyline.points->push_back(contour->destination);
446 break;
447 }
448 storage_offset += VerbToOffset(path_component);
449 }
450
451 // Subtract the last storage offset increment so that the storage lookup is
452 // correct, including potentially an empty contour as well.
453 if (component_i > 0 && path_components.back() == ComponentType::kContour) {
454 storage_offset -= VerbToOffset(ComponentType::kContour);
455 component_i--;
456 }
457
458 if (!polyline.contours.empty()) {
459 polyline.contours.back().start_direction =
460 start_direction.value_or(Vector2(0, -1));
461 }
462 EndContour(storage_offset, polyline, component_i, poly_components);
463 return polyline;
464 }
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:281
impeller::Vector2
Point Vector2
Definition: point.h:331
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:314

References impeller::CubicPathComponent::AppendPolylinePoints(), impeller::QuadraticPathComponent::AppendPolylinePoints(), impeller::ConicPathComponent::AppendPolylinePoints(), impeller::LinearPathComponent::AppendPolylinePoints(), EndContour(), kConic, 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().

## ◆ end()

Path::ComponentIterator impeller::Path::end | ( |  | ) | const

Definition at line 257 of file path.cc.

257 {
258 return ComponentIterator(*this, data_->components.size(),
259 data_->points.size());
260 }

Referenced by 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 281 of file path.cc.

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

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

Referenced by CreatePolyline().

## ◆ GetBoundingBox()

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

Definition at line 466 of file path.cc.

466 {
467 return data_->bounds;
468 }

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 92 of file path.cc.

92 {
93 if (!type.has_value()) {
94 return data_->components.size();
95 }
96 auto type_value = type.value();
97 size_t count = 0u;
98 for (const auto& component : data_->components) {
99 if (component == type_value) {
100 count++;
101 }
102 }
103 return count;
104 }
type
GLenum type
Definition: blit_command_gles.cc:151

References type.

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

## ◆ GetFillType()

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

Definition at line 110 of file path.cc.

110 {
111 return data_->fill;
112 }

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

## ◆ GetPointCount()

size_t impeller::Path::GetPointCount | ( |  | ) | const

Definition at line 106 of file path.cc.

106 {
107 return data_->points.size();
108 }

## ◆ GetTransformedBoundingBox()

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

Definition at line 470 of file path.cc.

471 {
472 auto bounds = GetBoundingBox();
473 if (!bounds.has_value()) {
474 return std::nullopt;
475 }
476 return bounds->TransformBounds(transform);
477 }
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:466
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214

References GetBoundingBox(), and transform.

## ◆ IsConvex()

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

Definition at line 114 of file path.cc.

114 {
115 return data_->convexity == Convexity::kConvex;
116 }
impeller::Convexity::kConvex
@ kConvex

References impeller::kConvex.

## ◆ IsEmpty()

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

Definition at line 118 of file path.cc.

118 {
119 return data_->points.empty() ||
120 (data_->components.size() == 1 &&
121 data_->components[0] == ComponentType::kContour);
122 }

References kContour.

## ◆ IsSingleContour()

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

Whether the line contains a single contour.

Definition at line 124 of file path.cc.

124 {
125 return data_->single_contour;
126 }

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

## ◆ VerbToOffset()

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

Definition at line 95 of file path.h.

95 {
96 switch (verb) {
97 case Path::ComponentType::kLinear:
98 return 2u;
99 case Path::ComponentType::kQuadratic:
100 return 3u;
101 case Path::ComponentType::kConic:
102 return 4u;
103 case Path::ComponentType::kCubic:
104 return 4u;
105 case Path::ComponentType::kContour:
106 return 2u;
107 break;
108 }
109 FML_UNREACHABLE();
110 }

References kConic, kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::PathBuilder::AddPath(), CountStorage(), CreatePolyline(), EndContour(), impeller::Path::ComponentIterator::operator++(), 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 174 of file path.cc.

174 {
175 auto& path_components = data_->components;
176 auto& path_points = data_->points;
177 bool started_contour = false;
178 bool first_point = true;
179
180 size_t storage_offset = 0u;
181 for (size_t component_i = 0; component_i < path_components.size();
182 component_i++) {
183 const auto& path_component = path_components[component_i];
184 switch (path_component) {
185 case ComponentType::kLinear: {
186 const LinearPathComponent* linear =
187 reinterpret_cast<const LinearPathComponent*>(
188 &path_points[storage_offset]);
189 if (first_point) {
190 writer.Write(linear->p1);
191 first_point = false;
192 }
193 writer.Write(linear->p2);
194 break;
195 }
196 case ComponentType::kQuadratic: {
197 const QuadraticPathComponent* quad =
198 reinterpret_cast<const QuadraticPathComponent*>(
199 &path_points[storage_offset]);
200 if (first_point) {
201 writer.Write(quad->p1);
202 first_point = false;
203 }
204 quad->ToLinearPathComponents(scale, writer);
205 break;
206 }
207 case ComponentType::kConic: {
208 const ConicPathComponent* conic =
209 reinterpret_cast<const ConicPathComponent*>(
210 &path_points[storage_offset]);
211 if (first_point) {
212 writer.Write(conic->p1);
213 first_point = false;
214 }
215 conic->ToLinearPathComponents(scale, writer);
216 break;
217 }
218 case ComponentType::kCubic: {
219 const CubicPathComponent* cubic =
220 reinterpret_cast<const CubicPathComponent*>(
221 &path_points[storage_offset]);
222 if (first_point) {
223 writer.Write(cubic->p1);
224 first_point = false;
225 }
226 cubic->ToLinearPathComponents(scale, writer);
227 break;
228 }
229 case Path::ComponentType::kContour:
230 if (component_i == path_components.size() - 1) {
231 // If the last component is a contour, that means it's an empty
232 // contour, so skip it.
233 continue;
234 }
235 // The contour component type is the first segment in a contour.
236 // Since this should contain the destination (if closed), we
237 // can start with this point. If there was already an open
238 // contour, or we've reached the end of the verb list, we
239 // also close the contour.
240 if (started_contour) {
241 writer.EndContour();
242 }
243 started_contour = true;
244 first_point = true;
245 }
246 storage_offset += VerbToOffset(path_component);
247 }
248 if (started_contour) {
249 writer.EndContour();
250 }
251 }

References impeller::VertexWriter::EndContour(), kConic, kContour, kCubic, kLinear, kQuadratic, impeller::LinearPathComponent::p1, impeller::QuadraticPathComponent::p1, impeller::ConicPathComponent::p1, impeller::CubicPathComponent::p1, impeller::LinearPathComponent::p2, scale, impeller::CubicPathComponent::ToLinearPathComponents(), impeller::QuadraticPathComponent::ToLinearPathComponents(), impeller::ConicPathComponent::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 233 of file path.h.

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

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