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 
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  size_t count = 0u;
40  for (const auto& component : data_->components) {
41  if (component == type_value) {
42  count++;
43  }
44  }
45  return count;
46 }
47 
49  return data_->fill;
50 }
51 
52 bool Path::IsConvex() const {
53  return data_->convexity == Convexity::kConvex;
54 }
55 
56 bool Path::IsEmpty() const {
57  return data_->points.empty() ||
58  (data_->components.size() == 1 &&
59  data_->components[0] == ComponentType::kContour);
60 }
61 
62 bool Path::IsSingleContour() const {
63  return data_->single_countour;
64 }
65 
66 /// Determine required storage for points and indices.
67 std::pair<size_t, size_t> Path::CountStorage(Scalar scale) const {
68  size_t points = 0;
69  size_t contours = 0;
70 
71  auto& path_components = data_->components;
72  auto& path_points = data_->points;
73 
74  size_t storage_offset = 0u;
75  for (size_t component_i = 0; component_i < path_components.size();
76  component_i++) {
77  const auto& path_component = path_components[component_i];
78  switch (path_component) {
80  points += 2;
81  break;
82  }
84  const QuadraticPathComponent* quad =
85  reinterpret_cast<const QuadraticPathComponent*>(
86  &path_points[storage_offset]);
87  points += quad->CountLinearPathComponents(scale);
88  break;
89  }
90  case ComponentType::kCubic: {
91  const CubicPathComponent* cubic =
92  reinterpret_cast<const CubicPathComponent*>(
93  &path_points[storage_offset]);
94  points += cubic->CountLinearPathComponents(scale);
95  break;
96  }
98  contours++;
99  }
100  storage_offset += VerbToOffset(path_component);
101  }
102  return std::make_pair(points, contours);
103 }
104 
106  auto& path_components = data_->components;
107  auto& path_points = data_->points;
108  bool started_contour = false;
109  bool first_point = true;
110 
111  size_t storage_offset = 0u;
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) {
116  case ComponentType::kLinear: {
117  const LinearPathComponent* linear =
118  reinterpret_cast<const LinearPathComponent*>(
119  &path_points[storage_offset]);
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[storage_offset]);
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[storage_offset]);
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  storage_offset += VerbToOffset(path_component);
167  }
168  if (started_contour) {
169  writer.EndContour();
170  }
171 }
172 
174  auto& components = data_->components;
175  return components[index];
176 }
177 
179  LinearPathComponent& linear) const {
180  auto& components = data_->components;
181  if (index >= components.size() ||
182  components[index] != ComponentType::kLinear) {
183  return false;
184  }
185 
186  size_t storage_offset = 0u;
187  for (auto i = 0u; i < index; i++) {
188  storage_offset += VerbToOffset(components[i]);
189  }
190  auto& points = data_->points;
191  linear =
192  LinearPathComponent(points[storage_offset], points[storage_offset + 1]);
193  return true;
194 }
195 
197  size_t index,
198  QuadraticPathComponent& quadratic) const {
199  auto& components = data_->components;
200  if (index >= components.size() ||
201  components[index] != ComponentType::kQuadratic) {
202  return false;
203  }
204 
205  size_t storage_offset = 0u;
206  for (auto i = 0u; i < index; i++) {
207  storage_offset += VerbToOffset(components[i]);
208  }
209  auto& points = data_->points;
210 
211  quadratic =
212  QuadraticPathComponent(points[storage_offset], points[storage_offset + 1],
213  points[storage_offset + 2]);
214  return true;
215 }
216 
218  CubicPathComponent& cubic) const {
219  auto& components = data_->components;
220  if (index >= components.size() ||
221  components[index] != ComponentType::kCubic) {
222  return false;
223  }
224 
225  size_t storage_offset = 0u;
226  for (auto i = 0u; i < index; i++) {
227  storage_offset += VerbToOffset(components[i]);
228  }
229  auto& points = data_->points;
230 
231  cubic = CubicPathComponent(points[storage_offset], points[storage_offset + 1],
232  points[storage_offset + 2],
233  points[storage_offset + 3]);
234  return true;
235 }
236 
238  ContourComponent& move) const {
239  auto& components = data_->components;
240  if (index >= components.size() ||
241  components[index] != ComponentType::kContour) {
242  return false;
243  }
244 
245  size_t storage_offset = 0u;
246  for (auto i = 0u; i < index; i++) {
247  storage_offset += VerbToOffset(components[i]);
248  }
249  auto& points = data_->points;
250 
251  move = ContourComponent(points[storage_offset], points[storage_offset + 1]);
252  return true;
253 }
254 
257  : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
258  FML_DCHECK(points);
259 }
260 
262  points = std::move(other.points);
263  reclaim_points_ = std::move(other.reclaim_points_);
264  contours = std::move(other.contours);
265 }
266 
268  if (reclaim_points_) {
269  points->clear();
270  reclaim_points_(std::move(points));
271  }
272 }
273 
275  size_t storage_offset,
277  size_t component_index,
278  std::vector<PolylineContour::Component>& poly_components) const {
279  auto& path_components = data_->components;
280  auto& path_points = data_->points;
281  // Whenever a contour has ended, extract the exact end direction from
282  // the last component.
283  if (polyline.contours.empty() || component_index == 0) {
284  return;
285  }
286 
287  auto& contour = polyline.contours.back();
288  contour.end_direction = Vector2(0, 1);
289  contour.components = poly_components;
290  poly_components.clear();
291 
292  size_t previous_index = component_index - 1;
293  storage_offset -= VerbToOffset(path_components[previous_index]);
294 
295  while (previous_index >= 0 && storage_offset >= 0) {
296  const auto& path_component = path_components[previous_index];
297  switch (path_component) {
298  case ComponentType::kLinear: {
299  auto* linear = reinterpret_cast<const LinearPathComponent*>(
300  &path_points[storage_offset]);
301  auto maybe_end = linear->GetEndDirection();
302  if (maybe_end.has_value()) {
303  contour.end_direction = maybe_end.value();
304  return;
305  }
306  break;
307  }
309  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
310  &path_points[storage_offset]);
311  auto maybe_end = quad->GetEndDirection();
312  if (maybe_end.has_value()) {
313  contour.end_direction = maybe_end.value();
314  return;
315  }
316  break;
317  }
318  case ComponentType::kCubic: {
319  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
320  &path_points[storage_offset]);
321  auto maybe_end = cubic->GetEndDirection();
322  if (maybe_end.has_value()) {
323  contour.end_direction = maybe_end.value();
324  return;
325  }
326  break;
327  }
329  // Hit previous contour, return.
330  return;
331  };
332  }
333  storage_offset -= VerbToOffset(path_component);
334  previous_index--;
335  }
336 };
337 
339  Scalar scale,
340  Path::Polyline::PointBufferPtr point_buffer,
342  Polyline polyline(std::move(point_buffer), std::move(reclaim));
343 
344  auto& path_components = data_->components;
345  auto& path_points = data_->points;
346  std::optional<Vector2> start_direction;
347  std::vector<PolylineContour::Component> poly_components;
348  size_t storage_offset = 0u;
349  size_t component_i = 0;
350 
351  for (; component_i < path_components.size(); component_i++) {
352  auto path_component = path_components[component_i];
353  switch (path_component) {
354  case ComponentType::kLinear: {
355  poly_components.push_back({
356  .component_start_index = polyline.points->size() - 1,
357  .is_curve = false,
358  });
359  auto* linear = reinterpret_cast<const LinearPathComponent*>(
360  &path_points[storage_offset]);
361  linear->AppendPolylinePoints(*polyline.points);
362  if (!start_direction.has_value()) {
363  start_direction = linear->GetStartDirection();
364  }
365  break;
366  }
368  poly_components.push_back({
369  .component_start_index = polyline.points->size() - 1,
370  .is_curve = true,
371  });
372  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
373  &path_points[storage_offset]);
374  quad->AppendPolylinePoints(scale, *polyline.points);
375  if (!start_direction.has_value()) {
376  start_direction = quad->GetStartDirection();
377  }
378  break;
379  }
380  case ComponentType::kCubic: {
381  poly_components.push_back({
382  .component_start_index = polyline.points->size() - 1,
383  .is_curve = true,
384  });
385  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
386  &path_points[storage_offset]);
387  cubic->AppendPolylinePoints(scale, *polyline.points);
388  if (!start_direction.has_value()) {
389  start_direction = cubic->GetStartDirection();
390  }
391  break;
392  }
394  if (component_i == path_components.size() - 1) {
395  // If the last component is a contour, that means it's an empty
396  // contour, so skip it.
397  break;
398  }
399  if (!polyline.contours.empty()) {
400  polyline.contours.back().start_direction =
401  start_direction.value_or(Vector2(0, -1));
402  start_direction = std::nullopt;
403  }
404  EndContour(storage_offset, polyline, component_i, poly_components);
405 
406  auto* contour = reinterpret_cast<const ContourComponent*>(
407  &path_points[storage_offset]);
408  polyline.contours.push_back(PolylineContour{
409  .start_index = polyline.points->size(), //
410  .is_closed = contour->IsClosed(), //
411  .start_direction = Vector2(0, -1), //
412  .components = poly_components //
413  });
414 
415  polyline.points->push_back(contour->destination);
416  break;
417  }
418  storage_offset += VerbToOffset(path_component);
419  }
420 
421  // Subtract the last storage offset increment so that the storage lookup is
422  // correct, including potentially an empty contour as well.
423  if (component_i > 0 && path_components.back() == ComponentType::kContour) {
424  storage_offset -= VerbToOffset(ComponentType::kContour);
425  component_i--;
426  }
427 
428  if (!polyline.contours.empty()) {
429  polyline.contours.back().start_direction =
430  start_direction.value_or(Vector2(0, -1));
431  }
432  EndContour(storage_offset, polyline, component_i, poly_components);
433  return polyline;
434 }
435 
436 std::optional<Rect> Path::GetBoundingBox() const {
437  return data_->bounds;
438 }
439 
441  const Matrix& transform) const {
442  auto bounds = GetBoundingBox();
443  if (!bounds.has_value()) {
444  return std::nullopt;
445  }
446  return bounds->TransformBounds(transform);
447 }
448 
449 } // namespace impeller
GLenum type
FillType GetFillType() const
Definition: path.cc:48
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:34
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:237
ComponentType GetComponentTypeAtIndex(size_t index) const
Definition: path.cc:173
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
Definition: path.cc:338
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:62
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:440
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:62
bool IsConvex() const
Definition: path.cc:52
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:178
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:105
bool IsEmpty() const
Definition: path.cc:56
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:196
void EndContour(size_t storage_offset, Polyline &polyline, size_t component_index, std::vector< PolylineContour::Component > &poly_components) const
Definition: path.cc:274
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:217
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:436
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
Definition: path.cc:67
An interface for generating a multi contour polyline as a triangle strip.
virtual void EndContour()=0
virtual void Write(Point point)=0
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:18
FillType
Definition: path.h:31
Definition: comparable.h:95
const Scalar scale
const Path::Polyline & polyline
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:87
Polyline(PointBufferPtr point_buffer, ReclaimPointBufferCallback reclaim)
Definition: path.cc:255
std::vector< PolylineContour > contours
Definition: path.h:132
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:115
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:114
PointBufferPtr points
Definition: path.h:126
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:22
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:64