| 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 <utility>
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 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
48 FillType Path::GetFillType() const {
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) {
79 case ComponentType::kLinear: {
80 points += 2;
81 break;
82 }
83 case ComponentType::kQuadratic: {
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 }
97 case Path::ComponentType::kContour:
98 contours++;
99 }
100 storage_offset += VerbToOffset(path_component);
101 }
102 return std::make_pair(points, contours);
103 }
104
105 void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
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 }
127 case ComponentType::kQuadratic: {
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 }
149 case Path::ComponentType::kContour:
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
173 Path::ComponentType Path::GetComponentTypeAtIndex(size_t index) const {
174 auto& components = data_->components;
175 return components[index];
176 }
177
178 bool Path::GetLinearComponentAtIndex(size_t index,
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
196 bool Path::GetQuadraticComponentAtIndex(
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
217 bool Path::GetCubicComponentAtIndex(size_t index,
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
237 bool Path::GetContourComponentAtIndex(size_t index,
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
255 Path::Polyline::Polyline(Path::Polyline::PointBufferPtr point_buffer,
256 Path::Polyline::ReclaimPointBufferCallback reclaim)
257 : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
258 FML_DCHECK(points);
259 }
260
261 Path::Polyline::Polyline(Path::Polyline&& other) {
262 points = std::move(other.points);
263 reclaim_points_ = std::move(other.reclaim_points_);
264 contours = std::move(other.contours);
265 }
266
267 Path::Polyline::~Polyline() {
268 if (reclaim_points_) {
269 points->clear();
270 reclaim_points_(std::move(points));
271 }
272 }
273
274 void Path::EndContour(
275 size_t storage_offset,
276 Polyline& polyline,
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 }
308 case ComponentType::kQuadratic: {
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 }
328 case ComponentType::kContour: {
329 // Hit previous contour, return.
330 return;
331 };
332 }
333 storage_offset -= VerbToOffset(path_component);
334 previous_index--;
335 }
336 };
337
338 Path::Polyline Path::CreatePolyline(
339 Scalar scale,
340 Path::Polyline::PointBufferPtr point_buffer,
341 Path::Polyline::ReclaimPointBufferCallback reclaim) const {
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 }
367 case ComponentType::kQuadratic: {
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 }
393 case ComponentType::kContour:
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
440 std::optional<Rect> Path::GetTransformedBoundingBox(
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
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:311
point.h
impeller::LinearPathComponent
Definition: path_component.h:110
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:196
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::VertexWriter::Write
virtual void Write(Point point)=0
impeller::Path::PolylineContour
Definition: path.h:77
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:176
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:112
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:136
impeller::Path::PolylineContour::start_index
size_t start_index
Index that denotes the first point of this contour.
Definition: path.h:87
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:64
impeller::VertexWriter::EndContour
virtual void EndContour()=0
impeller::CubicPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale, std::vector< Point > &points) const
Definition: path_component.cc:326
impeller::LinearPathComponent::AppendPolylinePoints
void AppendPolylinePoints(std::vector< Point > &points) const
Definition: path_component.cc:215
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:436
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::IsEmpty
bool IsEmpty() const
Definition: path.cc:56
impeller::QuadraticPathComponent::CountLinearPathComponents
size_t CountLinearPathComponents(Scalar scale) const
Definition: path_component.cc:283
impeller::QuadraticPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:302
impeller::Path::Polyline::Polyline
Polyline(PointBufferPtr point_buffer, ReclaimPointBufferCallback reclaim)
Definition: path.cc:255
impeller::Path::~Path
~Path()
impeller::QuadraticPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
Definition: path_component.cc:272
impeller::Path::GetComponentTypeAtIndex
ComponentType GetComponentTypeAtIndex(size_t index) const
Definition: path.cc:173
impeller::Path::Polyline
Definition: path.h:111
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:105
impeller::Path::ComponentType
ComponentType
Definition: path.h:55
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:217
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:111
impeller::Path::Polyline::ReclaimPointBufferCallback
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:115
impeller::Path::GetTransformedBoundingBox
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:440
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:274
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
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:151
impeller::FillType
FillType
Definition: path.h:31
impeller::CubicPathComponent::CountLinearPathComponents
size_t CountLinearPathComponents(Scalar scale) const
Definition: path_component.cc:342
impeller::Path::Path
Path()
Definition: path.cc:16
impeller::CubicPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:464
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:237
impeller::CubicPathComponent
Definition: path_component.h:174
impeller::Path::Polyline::points
PointBufferPtr points
Definition: path.h:126
impeller::CubicPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
Definition: path_component.cc:361
impeller::QuadraticPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
Definition: path_component.cc:264
impeller::VertexWriter
An interface for generating a multi contour polyline as a triangle strip.
Definition: path_component.h:20
impeller::Path::IsSingleContour
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:62
std
Definition: comparable.h:95
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:132
scale
const Scalar scale
Definition: stroke_path_geometry.cc:316
impeller::Path::VerbToOffset
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:62
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:114
impeller::Path::Polyline::~Polyline
~Polyline()
Definition: path.cc:267
impeller::Path::IsConvex
bool IsConvex() const
Definition: path.cc:52
path_component.h
impeller::ContourComponent
Definition: path_component.h:226
path.h
impeller::Convexity::kConvex
@ kConvex
impeller::LinearPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:233
impeller
Definition: allocation.cc:12
impeller::QuadraticPathComponent
Definition: path_component.h:134
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:338
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:178
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:48
impeller::Path::CountStorage
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
Definition: path.cc:67