Flutter Impeller
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 ()
 
 Path (Path &&other)=default
 
Path Clone () const
 Deeply clone this path and all data associated with it. More...
 
size_t GetComponentCount (std::optional< ComponentType > type={}) const
 
FillType GetFillType () const
 
bool IsConvex () 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< RectGetBoundingBox () const
 
std::optional< RectGetTransformedBoundingBox (const Matrix &transform) const
 
std::optional< std::pair< Point, Point > > GetMinMaxCoveragePoints () 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 55 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 148 of file path.h.

Member Enumeration Documentation

◆ ComponentType

Enumerator
kLinear 
kQuadratic 
kCubic 
kContour 

Definition at line 57 of file path.h.

57  {
58  kLinear,
59  kQuadratic,
60  kCubic,
61  kContour,
62  };

Constructor & Destructor Documentation

◆ Path() [1/2]

impeller::Path::Path ( )

Definition at line 16 of file path.cc.

16  {
17  AddContourComponent({});
18 };

◆ ~Path()

impeller::Path::~Path ( )
default

◆ Path() [2/2]

impeller::Path::Path ( Path &&  other)
default

Member Function Documentation

◆ Clone()

Path impeller::Path::Clone ( ) const

Deeply clone this path and all data associated with it.

Definition at line 76 of file path.cc.

76  {
77  Path new_path = *this;
78  return new_path;
79 }

Referenced by impeller::PathBuilder::CopyPath(), impeller::testing::TEST(), and impeller::testing::TEST_P().

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

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

References impeller::Path::Polyline::contours, kContour, kCubic, kLinear, kQuadratic, and impeller::Path::Polyline::points.

Referenced by impeller::Tessellator::Tessellate(), impeller::Tessellator::TessellateConvex(), 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 129 of file path.cc.

133  {
134  size_t currentIndex = 0;
135  for (const auto& component : components_) {
136  switch (component.type) {
138  if (linear_applier) {
139  linear_applier(currentIndex,
140  LinearPathComponent(points_[component.index],
141  points_[component.index + 1]));
142  }
143  break;
145  if (quad_applier) {
146  quad_applier(currentIndex,
147  QuadraticPathComponent(points_[component.index],
148  points_[component.index + 1],
149  points_[component.index + 2]));
150  }
151  break;
153  if (cubic_applier) {
154  cubic_applier(currentIndex,
155  CubicPathComponent(points_[component.index],
156  points_[component.index + 1],
157  points_[component.index + 2],
158  points_[component.index + 3]));
159  }
160  break;
162  if (contour_applier) {
163  contour_applier(currentIndex, contours_[component.index]);
164  }
165  break;
166  }
167  currentIndex++;
168  }
169 }

References kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::PathBuilder::AddPath(), and impeller::ComputeTessellator::Tessellate().

◆ GetBoundingBox()

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

Definition at line 388 of file path.cc.

388  {
389  return computed_bounds_;
390 }

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

◆ 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 components_.size();
37  }
38  auto type_value = type.value();
39  if (type_value == ComponentType::kContour) {
40  return contours_.size();
41  }
42  size_t count = 0u;
43  for (const auto& component : components_) {
44  if (component.type == type_value) {
45  count++;
46  }
47  }
48  return count;
49 }

References kContour.

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

◆ GetContourComponentAtIndex()

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

Definition at line 220 of file path.cc.

221  {
222  if (index >= components_.size()) {
223  return false;
224  }
225 
226  if (components_[index].type != ComponentType::kContour) {
227  return false;
228  }
229 
230  move = contours_[components_[index].index];
231  return true;
232 }

References kContour.

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

◆ GetCubicComponentAtIndex()

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

Definition at line 203 of file path.cc.

204  {
205  if (index >= components_.size()) {
206  return false;
207  }
208 
209  if (components_[index].type != ComponentType::kCubic) {
210  return false;
211  }
212 
213  auto point_index = components_[index].index;
214  cubic =
215  CubicPathComponent(points_[point_index], points_[point_index + 1],
216  points_[point_index + 2], points_[point_index + 3]);
217  return true;
218 }

