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
}
 

Public Member Functions

 Path ()
 
 ~Path ()
 
size_t GetComponentCount (std::optional< ComponentType > type={}) const
 
FillType GetFillType () const
 
bool IsConvex () const
 
bool IsEmpty () 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
 

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

15 : 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 289 of file path.cc.

292  {
293  Polyline polyline(std::move(point_buffer), std::move(reclaim));
294 
295  auto& path_components = data_->components;
296  auto& path_points = data_->points;
297  std::optional<Vector2> start_direction;
298  std::vector<PolylineContour::Component> poly_components;
299  size_t storage_offset = 0u;
300  size_t component_i = 0;
301 
302  for (; component_i < path_components.size(); component_i++) {
303  auto path_component = path_components[component_i];
304  switch (path_component) {
305  case ComponentType::kLinear: {
306  poly_components.push_back({
307  .component_start_index = polyline.points->size() - 1,
308  .is_curve = false,
309  });
310  auto* linear = reinterpret_cast<const LinearPathComponent*>(
311  &path_points[storage_offset]);
312  linear->AppendPolylinePoints(*polyline.points);
313  if (!start_direction.has_value()) {
314  start_direction = linear->GetStartDirection();
315  }
316  break;
317  }
319  poly_components.push_back({
320  .component_start_index = polyline.points->size() - 1,
321  .is_curve = true,
322  });
323  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
324  &path_points[storage_offset]);
325  quad->AppendPolylinePoints(scale, *polyline.points);
326  if (!start_direction.has_value()) {
327  start_direction = quad->GetStartDirection();
328  }
329  break;
330  }
331  case ComponentType::kCubic: {
332  poly_components.push_back({
333  .component_start_index = polyline.points->size() - 1,
334  .is_curve = true,
335  });
336  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
337  &path_points[storage_offset]);
338  cubic->AppendPolylinePoints(scale, *polyline.points);
339  if (!start_direction.has_value()) {
340  start_direction = cubic->GetStartDirection();
341  }
342  break;
343  }
345  if (component_i == path_components.size() - 1) {
346  // If the last component is a contour, that means it's an empty
347  // contour, so skip it.
348  break;
349  }
350  if (!polyline.contours.empty()) {
351  polyline.contours.back().start_direction =
352  start_direction.value_or(Vector2(0, -1));
353  start_direction = std::nullopt;
354  }
355  EndContour(storage_offset, polyline, component_i, poly_components);
356 
357  auto* contour = reinterpret_cast<const ContourComponent*>(
358  &path_points[storage_offset]);
359  polyline.contours.push_back(PolylineContour{
360  .start_index = polyline.points->size(), //
361  .is_closed = contour->IsClosed(), //
362  .start_direction = Vector2(0, -1), //
363  .components = poly_components //
364  });
365 
366  polyline.points->push_back(contour->destination);
367  break;
368  }
369  storage_offset += VerbToOffset(path_component);
370  }
371 
372  // Subtract the last storage offset increment so that the storage lookup is
373  // correct, including potentially an empty contour as well.
374  if (component_i > 0 && path_components.back() == ComponentType::kContour) {
375  storage_offset -= VerbToOffset(ComponentType::kContour);
376  component_i--;
377  }
378 
379  if (!polyline.contours.empty()) {
380  polyline.contours.back().start_direction =
381  start_direction.value_or(Vector2(0, -1));
382  }
383  EndContour(storage_offset, polyline, component_i, poly_components);
384  return polyline;
385 }

References impeller::LinearPathComponent::AppendPolylinePoints(), impeller::QuadraticPathComponent::AppendPolylinePoints(), impeller::CubicPathComponent::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 225 of file path.cc.

