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