References kCubic.

◆ GetFillType()

FillType impeller::Path::GetFillType ( ) const

Definition at line 55 of file path.cc.

55  {
56  return fill_;
57 }

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

◆ GetLinearComponentAtIndex()

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

Definition at line 171 of file path.cc.

172  {
173  if (index >= components_.size()) {
174  return false;
175  }
176 
177  if (components_[index].type != ComponentType::kLinear) {
178  return false;
179  }
180 
181  auto point_index = components_[index].index;
182  linear = LinearPathComponent(points_[point_index], points_[point_index + 1]);
183  return true;
184 }

References kLinear.

◆ GetMinMaxCoveragePoints()

std::optional< std::pair< Point, Point > > impeller::Path::GetMinMaxCoveragePoints ( ) const

Definition at line 413 of file path.cc.

413  {
414  if (points_.empty()) {
415  return std::nullopt;
416  }
417 
418  std::optional<Point> min, max;
419 
420  auto clamp = [&min, &max](const Point& point) {
421  if (min.has_value()) {
422  min = min->Min(point);
423  } else {
424  min = point;
425  }
426 
427  if (max.has_value()) {
428  max = max->Max(point);
429  } else {
430  max = point;
431  }
432  };
433 
434  for (const auto& component : components_) {
435  switch (component.type) {
436  case ComponentType::kLinear: {
437  auto* linear = reinterpret_cast<const LinearPathComponent*>(
438  &points_[component.index]);
439  clamp(linear->p1);
440  clamp(linear->p2);
441  break;
442  }
444  for (const auto& extrema :
445  reinterpret_cast<const QuadraticPathComponent*>(
446  &points_[component.index])
447  ->Extrema()) {
448  clamp(extrema);
449  }
450  break;
452  for (const auto& extrema : reinterpret_cast<const CubicPathComponent*>(
453  &points_[component.index])
454  ->Extrema()) {
455  clamp(extrema);
456  }
457  break;
459  break;
460  }
461  }
462 
463  if (!min.has_value() || !max.has_value()) {
464  return std::nullopt;
465  }
466 
467  return std::make_pair(min.value(), max.value());
468 }

References impeller::QuadraticPathComponent::Extrema(), impeller::CubicPathComponent::Extrema(), kContour, kCubic, kLinear, and kQuadratic.

◆ GetQuadraticComponentAtIndex()

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

Definition at line 186 of file path.cc.

188  {
189  if (index >= components_.size()) {
190  return false;
191  }
192 
193  if (components_[index].type != ComponentType::kQuadratic) {
194  return false;
195  }
196 
197  auto point_index = components_[index].index;
198  quadratic = QuadraticPathComponent(
199  points_[point_index], points_[point_index + 1], points_[point_index + 2]);
200  return true;
201 }

References kQuadratic.

◆ GetTransformedBoundingBox()

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

Definition at line 404 of file path.cc.

405  {
406  auto bounds = GetBoundingBox();
407  if (!bounds.has_value()) {
408  return std::nullopt;
409  }
410  return bounds->TransformBounds(transform);
411 }

References GetBoundingBox().

◆ IsConvex()

bool impeller::Path::IsConvex ( ) const

Definition at line 59 of file path.cc.

59  {
60  return convexity_ == Convexity::kConvex;
61 }

References impeller::kConvex.

Friends And Related Function Documentation

◆ PathBuilder

friend class PathBuilder
friend

Definition at line 185 of file path.h.


The documentation for this class was generated from the following files:
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:312
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:388
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Point
TPoint< Scalar > Point
Definition: point.h:308
impeller::MinMagFilter::kLinear
@ kLinear
impeller::Path::Path
Path()
Definition: path.cc:16
impeller::PathComponentVariant
std::variant< std::monostate, const LinearPathComponent *, const QuadraticPathComponent *, const CubicPathComponent * > PathComponentVariant
Definition: path_component.h:163
impeller::Convexity::kConvex
@ kConvex
impeller::Path::ComponentType::kContour
@ kContour