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 
9 #include "flutter/fml/logging.h"
12 
13 namespace impeller {
14 
15 Path::Path() : data_(new Data()) {}
16 
17 Path::Path(Data data) : data_(std::make_shared<Data>(std::move(data))) {}
18 
19 Path::~Path() = default;
20 
21 std::tuple<size_t, size_t> Path::Polyline::GetContourPointBounds(
22  size_t contour_index) const {
23  if (contour_index >= contours.size()) {
24  return {points->size(), points->size()};
25  }
26  const size_t start_index = contours.at(contour_index).start_index;
27  const size_t end_index = (contour_index >= contours.size() - 1)
28  ? points->size()
29  : contours.at(contour_index + 1).start_index;
30  return std::make_tuple(start_index, end_index);
31 }
32 
33 size_t Path::GetComponentCount(std::optional<ComponentType> type) const {
34  if (!type.has_value()) {
35  return data_->components.size();
36  }
37  auto type_value = type.value();
38  size_t count = 0u;
39  for (const auto& component : data_->components) {
40  if (component == type_value) {
41  count++;
42  }
43  }
44  return count;
45 }
46 
48  return data_->fill;
49 }
50 
51 bool Path::IsConvex() const {
52  return data_->convexity == Convexity::kConvex;
53 }
54 
55 bool Path::IsEmpty() const {
56  return data_->points.empty() ||
57  (data_->components.size() == 1 &&
58  data_->components[0] == ComponentType::kContour);
59 }
60 
62  auto& path_components = data_->components;
63  auto& path_points = data_->points;
64  bool started_contour = false;
65  bool first_point = true;
66 
67  size_t storage_offset = 0u;
68  for (size_t component_i = 0; component_i < path_components.size();
69  component_i++) {
70  const auto& path_component = path_components[component_i];
71  switch (path_component) {
73  const LinearPathComponent* linear =
74  reinterpret_cast<const LinearPathComponent*>(
75  &path_points[storage_offset]);
76  if (first_point) {
77  writer.Write(linear->p1);
78  first_point = false;
79  }
80  writer.Write(linear->p2);
81  break;
82  }
84  const QuadraticPathComponent* quad =
85  reinterpret_cast<const QuadraticPathComponent*>(
86  &path_points[storage_offset]);
87  if (first_point) {
88  writer.Write(quad->p1);
89  first_point = false;
90  }
91  quad->ToLinearPathComponents(scale, writer);
92  break;
93  }
94  case ComponentType::kCubic: {
95  const CubicPathComponent* cubic =
96  reinterpret_cast<const CubicPathComponent*>(
97  &path_points[storage_offset]);
98  if (first_point) {
99  writer.Write(cubic->p1);
100  first_point = false;
101  }
102  cubic->ToLinearPathComponents(scale, writer);
103  break;
104  }
106  if (component_i == path_components.size() - 1) {
107  // If the last component is a contour, that means it's an empty
108  // contour, so skip it.
109  continue;
110  }
111  // The contour component type is the first segment in a contour.
112  // Since this should contain the destination (if closed), we
113  // can start with this point. If there was already an open
114  // contour, or we've reached the end of the verb list, we
115  // also close the contour.
116  if (started_contour) {
117  writer.EndContour();
118  }
119  started_contour = true;
120  first_point = true;
121  }
122  storage_offset += VerbToOffset(path_component);
123  }
124  if (started_contour) {
125  writer.EndContour();
126  }
127 }
128 
130  LinearPathComponent& linear) const {
131  auto& components = data_->components;
132  if (index >= components.size() ||
133  components[index] != ComponentType::kLinear) {
134  return false;
135  }
136 
137  size_t storage_offset = 0u;
138  for (auto i = 0u; i < index; i++) {
139  storage_offset += VerbToOffset(components[i]);
140  }
141  auto& points = data_->points;
142  linear =
143  LinearPathComponent(points[storage_offset], points[storage_offset + 1]);
144  return true;
145 }
146 
148  size_t index,
149  QuadraticPathComponent& quadratic) const {
150  auto& components = data_->components;
151  if (index >= components.size() ||
152  components[index] != ComponentType::kQuadratic) {
153  return false;
154  }
155 
156  size_t storage_offset = 0u;
157  for (auto i = 0u; i < index; i++) {
158  storage_offset += VerbToOffset(components[i]);
159  }
160  auto& points = data_->points;
161 
162  quadratic =
163  QuadraticPathComponent(points[storage_offset], points[storage_offset + 1],
164  points[storage_offset + 2]);
165  return true;
166 }
167 
169  CubicPathComponent& cubic) const {
170  auto& components = data_->components;
171  if (index >= components.size() ||
172  components[index] != ComponentType::kCubic) {
173  return false;
174  }
175 
176  size_t storage_offset = 0u;
177  for (auto i = 0u; i < index; i++) {
178  storage_offset += VerbToOffset(components[i]);
179  }
180  auto& points = data_->points;
181 
182  cubic = CubicPathComponent(points[storage_offset], points[storage_offset + 1],
183  points[storage_offset + 2],
184  points[storage_offset + 3]);
185  return true;
186 }
187 
189  ContourComponent& move) const {
190  auto& components = data_->components;
191  if (index >= components.size() ||
192  components[index] != ComponentType::kContour) {
193  return false;
194  }
195 
196  size_t storage_offset = 0u;
197  for (auto i = 0u; i < index; i++) {
198  storage_offset += VerbToOffset(components[i]);
199  }
200  auto& points = data_->points;
201 
202  move = ContourComponent(points[storage_offset], points[storage_offset + 1]);
203  return true;
204 }
205 
208  : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
209  FML_DCHECK(points);
210 }
211 
213  points = std::move(other.points);
214  reclaim_points_ = std::move(other.reclaim_points_);
215  contours = std::move(other.contours);
216 }
217 
219  if (reclaim_points_) {
220  points->clear();
221  reclaim_points_(std::move(points));
222  }
223 }
224 
226  size_t storage_offset,
228  size_t component_index,
229  std::vector<PolylineContour::Component>& poly_components) const {
230  auto& path_components = data_->components;
231  auto& path_points = data_->points;
232  // Whenever a contour has ended, extract the exact end direction from
233  // the last component.
234  if (polyline.contours.empty() || component_index == 0) {
235  return;
236  }
237 
238  auto& contour = polyline.contours.back();
239  contour.end_direction = Vector2(0, 1);
240  contour.components = poly_components;
241  poly_components.clear();
242 
243  size_t previous_index = component_index - 1;
244  storage_offset -= VerbToOffset(path_components[previous_index]);
245 
246  while (previous_index >= 0 && storage_offset >= 0) {
247  const auto& path_component = path_components[previous_index];
248  switch (path_component) {
249  case ComponentType::kLinear: {
250  auto* linear = reinterpret_cast<const LinearPathComponent*>(
251  &path_points[storage_offset]);
252  auto maybe_end = linear->GetEndDirection();
253  if (maybe_end.has_value()) {
254  contour.end_direction = maybe_end.value();
255  return;
256  }
257  break;
258  }
260  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
261  &path_points[storage_offset]);
262  auto maybe_end = quad->GetEndDirection();
263  if (maybe_end.has_value()) {
264  contour.end_direction = maybe_end.value();
265  return;
266  }
267  break;
268  }
269  case ComponentType::kCubic: {
270  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
271  &path_points[storage_offset]);
272  auto maybe_end = cubic->GetEndDirection();
273  if (maybe_end.has_value()) {
274  contour.end_direction = maybe_end.value();
275  return;
276  }
277  break;
278  }
280  // Hit previous contour, return.
281  return;
282  };
283  }
284  storage_offset -= VerbToOffset(path_component);
285  previous_index--;
286  }
287 };
288 
290  Scalar scale,
291  Path::Polyline::PointBufferPtr point_buffer,
293  Polyline polyline(std::move(point_buffer), std::move(reclaim));
294 
295  auto& path_components = data_->components;
296  auto& path_points = data_->points;
297  std::optional<Vector2> start_direction;
298  std::vector<PolylineContour::Component> poly_components;
299  size_t storage_offset = 0u;
300  size_t component_i = 0;
301 
302  for (; component_i < path_components.size(); component_i++) {
303  auto path_component = path_components[component_i];
304  switch (path_component) {
305  case ComponentType::kLinear: {
306  poly_components.push_back({
307  .component_start_index = polyline.points->size() - 1,
308  .is_curve = false,
309  });
310  auto* linear = reinterpret_cast<const LinearPathComponent*>(
311  &path_points[storage_offset]);
312  linear->AppendPolylinePoints(*polyline.points);
313  if (!start_direction.has_value()) {
314  start_direction = linear->GetStartDirection();
315  }
316  break;
317  }
319  poly_components.push_back({
320  .component_start_index = polyline.points->size() - 1,
321  .is_curve = true,
322  });
323  auto* quad = reinterpret_cast<const QuadraticPathComponent*>(
324  &path_points[storage_offset]);
325  quad->AppendPolylinePoints(scale, *polyline.points);
326  if (!start_direction.has_value()) {
327  start_direction = quad->GetStartDirection();
328  }
329  break;
330  }
331  case ComponentType::kCubic: {
332  poly_components.push_back({
333  .component_start_index = polyline.points->size() - 1,
334  .is_curve = true,
335  });
336  auto* cubic = reinterpret_cast<const CubicPathComponent*>(
337  &path_points[storage_offset]);
338  cubic->AppendPolylinePoints(scale, *polyline.points);
339  if (!start_direction.has_value()) {
340  start_direction = cubic->GetStartDirection();
341  }
342  break;
343  }
345  if (component_i == path_components.size() - 1) {
346  // If the last component is a contour, that means it's an empty
347  // contour, so skip it.
348  break;
349  }
350  if (!polyline.contours.empty()) {
351  polyline.contours.back().start_direction =
352  start_direction.value_or(Vector2(0, -1));
353  start_direction = std::nullopt;
354  }
355  EndContour(storage_offset, polyline, component_i, poly_components);
356 
357  auto* contour = reinterpret_cast<const ContourComponent*>(
358  &path_points[storage_offset]);
359  polyline.contours.push_back(PolylineContour{
360  .start_index = polyline.points->size(), //
361  .is_closed = contour->IsClosed(), //
362  .start_direction = Vector2(0, -1), //
363  .components = poly_components //
364  });
365 
366  polyline.points->push_back(contour->destination);
367  break;
368  }
369  storage_offset += VerbToOffset(path_component);
370  }
371 
372  // Subtract the last storage offset increment so that the storage lookup is
373  // correct, including potentially an empty contour as well.
374  if (component_i > 0 && path_components.back() == ComponentType::kContour) {
375  storage_offset -= VerbToOffset(ComponentType::kContour);
376  component_i--;
377  }
378 
379  if (!polyline.contours.empty()) {
380  polyline.contours.back().start_direction =
381  start_direction.value_or(Vector2(0, -1));
382  }
383  EndContour(storage_offset, polyline, component_i, poly_components);
384  return polyline;
385 }
386 
387 std::optional<Rect> Path::GetBoundingBox() const {
388  return data_->bounds;
389 }
390 
392  const Matrix& transform) const {
393  auto bounds = GetBoundingBox();
394  if (!bounds.has_value()) {
395  return std::nullopt;
396  }
397  return bounds->TransformBounds(transform);
398 }
399 
400 } // namespace impeller
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:296
point.h
impeller::LinearPathComponent
Definition: path_component.h:38
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:147
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Path::PolylineContour
Definition: path.h:76
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:102
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:40
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:64
impeller::Path::PolylineContour::start_index
size_t start_index
Index that denotes the first point of this contour.
Definition: path.h:86
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::CubicPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale, std::vector< Point > &points) const
Definition: path_component.cc:229
impeller::LinearPathComponent::AppendPolylinePoints
void AppendPolylinePoints(std::vector< Point > &points) const
Definition: path_component.cc:122
impeller::VertexWriter::EndContour
void EndContour()
Definition: path_component.cc:17
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:387
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::IsEmpty
bool IsEmpty() const
Definition: path.cc:55
impeller::QuadraticPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:205
impeller::Path::Polyline::Polyline
Polyline(PointBufferPtr point_buffer, ReclaimPointBufferCallback reclaim)
Definition: path.cc:206
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:110
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:61
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:168
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:39
impeller::Path::Polyline::ReclaimPointBufferCallback
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:114
impeller::Path::GetTransformedBoundingBox
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:391
impeller::Path::EndContour
void EndContour(size_t storage_offset, Polyline &polyline, size_t component_index, std::vector< PolylineContour::Component > &poly_components) const
Definition: path.cc:225
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:213
impeller::Path::Polyline::GetContourPointBounds
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:21
type
GLenum type
Definition: blit_command_gles.cc:127
impeller::FillType
FillType
Definition: path.h:30
impeller::Path::Path
Path()
Definition: path.cc:15
impeller::CubicPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:363
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:188
impeller::CubicPathComponent
Definition: path_component.h:100
impeller::Path::Polyline::points
PointBufferPtr points
Definition: path.h:125
impeller::CubicPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
Definition: path_component.cc:260
impeller::QuadraticPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
Definition: path_component.cc:171
impeller::VertexWriter
An interface for generating a multi contour polyline as a triangle strip.
Definition: path_component.h:20
std
Definition: comparable.h:95
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:33
impeller::Path::Polyline::contours
std::vector< PolylineContour > contours
Definition: path.h:131
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::Path::VerbToOffset
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:61
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:113
impeller::Path::Polyline::~Polyline
~Polyline()
Definition: path.cc:218
impeller::Path::IsConvex
bool IsConvex() const
Definition: path.cc:51
path_component.h
impeller::ContourComponent
Definition: path_component.h:150
path.h
impeller::Convexity::kConvex
@ kConvex
impeller::LinearPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:140
impeller
Definition: allocation.cc:12
impeller::QuadraticPathComponent
Definition: path_component.h:62
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:289
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:129
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:47