229  {
230  auto& path_components = data_->components;
231  auto& path_points = data_->points;
232  // Whenever a contour has ended, extract the exact end direction from
233  // the last component.
234  if (polyline.contours.empty() || component_index == 0) {
235  return;
236  }
237 
238  auto& contour = polyline.contours.back();
239  contour.end_direction = Vector2(0, 1);
240  contour.components = poly_components;
241  poly_components.clear();
242 
243  size_t previous_index = component_index - 1;
244  storage_offset -= VerbToOffset(path_components[previous_index]);
245 
246  while (previous_index >= 0 && storage_offset >= 0) {
247  const auto& path_component = path_components[previous_index];
248  switch (path_component) {
249  case ComponentType::kLinear: {
250  auto* linear = reinterpret_cast<const LinearPathComponent*>(
251  &path_points[storage_offset]);
252  auto maybe_end = linear->GetEndDirection();
253  if (maybe_end.has_value()) {
254  contour.end_direction = maybe_end.value();
255  return;
256  }
257  break;
258  }
260  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
261  &path_points[storage_offset]);
262  auto maybe_end = quad->GetEndDirection();
263  if (maybe_end.has_value()) {
264  contour.end_direction = maybe_end.value();
265  return;
266  }
267  break;
268  }
269  case ComponentType::kCubic: {
270  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
271  &path_points[storage_offset]);
272  auto maybe_end = cubic->GetEndDirection();
273  if (maybe_end.has_value()) {
274  contour.end_direction = maybe_end.value();
275  return;
276  }
277  break;
278  }
280  // Hit previous contour, return.
281  return;
282  };
283  }
284  storage_offset -= VerbToOffset(path_component);
285  previous_index--;
286  }
287 };

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

387  {
388  return data_->bounds;
389 }

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

33  {
34  if (!type.has_value()) {
35  return data_->components.size();
36  }
37  auto type_value = type.value();
38  size_t count = 0u;
39  for (const auto& component : data_->components) {
40  if (component == type_value) {
41  count++;
42  }
43  }
44  return count;
45 }

References type.

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

◆ GetContourComponentAtIndex()

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

Definition at line 188 of file path.cc.

189  {
190  auto& components = data_->components;
191  if (index >= components.size() ||
192  components[index] != ComponentType::kContour) {
193  return false;
194  }
195 
196  size_t storage_offset = 0u;
197  for (auto i = 0u; i < index; i++) {
198  storage_offset += VerbToOffset(components[i]);
199  }
200  auto& points = data_->points;
201 
202  move = ContourComponent(points[storage_offset], points[storage_offset + 1]);
203  return true;
204 }

References kContour, and VerbToOffset().

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

◆ GetCubicComponentAtIndex()

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

Definition at line 168 of file path.cc.

169  {
170  auto& components = data_->components;
171  if (index >= components.size() ||
172  components[index] != ComponentType::kCubic) {
173  return false;
174  }
175 
176  size_t storage_offset = 0u;
177  for (auto i = 0u; i < index; i++) {
178  storage_offset += VerbToOffset(components[i]);
179  }
180  auto& points = data_->points;
181 
182  cubic = CubicPathComponent(points[storage_offset], points[storage_offset + 1],
183  points[storage_offset + 2],
184  points[storage_offset + 3]);
185  return true;
186 }

References kCubic, and VerbToOffset().

◆ GetFillType()

FillType impeller::Path::GetFillType ( ) const

Definition at line 47 of file path.cc.

47  {
48  return data_->fill;
49 }

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

◆ GetLinearComponentAtIndex()

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

Definition at line 129 of file path.cc.

130  {
131  auto& components = data_->components;
132  if (index >= components.size() ||
133  components[index] != ComponentType::kLinear) {
134  return false;
135  }
136 
137  size_t storage_offset = 0u;
138  for (auto i = 0u; i < index; i++) {
139  storage_offset += VerbToOffset(components[i]);
140  }
141  auto& points = data_->points;
142  linear =
143  LinearPathComponent(points[storage_offset], points[storage_offset + 1]);
144  return true;
145 }

References kLinear, and VerbToOffset().

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

◆ GetQuadraticComponentAtIndex()

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

Definition at line 147 of file path.cc.

