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 class  ComponentType {
  kLinear ,
  kQuadratic ,
  kCubic ,
  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< 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 53 of file path.h.

Member Enumeration Documentation

◆ ComponentType

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) {
80  points += 2;
81  break;
82  }
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  }
98  contours++;
99  }
100  storage_offset += VerbToOffset(path_component);
101  }
102  return std::make_pair(points, contours);
103 }
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:62
const Scalar scale

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

References impeller::CubicPathComponent::AppendPolylinePoints(), impeller::QuadraticPathComponent::AppendPolylinePoints(), impeller::LinearPathComponent::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  }
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  }
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 }
GLenum type

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

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

Definition at line 62 of file path.h.

62  {
63  switch (verb) {
65  return 2u;
67  return 3u;
69  return 4u;
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  }
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  }
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::CubicPathComponent::ToLinearPathComponents(), impeller::QuadraticPathComponent::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: