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

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

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  };

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  }
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  }
167  contours++;
168  }
169  storage_offset += VerbToOffset(path_component);
170  }
171  return std::make_pair(points, contours);
172 }
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:95
const Scalar scale

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  }
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  }
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 }
void EndContour(size_t storage_offset, Polyline &polyline, size_t component_index, std::vector< PolylineContour::Component > &poly_components) const
Definition: path.cc:281
Point Vector2
Definition: point.h:331
const Path::Polyline & polyline

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  }
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  }
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 }
GLenum type

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 }
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:466

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 }

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)
inlinestaticconstexpr

Definition at line 95 of file path.h.

95  {
96  switch (verb) {
98  return 2u;
100  return 3u;
102  return 4u;
104  return 4u;
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  }
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  }
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: