| 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 Path::ComponentType Path::ComponentIterator::type() const {
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
31 const LinearPathComponent* Path::ComponentIterator::linear() const {
32 if (!CHECK_COMPONENT(Path::ComponentType::kLinear)) {
33 return nullptr;
34 }
35 const Point* points = &(path_.data_->points[storage_offset_]);
36 return reinterpret_cast<const LinearPathComponent*>(points);
37 }
38
39 const QuadraticPathComponent* Path::ComponentIterator::quadratic() const {
40 if (!CHECK_COMPONENT(Path::ComponentType::kQuadratic)) {
41 return nullptr;
42 }
43 const Point* points = &(path_.data_->points[storage_offset_]);
44 return reinterpret_cast<const QuadraticPathComponent*>(points);
45 }
46
47 const ConicPathComponent* Path::ComponentIterator::conic() const {
48 if (!CHECK_COMPONENT(Path::ComponentType::kConic)) {
49 return nullptr;
50 }
51 const Point* points = &(path_.data_->points[storage_offset_]);
52 return reinterpret_cast<const ConicPathComponent*>(points);
53 }
54
55 const CubicPathComponent* Path::ComponentIterator::cubic() const {
56 if (!CHECK_COMPONENT(Path::ComponentType::kCubic)) {
57 return nullptr;
58 }
59 const Point* points = &(path_.data_->points[storage_offset_]);
60 return reinterpret_cast<const CubicPathComponent*>(points);
61 }
62
63 const ContourComponent* Path::ComponentIterator::contour() const {
64 if (!CHECK_COMPONENT(Path::ComponentType::kContour)) {
65 return nullptr;
66 }
67 const Point* points = &(path_.data_->points[storage_offset_]);
68 return reinterpret_cast<const ContourComponent*>(points);
69 }
70
71 Path::ComponentIterator& Path::ComponentIterator::operator++() {
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
110 FillType Path::GetFillType() const {
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 }
145 case ComponentType::kQuadratic: {
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 }
166 case Path::ComponentType::kContour:
167 contours++;
168 }
169 storage_offset += VerbToOffset(path_component);
170 }
171 return std::make_pair(points, contours);
172 }
173
174 void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
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 }
196 case ComponentType::kQuadratic: {
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 }
229 case Path::ComponentType::kContour:
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
253 Path::ComponentIterator Path::begin() const {
254 return ComponentIterator(*this, 0u, 0u);
255 }
256
257 Path::ComponentIterator Path::end() const {
258 return ComponentIterator(*this, data_->components.size(),
259 data_->points.size());
260 }
261
262 Path::Polyline::Polyline(Path::Polyline::PointBufferPtr point_buffer,
263 Path::Polyline::ReclaimPointBufferCallback reclaim)
264 : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
265 FML_DCHECK(points);
266 }
267
268 Path::Polyline::Polyline(Path::Polyline&& other) {
269 points = std::move(other.points);
270 reclaim_points_ = std::move(other.reclaim_points_);
271 contours = std::move(other.contours);
272 }
273
274 Path::Polyline::~Polyline() {
275 if (reclaim_points_) {
276 points->clear();
277 reclaim_points_(std::move(points));
278 }
279 }
280
281 void Path::EndContour(
282 size_t storage_offset,
283 Polyline& polyline,
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 }
315 case ComponentType::kQuadratic: {
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 }
345 case ComponentType::kContour: {
346 // Hit previous contour, return.
347 return;
348 };
349 }
350 storage_offset -= VerbToOffset(path_component);
351 previous_index--;
352 }
353 };
354
355 Path::Polyline Path::CreatePolyline(
356 Scalar scale,
357 Path::Polyline::PointBufferPtr point_buffer,
358 Path::Polyline::ReclaimPointBufferCallback reclaim) const {
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 }
384 case ComponentType::kQuadratic: {
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 }
423 case ComponentType::kContour:
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
470 std::optional<Rect> Path::GetTransformedBoundingBox(
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
type
GLenum type
Definition: blit_command_gles.cc:151
impeller::Path::ComponentIterator
Definition: path.h:64
impeller::Path::ComponentIterator::contour
const ContourComponent * contour() const
Definition: path.cc:63
impeller::Path::ComponentIterator::operator++
ComponentIterator & operator++()
Definition: path.cc:71
impeller::Path::ComponentIterator::quadratic
const QuadraticPathComponent * quadratic() const
Definition: path.cc:39
impeller::Path::ComponentIterator::type
ComponentType type() const
Definition: path.cc:22
impeller::Path::ComponentIterator::linear
const LinearPathComponent * linear() const
Definition: path.cc:31
impeller::Path::ComponentIterator::conic
const ConicPathComponent * conic() const
Definition: path.cc:47
impeller::Path::ComponentIterator::cubic
const CubicPathComponent * cubic() const
Definition: path.cc:55
impeller::Path::GetFillType
FillType GetFillType() const
Definition: path.cc:110
impeller::Path::GetComponentCount
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:92
impeller::Path::Path
Path()
Definition: path.cc:16
impeller::Path::begin
ComponentIterator begin() const
Definition: path.cc:253
impeller::Path::~Path
~Path()
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:355
impeller::Path::ComponentType
ComponentType
Definition: path.h:56
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kConic
@ kConic
impeller::Path::ComponentType::kContour
@ kContour
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::IsSingleContour
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:124
impeller::Path::GetTransformedBoundingBox
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:470
impeller::Path::VerbToOffset
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:95
impeller::Path::IsConvex
bool IsConvex() const
Definition: path.cc:114
impeller::Path::end
ComponentIterator end() const
Definition: path.cc:257
impeller::Path::GetPointCount
size_t GetPointCount() const
Definition: path.cc:106
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:174
impeller::Path::IsEmpty
bool IsEmpty() const
Definition: path.cc:118
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:281
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:466
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:129
impeller::VertexWriter
An interface for generating a multi contour polyline as a triangle strip.
Definition: path_component.h:21
impeller::VertexWriter::EndContour
virtual void EndContour()=0
impeller::VertexWriter::Write
virtual void Write(Point point)=0
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
CHECK_COMPONENT
#define CHECK_COMPONENT(type)
Definition: path.cc:26
path.h
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::FillType
FillType
Definition: path.h:32
impeller::Convexity::kConvex
@ kConvex
std
Definition: comparable.h:95
path_component.h
point.h
scale
const Scalar scale
Definition: stroke_path_geometry.cc:319
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:314
impeller::ConicPathComponent
Definition: path_component.h:226
impeller::ConicPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:385
impeller::ConicPathComponent::CountLinearPathComponents
size_t CountLinearPathComponents(Scalar scale) const
Definition: path_component.cc:362
impeller::ConicPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
Definition: path_component.cc:334
impeller::ConicPathComponent::p1
Point p1
Definition: path_component.h:228
impeller::ConicPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
Definition: path_component.cc:343
impeller::ContourComponent
Definition: path_component.h:332
impeller::CubicPathComponent
Definition: path_component.h:280
impeller::CubicPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
Definition: path_component.cc:494
impeller::CubicPathComponent::CountLinearPathComponents
size_t CountLinearPathComponents(Scalar scale) const
Definition: path_component.cc:475
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:282
impeller::CubicPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale, std::vector< Point > &points) const
Definition: path_component.cc:459
impeller::CubicPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:597
impeller::LinearPathComponent
Definition: path_component.h:111
impeller::LinearPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:248
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:113
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:112
impeller::LinearPathComponent::AppendPolylinePoints
void AppendPolylinePoints(std::vector< Point > &points) const
Definition: path_component.cc:230
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Path::PolylineContour
Definition: path.h:112
impeller::Path::PolylineContour::start_index
size_t start_index
Index that denotes the first point of this contour.
Definition: path.h:122
impeller::Path::Polyline
Definition: path.h:146
impeller::Path::Polyline::Polyline
Polyline(PointBufferPtr point_buffer, ReclaimPointBufferCallback reclaim)
Definition: path.cc:262
impeller::Path::Polyline::~Polyline
~Polyline()
Definition: path.cc:274
impeller::Path::Polyline::ReclaimPointBufferCallback
std::function< void(PointBufferPtr)> ReclaimPointBufferCallback
Definition: path.h:150
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:149
impeller::Path::Polyline::points
PointBufferPtr points
Definition: path.h:161
impeller::Path::Polyline::GetContourPointBounds
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:80
impeller::QuadraticPathComponent
Definition: path_component.h:135
impeller::QuadraticPathComponent::CountLinearPathComponents
size_t CountLinearPathComponents(Scalar scale) const
Definition: path_component.cc:298
impeller::QuadraticPathComponent::GetEndDirection
std::optional< Vector2 > GetEndDirection() const
Definition: path_component.cc:317
impeller::QuadraticPathComponent::AppendPolylinePoints
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
Definition: path_component.cc:279
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:137
impeller::QuadraticPathComponent::ToLinearPathComponents
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
Definition: path_component.cc:287
impeller::TPoint< Scalar >
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:67