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 <utility>
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 
23  return path_.data_->components[component_index_];
24 }
25 
26 #define CHECK_COMPONENT(type) \
27  (component_index_ < path_.data_->components.size() && \
28  path_.data_->components[component_index_] == type && \
29  storage_offset_ + VerbToOffset(type) <= path_.data_->points.size())
30 
33  return nullptr;
34  }
35  const Point* points = &(path_.data_->points[storage_offset_]);
36  return reinterpret_cast<const LinearPathComponent*>(points);
37 }
38 
41  return nullptr;
42  }
43  const Point* points = &(path_.data_->points[storage_offset_]);
44  return reinterpret_cast<const QuadraticPathComponent*>(points);
45 }
46 
49  return nullptr;
50  }
51  const Point* points = &(path_.data_->points[storage_offset_]);
52  return reinterpret_cast<const ConicPathComponent*>(points);
53 }
54 
57  return nullptr;
58  }
59  const Point* points = &(path_.data_->points[storage_offset_]);
60  return reinterpret_cast<const CubicPathComponent*>(points);
61 }
62 
65  return nullptr;
66  }
67  const Point* points = &(path_.data_->points[storage_offset_]);
68  return reinterpret_cast<const ContourComponent*>(points);
69 }
70 
72  auto components = path_.data_->components;
73  if (component_index_ < components.size()) {
74  storage_offset_ += VerbToOffset(path_.data_->components[component_index_]);
75  component_index_++;
76  }
77  return *this;
78 }
79 
80 std::tuple<size_t, size_t> Path::Polyline::GetContourPointBounds(
81  size_t contour_index) const {
82  if (contour_index >= contours.size()) {
83  return {points->size(), points->size()};
84  }
85  const size_t start_index = contours.at(contour_index).start_index;
86  const size_t end_index = (contour_index >= contours.size() - 1)
87  ? points->size()
88  : contours.at(contour_index + 1).start_index;
89  return std::make_tuple(start_index, end_index);
90 }
91 
92 size_t Path::GetComponentCount(std::optional<ComponentType> type) const {
93  if (!type.has_value()) {
94  return data_->components.size();
95  }
96  auto type_value = type.value();
97  size_t count = 0u;
98  for (const auto& component : data_->components) {
99  if (component == type_value) {
100  count++;
101  }
102  }
103  return count;
104 }
105 
106 size_t Path::GetPointCount() const {
107  return data_->points.size();
108 }
109 
111  return data_->fill;
112 }
113 
114 bool Path::IsConvex() const {
115  return data_->convexity == Convexity::kConvex;
116 }
117 
118 bool Path::IsEmpty() const {
119  return data_->points.empty() ||
120  (data_->components.size() == 1 &&
121  data_->components[0] == ComponentType::kContour);
122 }
123 
124 bool Path::IsSingleContour() const {
125  return data_->single_contour;
126 }
127 
128 /// Determine required storage for points and indices.
129 std::pair<size_t, size_t> Path::CountStorage(Scalar scale) const {
130  size_t points = 0;
131  size_t contours = 0;
132 
133  auto& path_components = data_->components;
134  auto& path_points = data_->points;
135 
136  size_t storage_offset = 0u;
137  for (size_t component_i = 0; component_i < path_components.size();
138  component_i++) {
139  const auto& path_component = path_components[component_i];
140  switch (path_component) {
141  case ComponentType::kLinear: {
142  points += 2;
143  break;
144  }
146  const QuadraticPathComponent* quad =
147  reinterpret_cast<const QuadraticPathComponent*>(
148  &path_points[storage_offset]);
149  points += quad->CountLinearPathComponents(scale);
150  break;
151  }
152  case ComponentType::kConic: {
153  const ConicPathComponent* conic =
154  reinterpret_cast<const ConicPathComponent*>(
155  &path_points[storage_offset]);
156  points += conic->CountLinearPathComponents(scale);
157  break;
158  }
159  case ComponentType::kCubic: {
160  const CubicPathComponent* cubic =
161  reinterpret_cast<const CubicPathComponent*>(
162  &path_points[storage_offset]);
163  points += cubic->CountLinearPathComponents(scale);
164  break;
165  }
167  contours++;
168  }
169  storage_offset += VerbToOffset(path_component);
170  }
171  return std::make_pair(points, contours);
172 }
173 
175  auto& path_components = data_->components;
176  auto& path_points = data_->points;
177  bool started_contour = false;
178  bool first_point = true;
179 
180  size_t storage_offset = 0u;
181  for (size_t component_i = 0; component_i < path_components.size();
182  component_i++) {
183  const auto& path_component = path_components[component_i];
184  switch (path_component) {
185  case ComponentType::kLinear: {
186  const LinearPathComponent* linear =
187  reinterpret_cast<const LinearPathComponent*>(
188  &path_points[storage_offset]);
189  if (first_point) {
190  writer.Write(linear->p1);
191  first_point = false;
192  }
193  writer.Write(linear->p2);
194  break;
195  }
197  const QuadraticPathComponent* quad =
198  reinterpret_cast<const QuadraticPathComponent*>(
199  &path_points[storage_offset]);
200  if (first_point) {
201  writer.Write(quad->p1);
202  first_point = false;
203  }
204  quad->ToLinearPathComponents(scale, writer);
205  break;
206  }
207  case ComponentType::kConic: {
208  const ConicPathComponent* conic =
209  reinterpret_cast<const ConicPathComponent*>(
210  &path_points[storage_offset]);
211  if (first_point) {
212  writer.Write(conic->p1);
213  first_point = false;
214  }
215  conic->ToLinearPathComponents(scale, writer);
216  break;
217  }
218  case ComponentType::kCubic: {
219  const CubicPathComponent* cubic =
220  reinterpret_cast<const CubicPathComponent*>(
221  &path_points[storage_offset]);
222  if (first_point) {
223  writer.Write(cubic->p1);
224  first_point = false;
225  }
226  cubic->ToLinearPathComponents(scale, writer);
227  break;
228  }
230  if (component_i == path_components.size() - 1) {
231  // If the last component is a contour, that means it's an empty
232  // contour, so skip it.
233  continue;
234  }
235  // The contour component type is the first segment in a contour.
236  // Since this should contain the destination (if closed), we
237  // can start with this point. If there was already an open
238  // contour, or we've reached the end of the verb list, we
239  // also close the contour.
240  if (started_contour) {
241  writer.EndContour();
242  }
243  started_contour = true;
244  first_point = true;
245  }
246  storage_offset += VerbToOffset(path_component);
247  }
248  if (started_contour) {
249  writer.EndContour();
250  }
251 }
252 
254  return ComponentIterator(*this, 0u, 0u);
255 }
256 
258  return ComponentIterator(*this, data_->components.size(),
259  data_->points.size());
260 }
261 
264  : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
265  FML_DCHECK(points);
266 }
267 
269  points = std::move(other.points);
270  reclaim_points_ = std::move(other.reclaim_points_);
271  contours = std::move(other.contours);
272 }
273 
275  if (reclaim_points_) {
276  points->clear();
277  reclaim_points_(std::move(points));
278  }
279 }
280 
282  size_t storage_offset,
284  size_t component_index,
285  std::vector<PolylineContour::Component>& poly_components) const {
286  auto& path_components = data_->components;
287  auto& path_points = data_->points;
288  // Whenever a contour has ended, extract the exact end direction from
289  // the last component.
290  if (polyline.contours.empty() || component_index == 0) {
291  return;
292  }
293 
294  auto& contour = polyline.contours.back();
295  contour.end_direction = Vector2(0, 1);
296  contour.components = poly_components;
297  poly_components.clear();
298 
299  size_t previous_index = component_index - 1;
300  storage_offset -= VerbToOffset(path_components[previous_index]);
301 
302  while (previous_index >= 0 && storage_offset >= 0) {
303  const auto& path_component = path_components[previous_index];
304  switch (path_component) {
305  case ComponentType::kLinear: {
306  auto* linear = reinterpret_cast<const LinearPathComponent*>(
307  &path_points[storage_offset]);
308  auto maybe_end = linear->GetEndDirection();
309  if (maybe_end.has_value()) {
310  contour.end_direction = maybe_end.value();
311  return;
312  }
313  break;
314  }
316  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
317  &path_points[storage_offset]);
318  auto maybe_end = quad->GetEndDirection();
319  if (maybe_end.has_value()) {
320  contour.end_direction = maybe_end.value();
321  return;
322  }
323  break;
324  }
325  case ComponentType::kConic: {
326  auto* conic = reinterpret_cast<const ConicPathComponent*>(
327  &path_points[storage_offset]);
328  auto maybe_end = conic->GetEndDirection();
329  if (maybe_end.has_value()) {
330  contour.end_direction = maybe_end.value();
331  return;
332  }
333  break;
334  }
335  case ComponentType::kCubic: {
336  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
337  &path_points[storage_offset]);
338  auto maybe_end = cubic->GetEndDirection();
339  if (maybe_end.has_value()) {
340  contour.end_direction = maybe_end.value();
341  return;
342  }
343  break;
344  }
346  // Hit previous contour, return.
347  return;
348  };
349  }
350  storage_offset -= VerbToOffset(path_component);
351  previous_index--;
352  }
353 };
354 
356  Scalar scale,
357  Path::Polyline::PointBufferPtr point_buffer,
359  Polyline polyline(std::move(point_buffer), std::move(reclaim));
360 
361  auto& path_components = data_->components;
362  auto& path_points = data_->points;
363  std::optional<Vector2> start_direction;
364  std::vector<PolylineContour::Component> poly_components;
365  size_t storage_offset = 0u;
366  size_t component_i = 0;
367 
368  for (; component_i < path_components.size(); component_i++) {
369  auto path_component = path_components[component_i];
370  switch (path_component) {
371  case ComponentType::kLinear: {
372  poly_components.push_back({
373  .component_start_index = polyline.points->size() - 1,
374  .is_curve = false,
375  });
376  auto* linear = reinterpret_cast<const LinearPathComponent*>(
377  &path_points[storage_offset]);
378  linear->AppendPolylinePoints(*polyline.points);
379  if (!start_direction.has_value()) {
380  start_direction = linear->GetStartDirection();
381  }
382  break;
383  }
385  poly_components.push_back({
386  .component_start_index = polyline.points->size() - 1,
387  .is_curve = true,
388  });
389  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
390  &path_points[storage_offset]);
391  quad->AppendPolylinePoints(scale, *polyline.points);
392  if (!start_direction.has_value()) {
393  start_direction = quad->GetStartDirection();
394  }
395  break;
396  }
397  case ComponentType::kConic: {
398  poly_components.push_back({
399  .component_start_index = polyline.points->size() - 1,
400  .is_curve = true,
401  });
402  auto* conic = reinterpret_cast<const ConicPathComponent*>(
403  &path_points[storage_offset]);
404  conic->AppendPolylinePoints(scale, *polyline.points);
405  if (!start_direction.has_value()) {
406  start_direction = conic->GetStartDirection();
407  }
408  break;
409  }
410  case ComponentType::kCubic: {
411  poly_components.push_back({
412  .component_start_index = polyline.points->size() - 1,
413  .is_curve = true,
414  });
415  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
416  &path_points[storage_offset]);
417  cubic->AppendPolylinePoints(scale, *polyline.points);
418  if (!start_direction.has_value()) {
419  start_direction = cubic->GetStartDirection();
420  }
421  break;
422  }
424  if (component_i == path_components.size() - 1) {
425  // If the last component is a contour, that means it's an empty
426  // contour, so skip it.
427  break;
428  }
429  if (!polyline.contours.empty()) {
430  polyline.contours.back().start_direction =
431  start_direction.value_or(Vector2(0, -1));
432  start_direction = std::nullopt;
433  }
434  EndContour(storage_offset, polyline, component_i, poly_components);
435 
436  auto* contour = reinterpret_cast<const ContourComponent*>(
437  &path_points[storage_offset]);
438  polyline.contours.push_back(PolylineContour{
439  .start_index = polyline.points->size(), //
440  .is_closed = contour->IsClosed(), //
441  .start_direction = Vector2(0, -1), //
442  .components = poly_components //
443  });
444 
445  polyline.points->push_back(contour->destination);
446  break;
447  }
448  storage_offset += VerbToOffset(path_component);
449  }
450 
451  // Subtract the last storage offset increment so that the storage lookup is
452  // correct, including potentially an empty contour as well.
453  if (component_i > 0 && path_components.back() == ComponentType::kContour) {
454  storage_offset -= VerbToOffset(ComponentType::kContour);
455  component_i--;
456  }
457 
458  if (!polyline.contours.empty()) {
459  polyline.contours.back().start_direction =
460  start_direction.value_or(Vector2(0, -1));
461  }
462  EndContour(storage_offset, polyline, component_i, poly_components);
463  return polyline;
464 }
465 
466 std::optional<Rect> Path::GetBoundingBox() const {
467  return data_->bounds;
468 }
469 
471  const Matrix& transform) const {
472  auto bounds = GetBoundingBox();
473  if (!bounds.has_value()) {
474  return std::nullopt;
475  }
476  return bounds->TransformBounds(transform);
477 }
478 
479 } // namespace impeller
GLenum type
const ContourComponent * contour() const
Definition: path.cc:63
ComponentIterator & operator++()
Definition: path.cc:71
const QuadraticPathComponent * quadratic() const
Definition: path.cc:39
ComponentType type() const
Definition: path.cc:22
const LinearPathComponent * linear() const
Definition: path.cc:31
const ConicPathComponent * conic() const
Definition: path.cc:47
const CubicPathComponent * cubic() const
Definition: path.cc:55
FillType GetFillType() const
Definition: path.cc:110
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:92
ComponentIterator begin() const
Definition: path.cc:253
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
Definition: path.cc:355
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:124
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:470
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:95
bool IsConvex() const
Definition: path.cc:114
ComponentIterator end() const
Definition: path.cc:257
size_t GetPointCount() const
Definition: path.cc:106
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:174
bool IsEmpty() const
Definition: path.cc:118
void EndContour(size_t storage_offset, Polyline &polyline, size_t component_index, std::vector< PolylineContour::Component > &poly_components) const
Definition: path.cc:281
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:466
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
Definition: path.cc:129
An interface for generating a multi contour polyline as a triangle strip.
virtual void EndContour()=0
virtual void Write(Point point)=0
#define CHECK_COMPONENT(type)
Definition: path.cc:26
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:18
FillType
Definition: path.h:32
Definition: comparable.h:95
const Scalar scale
const Path::Polyline & polyline
std::optional< Vector2 > GetEndDirection() const
size_t CountLinearPathComponents(Scalar scale) const
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
size_t CountLinearPathComponents(Scalar scale) const
void AppendPolylinePoints(Scalar scale, std::vector< Point > &points) const
std::optional< Vector2 > GetEndDirection() const
std::optional< Vector2 > GetEndDirection() const
void AppendPolylinePoints(std::vector< Point > &points) const
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
size_t start_index
Index that denotes the first point of this contour.
Definition: path.h:122
Polyline(PointBufferPtr point_buffer, ReclaimPointBufferCallback reclaim)
Definition: path.cc:262
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:150
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:149
PointBufferPtr points
Definition: path.h:161
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:80
size_t CountLinearPathComponents(Scalar scale) const
std::optional< Vector2 > GetEndDirection() const
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:67