Flutter Impeller
path.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <optional>
8 #include <variant>
9 
11 
12 namespace impeller {
13 
15  AddContourComponent({});
16 };
17 
18 Path::~Path() = default;
19 
20 std::tuple<size_t, size_t> Path::Polyline::GetContourPointBounds(
21  size_t contour_index) const {
22  if (contour_index >= contours.size()) {
23  return {points.size(), points.size()};
24  }
25  const size_t start_index = contours.at(contour_index).start_index;
26  const size_t end_index = (contour_index >= contours.size() - 1)
27  ? points.size()
28  : contours.at(contour_index + 1).start_index;
29  return std::make_tuple(start_index, end_index);
30 }
31 
32 size_t Path::GetComponentCount(std::optional<ComponentType> type) const {
33  if (type.has_value()) {
34  switch (type.value()) {
36  return linears_.size();
38  return quads_.size();
40  return cubics_.size();
42  return contours_.size();
43  }
44  }
45  return components_.size();
46 }
47 
48 void Path::SetFillType(FillType fill) {
49  fill_ = fill;
50 }
51 
53  return fill_;
54 }
55 
56 bool Path::IsConvex() const {
57  return convexity_ == Convexity::kConvex;
58 }
59 
60 void Path::SetConvexity(Convexity value) {
61  convexity_ = value;
62 }
63 
64 void Path::Shift(Point shift) {
65  size_t currentIndex = 0;
66  for (const auto& component : components_) {
67  switch (component.type) {
69  linears_[component.index].p1 += shift;
70  linears_[component.index].p2 += shift;
71  break;
73  quads_[component.index].cp += shift;
74  quads_[component.index].p1 += shift;
75  quads_[component.index].p2 += shift;
76  break;
78  cubics_[component.index].cp1 += shift;
79  cubics_[component.index].cp2 += shift;
80  cubics_[component.index].p1 += shift;
81  cubics_[component.index].p2 += shift;
82  break;
84  contours_[component.index].destination += shift;
85  break;
86  }
87  currentIndex++;
88  }
89 }
90 
91 Path& Path::AddLinearComponent(Point p1, Point p2) {
92  linears_.emplace_back(p1, p2);
93  components_.emplace_back(ComponentType::kLinear, linears_.size() - 1);
94  return *this;
95 }
96 
97 Path& Path::AddQuadraticComponent(Point p1, Point cp, Point p2) {
98  quads_.emplace_back(p1, cp, p2);
99  components_.emplace_back(ComponentType::kQuadratic, quads_.size() - 1);
100  return *this;
101 }
102 
103 Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) {
104  cubics_.emplace_back(p1, cp1, cp2, p2);
105  components_.emplace_back(ComponentType::kCubic, cubics_.size() - 1);
106  return *this;
107 }
108 
109 Path& Path::AddContourComponent(Point destination, bool is_closed) {
110  if (components_.size() > 0 &&
111  components_.back().type == ComponentType::kContour) {
112  // Never insert contiguous contours.
113  contours_.back() = ContourComponent(destination, is_closed);
114  } else {
115  contours_.emplace_back(ContourComponent(destination, is_closed));
116  components_.emplace_back(ComponentType::kContour, contours_.size() - 1);
117  }
118  return *this;
119 }
120 
121 void Path::SetContourClosed(bool is_closed) {
122  contours_.back().is_closed = is_closed;
123 }
124 
126  const Applier<LinearPathComponent>& linear_applier,
127  const Applier<QuadraticPathComponent>& quad_applier,
128  const Applier<CubicPathComponent>& cubic_applier,
129  const Applier<ContourComponent>& contour_applier) const {
130  size_t currentIndex = 0;
131  for (const auto& component : components_) {
132  switch (component.type) {
134  if (linear_applier) {
135  linear_applier(currentIndex, linears_[component.index]);
136  }
137  break;
139  if (quad_applier) {
140  quad_applier(currentIndex, quads_[component.index]);
141  }
142  break;
144  if (cubic_applier) {
145  cubic_applier(currentIndex, cubics_[component.index]);
146  }
147  break;
149  if (contour_applier) {
150  contour_applier(currentIndex, contours_[component.index]);
151  }
152  break;
153  }
154  currentIndex++;
155  }
156 }
157 
159  LinearPathComponent& linear) const {
160  if (index >= components_.size()) {
161  return false;
162  }
163 
164  if (components_[index].type != ComponentType::kLinear) {
165  return false;
166  }
167 
168  linear = linears_[components_[index].index];
169  return true;
170 }
171 
173  size_t index,
174  QuadraticPathComponent& quadratic) const {
175  if (index >= components_.size()) {
176  return false;
177  }
178 
179  if (components_[index].type != ComponentType::kQuadratic) {
180  return false;
181  }
182 
183  quadratic = quads_[components_[index].index];
184  return true;
185 }
186 
188  CubicPathComponent& cubic) const {
189  if (index >= components_.size()) {
190  return false;
191  }
192 
193  if (components_[index].type != ComponentType::kCubic) {
194  return false;
195  }
196 
197  cubic = cubics_[components_[index].index];
198  return true;
199 }
200 
202  ContourComponent& move) const {
203  if (index >= components_.size()) {
204  return false;
205  }
206 
207  if (components_[index].type != ComponentType::kContour) {
208  return false;
209  }
210 
211  move = contours_[components_[index].index];
212  return true;
213 }
214 
215 bool Path::UpdateLinearComponentAtIndex(size_t index,
216  const LinearPathComponent& linear) {
217  if (index >= components_.size()) {
218  return false;
219  }
220 
221  if (components_[index].type != ComponentType::kLinear) {
222  return false;
223  }
224 
225  linears_[components_[index].index] = linear;
226  return true;
227 }
228 
229 bool Path::UpdateQuadraticComponentAtIndex(
230  size_t index,
231  const QuadraticPathComponent& quadratic) {
232  if (index >= components_.size()) {
233  return false;
234  }
235 
236  if (components_[index].type != ComponentType::kQuadratic) {
237  return false;
238  }
239 
240  quads_[components_[index].index] = quadratic;
241  return true;
242 }
243 
244 bool Path::UpdateCubicComponentAtIndex(size_t index,
245  CubicPathComponent& cubic) {
246  if (index >= components_.size()) {
247  return false;
248  }
249 
250  if (components_[index].type != ComponentType::kCubic) {
251  return false;
252  }
253 
254  cubics_[components_[index].index] = cubic;
255  return true;
256 }
257 
258 bool Path::UpdateContourComponentAtIndex(size_t index,
259  const ContourComponent& move) {
260  if (index >= components_.size()) {
261  return false;
262  }
263 
264  if (components_[index].type != ComponentType::kContour) {
265  return false;
266  }
267 
268  contours_[components_[index].index] = move;
269  return true;
270 }
271 
273  Polyline polyline;
274 
275  std::optional<Point> previous_contour_point;
276  auto collect_points = [&polyline, &previous_contour_point](
277  const std::vector<Point>& collection) {
278  if (collection.empty()) {
279  return;
280  }
281 
282  for (const auto& point : collection) {
283  if (previous_contour_point.has_value() &&
284  previous_contour_point.value() == point) {
285  // Skip over duplicate points in the same contour.
286  continue;
287  }
288  previous_contour_point = point;
289  polyline.points.push_back(point);
290  }
291  };
292 
293  auto get_path_component = [this](size_t component_i) -> PathComponentVariant {
294  if (component_i >= components_.size()) {
295  return std::monostate{};
296  }
297  const auto& component = components_[component_i];
298  switch (component.type) {
300  return &linears_[component.index];
302  return &quads_[component.index];
304  return &cubics_[component.index];
306  return std::monostate{};
307  }
308  };
309 
310  auto compute_contour_start_direction =
311  [&get_path_component](size_t current_path_component_index) {
312  size_t next_component_index = current_path_component_index + 1;
313  while (!std::holds_alternative<std::monostate>(
314  get_path_component(next_component_index))) {
315  auto next_component = get_path_component(next_component_index);
316  auto maybe_vector =
317  std::visit(PathComponentStartDirectionVisitor(), next_component);
318  if (maybe_vector.has_value()) {
319  return maybe_vector.value();
320  } else {
321  next_component_index++;
322  }
323  }
324  return Vector2(0, -1);
325  };
326 
327  std::optional<size_t> previous_path_component_index;
328  auto end_contour = [&polyline, &previous_path_component_index,
329  &get_path_component]() {
330  // Whenever a contour has ended, extract the exact end direction from the
331  // last component.
332  if (polyline.contours.empty()) {
333  return;
334  }
335 
336  if (!previous_path_component_index.has_value()) {
337  return;
338  }
339 
340  auto& contour = polyline.contours.back();
341  contour.end_direction = Vector2(0, 1);
342 
343  size_t previous_index = previous_path_component_index.value();
344  while (!std::holds_alternative<std::monostate>(
345  get_path_component(previous_index))) {
346  auto previous_component = get_path_component(previous_index);
347  auto maybe_vector =
348  std::visit(PathComponentEndDirectionVisitor(), previous_component);
349  if (maybe_vector.has_value()) {
350  contour.end_direction = maybe_vector.value();
351  break;
352  } else {
353  if (previous_index == 0) {
354  break;
355  }
356  previous_index--;
357  }
358  }
359  };
360 
361  for (size_t component_i = 0; component_i < components_.size();
362  component_i++) {
363  const auto& component = components_[component_i];
364  switch (component.type) {
366  collect_points(linears_[component.index].CreatePolyline());
367  previous_path_component_index = component_i;
368  break;
370  collect_points(quads_[component.index].CreatePolyline(scale));
371  previous_path_component_index = component_i;
372  break;
374  collect_points(cubics_[component.index].CreatePolyline(scale));
375  previous_path_component_index = component_i;
376  break;
378  if (component_i == components_.size() - 1) {
379  // If the last component is a contour, that means it's an empty
380  // contour, so skip it.
381  continue;
382  }
383  end_contour();
384 
385  Vector2 start_direction = compute_contour_start_direction(component_i);
386  const auto& contour = contours_[component.index];
387  polyline.contours.push_back({.start_index = polyline.points.size(),
388  .is_closed = contour.is_closed,
389  .start_direction = start_direction});
390  previous_contour_point = std::nullopt;
391  collect_points({contour.destination});
392  break;
393  }
394  end_contour();
395  }
396  return polyline;
397 }
398 
399 std::optional<Rect> Path::GetBoundingBox() const {
400  return computed_bounds_;
401 }
402 
403 void Path::ComputeBounds() {
404  auto min_max = GetMinMaxCoveragePoints();
405  if (!min_max.has_value()) {
406  computed_bounds_ = std::nullopt;
407  return;
408  }
409  auto min = min_max->first;
410  auto max = min_max->second;
411  const auto difference = max - min;
412  computed_bounds_ = Rect{min.x, min.y, difference.x, difference.y};
413 }
414 
416  const Matrix& transform) const {
417  auto bounds = GetBoundingBox();
418  if (!bounds.has_value()) {
419  return std::nullopt;
420  }
421  return bounds->TransformBounds(transform);
422 }
423 
424 std::optional<std::pair<Point, Point>> Path::GetMinMaxCoveragePoints() const {
425  if (linears_.empty() && quads_.empty() && cubics_.empty()) {
426  return std::nullopt;
427  }
428 
429  std::optional<Point> min, max;
430 
431  auto clamp = [&min, &max](const Point& point) {
432  if (min.has_value()) {
433  min = min->Min(point);
434  } else {
435  min = point;
436  }
437 
438  if (max.has_value()) {
439  max = max->Max(point);
440  } else {
441  max = point;
442  }
443  };
444 
445  for (const auto& linear : linears_) {
446  clamp(linear.p1);
447  clamp(linear.p2);
448  }
449 
450  for (const auto& quad : quads_) {
451  for (const Point& point : quad.Extrema()) {
452  clamp(point);
453  }
454  }
455 
456  for (const auto& cubic : cubics_) {
457  for (const Point& point : cubic.Extrema()) {
458  clamp(point);
459  }
460  }
461 
462  if (!min.has_value() || !max.has_value()) {
463  return std::nullopt;
464  }
465 
466  return std::make_pair(min.value(), max.value());
467 }
468 
469 void Path::SetBounds(Rect rect) {
470  computed_bounds_ = rect;
471 }
472 
473 } // namespace impeller
path.h
impeller::LinearPathComponent
Definition: path_component.h:27
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:172
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:29
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::PathComponentEndDirectionVisitor
Definition: path_component.h:165
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:399
impeller::Convexity
Convexity
Definition: path.h:37
impeller::Path::GetMinMaxCoveragePoints
std::optional< std::pair< Point, Point > > GetMinMaxCoveragePoints() const
Definition: path.cc:424
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::~Path
~Path()
impeller::Path::Polyline
Definition: path.h:78
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:187
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:28
impeller::Path::EnumerateComponents
void EnumerateComponents(const Applier< LinearPathComponent > &linear_applier, const Applier< QuadraticPathComponent > &quad_applier, const Applier< CubicPathComponent > &cubic_applier, const Applier< ContourComponent > &contour_applier) const
Definition: path.cc:125
impeller::Path::GetTransformedBoundingBox
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:415
impeller::Path::Polyline::points
std::vector< Point > points
Definition: path.h:81
impeller::Path::Polyline::GetContourPointBounds
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:20
impeller::FillType
FillType
Definition: path.h:29
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::PathComponentStartDirectionVisitor
Definition: path_component.h:156
impeller::Path::Path
Path()
Definition: path.cc:14
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:201
impeller::CubicPathComponent
Definition: path_component.h:90
impeller::Path::Applier
std::function< void(size_t index, const T &component)> Applier
Definition: path.h:103
impeller::TPoint< Scalar >
impeller::Path::GetComponentCount
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:32
impeller::Path::Polyline::contours
std::vector< PolylineContour > contours
Definition: path.h:82
impeller::Path::IsConvex
bool IsConvex() const
Definition: path.cc:56
impeller::PathComponentVariant
std::variant< std::monostate, const LinearPathComponent *, const QuadraticPathComponent *, const CubicPathComponent * > PathComponentVariant
Definition: path_component.h:154
path_component.h
impeller::ContourComponent
Definition: path_component.h:137
impeller::Convexity::kConvex
@ kConvex
impeller
Definition: aiks_context.cc:10
impeller::QuadraticPathComponent
Definition: path_component.h:50
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:158
impeller::Path::ComponentType::kContour
@ kContour
impeller::TRect
Definition: rect.h:20
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:36
impeller::Path::GetFillType
FillType GetFillType() const
Definition: path.cc:52
impeller::Path::CreatePolyline
Polyline CreatePolyline(Scalar scale) const
Definition: path.cc:272