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 ()
 
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< RectGetBoundingBox () const
 
std::optional< RectGetTransformedBoundingBox (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

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) {
281  return reinterpret_cast<const LinearPathComponent*>(
282  &path_points[component.index]);
284  return reinterpret_cast<const QuadraticPathComponent*>(
285  &path_points[component.index]);
287  return reinterpret_cast<const CubicPathComponent*>(
288  &path_points[component.index]);
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) {
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;
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;
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;
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) {
73  if (linear_applier) {
74  linear_applier(currentIndex,
75  LinearPathComponent(points[component.index],
76  points[component.index + 1]));
77  }
78  break;
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;
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;
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  }
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  }
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:
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