149  {
150  auto& components = data_->components;
151  if (index >= components.size() ||
152  components[index] != ComponentType::kQuadratic) {
153  return false;
154  }
155 
156  size_t storage_offset = 0u;
157  for (auto i = 0u; i < index; i++) {
158  storage_offset += VerbToOffset(components[i]);
159  }
160  auto& points = data_->points;
161 
162  quadratic =
163  QuadraticPathComponent(points[storage_offset], points[storage_offset + 1],
164  points[storage_offset + 2]);
165  return true;
166 }

References kQuadratic, and VerbToOffset().

◆ GetTransformedBoundingBox()

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

Definition at line 391 of file path.cc.

392  {
393  auto bounds = GetBoundingBox();
394  if (!bounds.has_value()) {
395  return std::nullopt;
396  }
397  return bounds->TransformBounds(transform);
398 }

References GetBoundingBox(), and transform.

◆ IsConvex()

bool impeller::Path::IsConvex ( ) const

Definition at line 51 of file path.cc.

51  {
52  return data_->convexity == Convexity::kConvex;
53 }

References impeller::kConvex.

◆ IsEmpty()

bool impeller::Path::IsEmpty ( ) const

Definition at line 55 of file path.cc.

55  {
56  return data_->points.empty() ||
57  (data_->components.size() == 1 &&
58  data_->components[0] == ComponentType::kContour);
59 }

References kContour.

◆ VerbToOffset()

static constexpr size_t impeller::Path::VerbToOffset ( Path::ComponentType  verb)
inlinestaticconstexpr

Definition at line 61 of file path.h.

61  {
62  switch (verb) {
64  return 2u;
66  return 3u;
68  return 4u;
70  return 2u;
71  break;
72  }
73  FML_UNREACHABLE();
74  }

References kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::PathBuilder::AddPath(), 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 61 of file path.cc.

61  {
62  auto& path_components = data_->components;
63  auto& path_points = data_->points;
64  bool started_contour = false;
65  bool first_point = true;
66 
67  size_t storage_offset = 0u;
68  for (size_t component_i = 0; component_i < path_components.size();
69  component_i++) {
70  const auto& path_component = path_components[component_i];
71  switch (path_component) {
73  const LinearPathComponent* linear =
74  reinterpret_cast<const LinearPathComponent*>(
75  &path_points[storage_offset]);
76  if (first_point) {
77  writer.Write(linear->p1);
78  first_point = false;
79  }
80  writer.Write(linear->p2);
81  break;
82  }
84  const QuadraticPathComponent* quad =
85  reinterpret_cast<const QuadraticPathComponent*>(
86  &path_points[storage_offset]);
87  if (first_point) {
88  writer.Write(quad->p1);
89  first_point = false;
90  }
91  quad->ToLinearPathComponents(scale, writer);
92  break;
93  }
94  case ComponentType::kCubic: {
95  const CubicPathComponent* cubic =
96  reinterpret_cast<const CubicPathComponent*>(
97  &path_points[storage_offset]);
98  if (first_point) {
99  writer.Write(cubic->p1);
100  first_point = false;
101  }
102  cubic->ToLinearPathComponents(scale, writer);
103  break;
104  }
106  if (component_i == path_components.size() - 1) {
107  // If the last component is a contour, that means it's an empty
108  // contour, so skip it.
109  continue;
110  }
111  // The contour component type is the first segment in a contour.
112  // Since this should contain the destination (if closed), we
113  // can start with this point. If there was already an open
114  // contour, or we've reached the end of the verb list, we
115  // also close the contour.
116  if (started_contour) {
117  writer.EndContour();
118  }
119  started_contour = true;
120  first_point = true;
121  }
122  storage_offset += VerbToOffset(path_component);
123  }
124  if (started_contour) {
125  writer.EndContour();
126  }
127 }

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(), VerbToOffset(), and impeller::VertexWriter::Write().

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

Friends And Related Function Documentation

◆ PathBuilder

friend class PathBuilder
friend

Definition at line 197 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:296
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:387
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
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:225
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:213
impeller::MinMagFilter::kLinear
@ kLinear
type
GLenum type
Definition: blit_command_gles.cc:127
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::Path::VerbToOffset
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:61
impeller::Convexity::kConvex
@ kConvex
impeller::Path::ComponentType::kContour
@ kContour