| 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
9 #include "flutter/fml/logging.h"
10 #include "impeller/geometry/path_component.h"
11 #include "impeller/geometry/point.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
47 FillType Path::GetFillType() const {
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
61 void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
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) {
72 case ComponentType::kLinear: {
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 }
83 case ComponentType::kQuadratic: {
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 }
105 case Path::ComponentType::kContour:
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
129 bool Path::GetLinearComponentAtIndex(size_t index,
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
147 bool Path::GetQuadraticComponentAtIndex(
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
168 bool Path::GetCubicComponentAtIndex(size_t index,
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
188 bool Path::GetContourComponentAtIndex(size_t index,
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
206 Path::Polyline::Polyline(Path::Polyline::PointBufferPtr point_buffer,
207 Path::Polyline::ReclaimPointBufferCallback reclaim)
208 : points(std::move(point_buffer)), reclaim_points_(std::move(reclaim)) {
209 FML_DCHECK(points);
210 }
211
212 Path::Polyline::Polyline(Path::Polyline&& other) {
213 points = std::move(other.points);
214 reclaim_points_ = std::move(other.reclaim_points_);
215 contours = std::move(other.contours);
216 }
217
218 Path::Polyline::~Polyline() {
219 if (reclaim_points_) {
220 points->clear();
221 reclaim_points_(std::move(points));
222 }
223 }
224
225 void Path::EndContour(
226 size_t storage_offset,
227 Polyline& polyline,
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 }
259 case ComponentType::kQuadratic: {
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 }
279 case ComponentType::kContour: {
280 // Hit previous contour, return.
281 return;
282 };
283 }
284 storage_offset -= VerbToOffset(path_component);
285 previous_index--;
286 }
287 };
288
289 Path::Polyline Path::CreatePolyline(
290 Scalar scale,
291 Path::Polyline::PointBufferPtr point_buffer,
292 Path::Polyline::ReclaimPointBufferCallback reclaim) const {
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 }
318 case ComponentType::kQuadratic: {
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 }
344 case ComponentType::kContour:
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
391 std::optional<Rect> Path::GetTransformedBoundingBox(
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