| 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
5 #include "impeller/geometry/path.h"
6
7 #include <optional>
8 #include <variant>
9
10 #include "flutter/fml/logging.h"
11 #include "impeller/geometry/path_component.h"
12 #include "impeller/geometry/point.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
51 FillType Path::GetFillType() const {
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
63 void Path::EnumerateComponents(
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) {
72 case ComponentType::kLinear:
73 if (linear_applier) {
74 linear_applier(currentIndex,
75 LinearPathComponent(points[component.index],
76 points[component.index + 1]));
77 }
78 break;
79 case ComponentType::kQuadratic:
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;
87 case ComponentType::kCubic:
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;
96 case ComponentType::kContour:
97 if (contour_applier) {
98 contour_applier(currentIndex, data_->contours[component.index]);
99 }
100 break;
101 }
102 currentIndex++;
103 }
104 }
105
106 bool Path::GetLinearComponentAtIndex(size_t index,
107 LinearPathComponent& linear) const {
108 auto& components = data_->components;
109
110 if (index >= components.size()) {
111 return false;
112 }
113
114 if (components[index].type != ComponentType::kLinear) {
115 return false;
116 }
117
118 auto& points = data_->points;
119 auto point_index = components[index].index;
120 linear = LinearPathComponent(points[point_index], points[point_index + 1]);
121 return true;
122 }
123
124 bool Path::GetQuadraticComponentAtIndex(
125 size_t index,
126 QuadraticPathComponent& quadratic) const {
127 auto& components = data_->components;
128
129 if (index >= components.size()) {
130 return false;
131 }
132
133 if (components[index].type != ComponentType::kQuadratic) {
134 return false;
135 }
136
137 auto& points = data_->points;
138 auto point_index = components[index].index;
139 quadratic = QuadraticPathComponent(
140 points[point_index], points[point_index + 1], points[point_index + 2]);
141 return true;
142 }
143
144 bool Path::GetCubicComponentAtIndex(size_t index,
145 CubicPathComponent& cubic) const {
146 auto& components = data_->components;
147
148 if (index >= components.size()) {
149 return false;
150 }
151
152 if (components[index].type != ComponentType::kCubic) {
153 return false;
154 }
155
156 auto& points = data_->points;
157 auto point_index = components[index].index;
158 cubic = CubicPathComponent(points[point_index], points[point_index + 1],
159 points[point_index + 2], points[point_index + 3]);
160 return true;
161 }
162
163 bool Path::GetContourComponentAtIndex(size_t index,
164 ContourComponent& move) const {
165 auto& components = data_->components;
166
167 if (index >= components.size()) {
168 return false;
169 }
170
171 if (components[index].type != ComponentType::kContour) {
172 return false;
173 }
174
175 move = data_->contours[components[index].index];
176 return true;
177 }
178
179 Path::Polyline::Polyline(Path::Polyline::PointBufferPtr point_buffer,
180 Path::Polyline::ReclaimPointBufferCallback reclaim)
181 : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
182 FML_DCHECK(points);
183 }
184
185 Path::Polyline::Polyline(Path::Polyline&& other) {
186 points = std::move(other.points);
187 reclaim_points_ = std::move(other.reclaim_points_);
188 contours = std::move(other.contours);
189 }
190
191 Path::Polyline::~Polyline() {
192 if (reclaim_points_) {
193 points->clear();
194 reclaim_points_(std::move(points));
195 }
196 }
197
198 Path::Polyline Path::CreatePolyline(
199 Scalar scale,
200 Path::Polyline::PointBufferPtr point_buffer,
201 Path::Polyline::ReclaimPointBufferCallback reclaim) const {
202 Polyline polyline(std::move(point_buffer), std::move(reclaim));
203
204 auto& path_components = data_->components;
205 auto& path_points = data_->points;
206
207 auto get_path_component = [&path_components, &path_points](
208 size_t component_i) -> PathComponentVariant {
209 if (component_i >= path_components.size()) {
210 return std::monostate{};
211 }
212 const auto& component = path_components[component_i];
213 switch (component.type) {
214 case ComponentType::kLinear:
215 return reinterpret_cast<const LinearPathComponent*>(
216 &path_points[component.index]);
217 case ComponentType::kQuadratic:
218 return reinterpret_cast<const QuadraticPathComponent*>(
219 &path_points[component.index]);
220 case ComponentType::kCubic:
221 return reinterpret_cast<const CubicPathComponent*>(
222 &path_points[component.index]);
223 case ComponentType::kContour:
224 return std::monostate{};
225 }
226 };
227
228 auto compute_contour_start_direction =
229 [&get_path_component](size_t current_path_component_index) {
230 size_t next_component_index = current_path_component_index + 1;
231 while (!std::holds_alternative<std::monostate>(
232 get_path_component(next_component_index))) {
233 auto next_component = get_path_component(next_component_index);
234 auto maybe_vector =
235 std::visit(PathComponentStartDirectionVisitor(), next_component);
236 if (maybe_vector.has_value()) {
237 return maybe_vector.value();
238 } else {
239 next_component_index++;
240 }
241 }
242 return Vector2(0, -1);
243 };
244
245 std::vector<PolylineContour::Component> poly_components;
246 std::optional<size_t> previous_path_component_index;
247 auto end_contour = [&polyline, &previous_path_component_index,
248 &get_path_component, &poly_components]() {
249 // Whenever a contour has ended, extract the exact end direction from
250 // the last component.
251 if (polyline.contours.empty()) {
252 return;
253 }
254
255 if (!previous_path_component_index.has_value()) {
256 return;
257 }
258
259 auto& contour = polyline.contours.back();
260 contour.end_direction = Vector2(0, 1);
261 contour.components = poly_components;
262 poly_components.clear();
263
264 size_t previous_index = previous_path_component_index.value();
265 while (!std::holds_alternative<std::monostate>(
266 get_path_component(previous_index))) {
267 auto previous_component = get_path_component(previous_index);
268 auto maybe_vector =
269 std::visit(PathComponentEndDirectionVisitor(), previous_component);
270 if (maybe_vector.has_value()) {
271 contour.end_direction = maybe_vector.value();
272 break;
273 } else {
274 if (previous_index == 0) {
275 break;
276 }
277 previous_index--;
278 }
279 }
280 };
281
282 for (size_t component_i = 0; component_i < path_components.size();
283 component_i++) {
284 const auto& path_component = path_components[component_i];
285 switch (path_component.type) {
286 case ComponentType::kLinear:
287 poly_components.push_back({
288 .component_start_index = polyline.points->size() - 1,
289 .is_curve = false,
290 });
291 reinterpret_cast<const LinearPathComponent*>(
292 &path_points[path_component.index])
293 ->AppendPolylinePoints(*polyline.points);
294 previous_path_component_index = component_i;
295 break;
296 case ComponentType::kQuadratic:
297 poly_components.push_back({
298 .component_start_index = polyline.points->size() - 1,
299 .is_curve = true,
300 });
301 reinterpret_cast<const QuadraticPathComponent*>(
302 &path_points[path_component.index])
303 ->AppendPolylinePoints(scale, *polyline.points);
304 previous_path_component_index = component_i;
305 break;
306 case ComponentType::kCubic:
307 poly_components.push_back({
308 .component_start_index = polyline.points->size() - 1,
309 .is_curve = true,
310 });
311 reinterpret_cast<const CubicPathComponent*>(
312 &path_points[path_component.index])
313 ->AppendPolylinePoints(scale, *polyline.points);
314 previous_path_component_index = component_i;
315 break;
316 case ComponentType::kContour:
317 if (component_i == path_components.size() - 1) {
318 // If the last component is a contour, that means it's an empty
319 // contour, so skip it.
320 continue;
321 }
322 end_contour();
323
324 Vector2 start_direction = compute_contour_start_direction(component_i);
325 const auto& contour = data_->contours[path_component.index];
326 polyline.contours.push_back({.start_index = polyline.points->size(),
327 .is_closed = contour.is_closed,
328 .start_direction = start_direction,
329 .components = poly_components});
330
331 polyline.points->push_back(contour.destination);
332 break;
333 }
334 }
335 end_contour();
336 return polyline;
337 }
338
339 std::optional<Rect> Path::GetBoundingBox() const {
340 return data_->bounds;
341 }
342
343 std::optional<Rect> Path::GetTransformedBoundingBox(
344 const Matrix& transform) const {
345 auto bounds = GetBoundingBox();
346 if (!bounds.has_value()) {
347 return std::nullopt;
348 }
349 return bounds->TransformBounds(transform);
350 }
351
352 } // namespace impeller
path.h
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:292
point.h
impeller::LinearPathComponent
Definition: path_component.h:29
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:124
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:320
impeller::PathComponentEndDirectionVisitor
Definition: path_component.h:180
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:339
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:179
impeller::Path::~Path
~Path()
impeller::Path::Polyline
Definition: path.h:94
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:144
impeller::Path::Polyline::ReclaimPointBufferCallback
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:98
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:343
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:29
impeller::PathComponentStartDirectionVisitor
Definition: path_component.h:171
impeller::Path::Path
Path()
Definition: path.cc:16
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:163
impeller::CubicPathComponent
Definition: path_component.h:99
impeller::Path::Polyline::points
PointBufferPtr points
Definition: path.h:109
impeller::Path::Applier
std::function< void(size_t index, const T &component)> Applier
Definition: path.h:141
std
Definition: comparable.h:95
impeller::TPoint< Scalar >
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:115
scale
const Scalar scale
Definition: stroke_path_geometry.cc:297
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:97
impeller::Path::Polyline::~Polyline
~Polyline()
Definition: path.cc:191
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:169
path_component.h
impeller::ContourComponent
Definition: path_component.h:152
impeller::Convexity::kConvex
@ kConvex
impeller
Definition: aiks_blur_unittests.cc:20
impeller::QuadraticPathComponent
Definition: path_component.h:53
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:198
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:106
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