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 
16 Path::Path() : data_(new Data()) {}
17 
18 Path::Path(Data data) : data_(std::make_shared<Data>(std::move(data))) {}
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 data_->components.size();
37  }
38  auto type_value = type.value();
39  if (type_value == ComponentType::kContour) {
40  return data_->contours.size();
41  }
42  size_t count = 0u;
43  for (const auto& component : data_->components) {
44  if (component.type == type_value) {
45  count++;
46  }
47  }
48  return count;
49 }
50 
52  return data_->fill;
53 }
54 
55 bool Path::IsConvex() const {
56  return data_->convexity == Convexity::kConvex;
57 }
58 
59 bool Path::IsEmpty() const {
60  return data_->points.empty();
61 }
62 
64  const Applier<LinearPathComponent>& linear_applier,
65  const Applier<QuadraticPathComponent>& quad_applier,
66  const Applier<CubicPathComponent>& cubic_applier,
67  const Applier<ContourComponent>& contour_applier) const {
68  auto& points = data_->points;
69  size_t currentIndex = 0;
70  for (const auto& component : data_->components) {
71  switch (component.type) {
73  if (linear_applier) {
74  linear_applier(currentIndex,
75  LinearPathComponent(points[component.index],
76  points[component.index + 1]));
77  }
78  break;
80  if (quad_applier) {
81  quad_applier(currentIndex,
82  QuadraticPathComponent(points[component.index],
83  points[component.index + 1],
84  points[component.index + 2]));
85  }
86  break;
88  if (cubic_applier) {
89  cubic_applier(currentIndex,
90  CubicPathComponent(points[component.index],
91  points[component.index + 1],
92  points[component.index + 2],
93  points[component.index + 3]));
94  }
95  break;
97  if (contour_applier) {
98  contour_applier(currentIndex, data_->contours[component.index]);
99  }
100  break;
101  }
102  currentIndex++;
103  }
104 }
105 
107  auto& path_components = data_->components;
108  auto& path_points = data_->points;
109  bool started_contour = false;
110  bool first_point = true;
111 
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.type) {
116  case ComponentType::kLinear: {
117  const LinearPathComponent* linear =
118  reinterpret_cast<const LinearPathComponent*>(
119  &path_points[path_component.index]);
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[path_component.index]);
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[path_component.index]);
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  }
167  if (started_contour) {
168  writer.EndContour();
169  }
170 }
171 
173  LinearPathComponent& linear) const {
174  auto& components = data_->components;
175 
176  if (index >= components.size()) {
177  return false;
178  }
179 
180  if (components[index].type != ComponentType::kLinear) {
181  return false;
182  }
183 
184  auto& points = data_->points;
185  auto point_index = components[index].index;
186  linear = LinearPathComponent(points[point_index], points[point_index + 1]);
187  return true;
188 }
189 
191  size_t index,
192  QuadraticPathComponent& quadratic) const {
193  auto& components = data_->components;
194 
195  if (index >= components.size()) {
196  return false;
197  }
198 
199  if (components[index].type != ComponentType::kQuadratic) {
200  return false;
201  }
202 
203  auto& points = data_->points;
204  auto point_index = components[index].index;
205  quadratic = QuadraticPathComponent(
206  points[point_index], points[point_index + 1], points[point_index + 2]);
207  return true;
208 }
209 
211  CubicPathComponent& cubic) const {
212  auto& components = data_->components;
213 
214  if (index >= components.size()) {
215  return false;
216  }
217 
218  if (components[index].type != ComponentType::kCubic) {
219  return false;
220  }
221 
222  auto& points = data_->points;
223  auto point_index = components[index].index;
224  cubic = CubicPathComponent(points[point_index], points[point_index + 1],
225  points[point_index + 2], points[point_index + 3]);
226  return true;
227 }
228 
230  ContourComponent& move) const {
231  auto& components = data_->components;
232 
233  if (index >= components.size()) {
234  return false;
235  }
236 
237  if (components[index].type != ComponentType::kContour) {
238  return false;
239  }
240 
241  move = data_->contours[components[index].index];
242  return true;
243 }
244 
247  : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
248  FML_DCHECK(points);
249 }
250 
252  points = std::move(other.points);
253  reclaim_points_ = std::move(other.reclaim_points_);
254  contours = std::move(other.contours);
255 }
256 
258  if (reclaim_points_) {
259  points->clear();
260  reclaim_points_(std::move(points));
261  }
262 }
263 
265  Scalar scale,
266  Path::Polyline::PointBufferPtr point_buffer,
268  Polyline polyline(std::move(point_buffer), std::move(reclaim));
269 
270  auto& path_components = data_->components;
271  auto& path_points = data_->points;
272 
273  auto get_path_component = [&path_components, &path_points](
274  size_t component_i) -> PathComponentVariant {
275  if (component_i >= path_components.size()) {
276  return std::monostate{};
277  }
278  const auto& component = path_components[component_i];
279  switch (component.type) {
281  return reinterpret_cast<const LinearPathComponent*>(
282  &path_points[component.index]);
284  return reinterpret_cast<const QuadraticPathComponent*>(
285  &path_points[component.index]);
287  return reinterpret_cast<const CubicPathComponent*>(
288  &path_points[component.index]);
290  return std::monostate{};
291  }
292  };
293 
294  auto compute_contour_start_direction =
295  [&get_path_component](size_t current_path_component_index) {
296  size_t next_component_index = current_path_component_index + 1;
297  while (!std::holds_alternative<std::monostate>(
298  get_path_component(next_component_index))) {
299  auto next_component = get_path_component(next_component_index);
300  auto maybe_vector =
301  std::visit(PathComponentStartDirectionVisitor(), next_component);
302  if (maybe_vector.has_value()) {
303  return maybe_vector.value();
304  } else {
305  next_component_index++;
306  }
307  }
308  return Vector2(0, -1);
309  };
310 
311  std::vector<PolylineContour::Component> poly_components;
312  std::optional<size_t> previous_path_component_index;
313  auto end_contour = [&polyline, &previous_path_component_index,
314  &get_path_component, &poly_components]() {
315  // Whenever a contour has ended, extract the exact end direction from
316  // the last component.
317  if (polyline.contours.empty()) {
318  return;
319  }
320 
321  if (!previous_path_component_index.has_value()) {
322  return;
323  }
324 
325  auto& contour = polyline.contours.back();
326  contour.end_direction = Vector2(0, 1);
327  contour.components = poly_components;
328  poly_components.clear();
329 
330  size_t previous_index = previous_path_component_index.value();
331  while (!std::holds_alternative<std::monostate>(
332  get_path_component(previous_index))) {
333  auto previous_component = get_path_component(previous_index);
334  auto maybe_vector =
335  std::visit(PathComponentEndDirectionVisitor(), previous_component);
336  if (maybe_vector.has_value()) {
337  contour.end_direction = maybe_vector.value();
338  break;
339  } else {
340  if (previous_index == 0) {
341  break;
342  }
343  previous_index--;
344  }
345  }
346  };
347 
348  for (size_t component_i = 0; component_i < path_components.size();
349  component_i++) {
350  const auto& path_component = path_components[component_i];
351  switch (path_component.type) {
353  poly_components.push_back({
354  .component_start_index = polyline.points->size() - 1,
355  .is_curve = false,
356  });
357  reinterpret_cast<const LinearPathComponent*>(
358  &path_points[path_component.index])
359  ->AppendPolylinePoints(*polyline.points);
360  previous_path_component_index = component_i;
361  break;
363  poly_components.push_back({
364  .component_start_index = polyline.points->size() - 1,
365  .is_curve = true,
366  });
367  reinterpret_cast<const QuadraticPathComponent*>(
368  &path_points[path_component.index])
369  ->AppendPolylinePoints(scale, *polyline.points);
370  previous_path_component_index = component_i;
371  break;
373  poly_components.push_back({
374  .component_start_index = polyline.points->size() - 1,
375  .is_curve = true,
376  });
377  reinterpret_cast<const CubicPathComponent*>(
378  &path_points[path_component.index])
379  ->AppendPolylinePoints(scale, *polyline.points);
380  previous_path_component_index = component_i;
381  break;
383  if (component_i == path_components.size() - 1) {
384  // If the last component is a contour, that means it's an empty
385  // contour, so skip it.
386  continue;
387  }
388  end_contour();
389 
390  Vector2 start_direction = compute_contour_start_direction(component_i);
391  const auto& contour = data_->contours[path_component.index];
392  polyline.contours.push_back({.start_index = polyline.points->size(),
393  .is_closed = contour.is_closed,
394  .start_direction = start_direction,
395  .components = poly_components});
396 
397  polyline.points->push_back(contour.destination);
398  break;
399  }
400  }
401  end_contour();
402  return polyline;
403 }
404 
405 std::optional<Rect> Path::GetBoundingBox() const {
406  return data_->bounds;
407 }
408 
410  const Matrix& transform) const {
411  auto bounds = GetBoundingBox();
412  if (!bounds.has_value()) {
413  return std::nullopt;
414  }
415  return bounds->TransformBounds(transform);
416 }
417 
418 } // namespace impeller
path.h
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:303
point.h
impeller::LinearPathComponent
Definition: path_component.h:39
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:190
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:103
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:41
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:65
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::VertexWriter::EndContour
void EndContour()
Definition: path_component.cc:17
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:326
impeller::PathComponentEndDirectionVisitor
Definition: path_component.h:179
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:405
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::IsEmpty
bool IsEmpty() const
Definition: path.cc:59
impeller::Path::Polyline::Polyline
Polyline(PointBufferPtr point_buffer, ReclaimPointBufferCallback reclaim)
Definition: path.cc:245
impeller::Path::~Path
~Path()
impeller::QuadraticPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
Definition: path_component.cc:179
impeller::Path::Polyline
Definition: path.h:95
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:106
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:210
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:40
impeller::Path::Polyline::ReclaimPointBufferCallback
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:99
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:63
impeller::Path::GetTransformedBoundingBox
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:409
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:231
impeller::Path::Polyline::GetContourPointBounds
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:22
type
GLenum type
Definition: blit_command_gles.cc:126
impeller::FillType
FillType
Definition: path.h:30
impeller::PathComponentStartDirectionVisitor
Definition: path_component.h:170
impeller::Path::Path
Path()
Definition: path.cc:16
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:229
impeller::CubicPathComponent
Definition: path_component.h:101
impeller::Path::Polyline::points
PointBufferPtr points
Definition: path.h:110
impeller::CubicPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
Definition: path_component.cc:260
impeller::VertexWriter
An interface for generating a multi contour polyline as a triangle strip.
Definition: path_component.h:21
impeller::Path::Applier
std::function< void(size_t index, const T &component)> Applier
Definition: path.h:142
std
Definition: comparable.h:95
impeller::TPoint< Scalar >
impeller::VertexWriter::Write
void Write(Point point)
Definition: path_component.cc:67
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:116
scale
const Scalar scale
Definition: stroke_path_geometry.cc:308
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:98
impeller::Path::Polyline::~Polyline
~Polyline()
Definition: path.cc:257
impeller::Path::IsConvex
bool IsConvex() const
Definition: path.cc:55
impeller::PathComponentVariant
std::variant< std::monostate, const LinearPathComponent *, const QuadraticPathComponent *, const CubicPathComponent * > PathComponentVariant
Definition: path_component.h:168
path_component.h
impeller::ContourComponent
Definition: path_component.h:151
impeller::Convexity::kConvex
@ kConvex
impeller
Definition: aiks_blend_unittests.cc:18
impeller::QuadraticPathComponent
Definition: path_component.h:63
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:264
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:172
impeller::Path::ComponentType::kContour
@ kContour
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Path::GetFillType
FillType GetFillType() const
Definition: path.cc:51