Flutter Engine
The Flutter Engine
path_builder.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 "path_builder.h"
6
7#include <cmath>
8
9namespace impeller {
10
12 AddContourComponent({});
13}
14
16
18 prototype_.fill = fill;
19 return Path(prototype_);
20}
21
23 prototype_.fill = fill;
24 UpdateBounds();
25 return Path(std::move(prototype_));
26}
27
28void PathBuilder::Reserve(size_t point_size, size_t verb_size) {
29 prototype_.points.reserve(point_size);
30 prototype_.components.reserve(verb_size);
31}
32
33PathBuilder& PathBuilder::MoveTo(Point point, bool relative) {
34 current_ = relative ? current_ + point : point;
35 subpath_start_ = current_;
36 AddContourComponent(current_);
37 return *this;
38}
39
41 // If the subpath start is the same as the current position, this
42 // is an empty contour and inserting a line segment will just
43 // confuse the tessellator.
44 if (subpath_start_ != current_) {
45 LineTo(subpath_start_);
46 }
47 SetContourClosed(true);
48 AddContourComponent(current_);
49 return *this;
50}
51
52PathBuilder& PathBuilder::LineTo(Point point, bool relative) {
53 point = relative ? current_ + point : point;
54 AddLinearComponent(current_, point);
55 current_ = point;
56 return *this;
57}
58
60 Point endpoint =
61 relative ? Point{current_.x + x, current_.y} : Point{x, current_.y};
62 AddLinearComponent(current_, endpoint);
63 current_ = endpoint;
64 return *this;
65}
66
68 Point endpoint =
69 relative ? Point{current_.x, current_.y + y} : Point{current_.x, y};
70 AddLinearComponent(current_, endpoint);
71 current_ = endpoint;
72 return *this;
73}
74
76 Point point,
77 bool relative) {
78 point = relative ? current_ + point : point;
79 controlPoint = relative ? current_ + controlPoint : controlPoint;
80 AddQuadraticComponent(current_, controlPoint, point);
81 current_ = point;
82 return *this;
83}
84
86 prototype_.convexity = value;
87 return *this;
88}
89
91 Point controlPoint2,
92 Point point,
93 bool relative) {
94 controlPoint1 = relative ? current_ + controlPoint1 : controlPoint1;
95 controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2;
96 point = relative ? current_ + point : point;
97 AddCubicComponent(current_, controlPoint1, controlPoint2, point);
98 current_ = point;
99 return *this;
100}
101
103 MoveTo(p1);
104 AddQuadraticComponent(p1, cp, p2);
105 return *this;
106}
107
109 Point cp1,
110 Point cp2,
111 Point p2) {
112 MoveTo(p1);
113 AddCubicComponent(p1, cp1, cp2, p2);
114 return *this;
115}
116
118 auto origin = rect.GetOrigin();
119 auto size = rect.GetSize();
120
121 auto tl = origin;
122 auto bl = origin + Point{0.0, size.height};
123 auto br = origin + size;
124 auto tr = origin + Point{size.width, 0.0};
125
126 MoveTo(tl);
127 LineTo(tr);
128 LineTo(br);
129 LineTo(bl);
130 Close();
131
132 return *this;
133}
134
136 return AddOval(Rect::MakeXYWH(c.x - r, c.y - r, 2.0f * r, 2.0f * r));
137}
138
140 return radius <= 0.0 ? AddRect(rect)
142}
143
145 return radii.width <= 0 || radii.height <= 0
146 ? AddRect(rect)
148}
149
151 if (radii.AreAllZero()) {
152 return AddRect(rect);
153 }
154
155 auto rect_origin = rect.GetOrigin();
156 auto rect_size = rect.GetSize();
157
158 current_ = rect_origin + Point{radii.top_left.x, 0.0};
159
160 MoveTo({rect_origin.x + radii.top_left.x, rect_origin.y});
161
162 //----------------------------------------------------------------------------
163 // Top line.
164 //
165 AddLinearComponent(
166 {rect_origin.x + radii.top_left.x, rect_origin.y},
167 {rect_origin.x + rect_size.width - radii.top_right.x, rect_origin.y});
168
169 //----------------------------------------------------------------------------
170 // Top right arc.
171 //
172 AddRoundedRectTopRight(rect, radii);
173
174 //----------------------------------------------------------------------------
175 // Right line.
176 //
177 AddLinearComponent(
178 {rect_origin.x + rect_size.width, rect_origin.y + radii.top_right.y},
179 {rect_origin.x + rect_size.width,
180 rect_origin.y + rect_size.height - radii.bottom_right.y});
181
182 //----------------------------------------------------------------------------
183 // Bottom right arc.
184 //
185 AddRoundedRectBottomRight(rect, radii);
186
187 //----------------------------------------------------------------------------
188 // Bottom line.
189 //
190 AddLinearComponent(
191 {rect_origin.x + rect_size.width - radii.bottom_right.x,
192 rect_origin.y + rect_size.height},
193 {rect_origin.x + radii.bottom_left.x, rect_origin.y + rect_size.height});
194
195 //----------------------------------------------------------------------------
196 // Bottom left arc.
197 //
198 AddRoundedRectBottomLeft(rect, radii);
199
200 //----------------------------------------------------------------------------
201 // Left line.
202 //
203 AddLinearComponent(
204 {rect_origin.x, rect_origin.y + rect_size.height - radii.bottom_left.y},
205 {rect_origin.x, rect_origin.y + radii.top_left.y});
206
207 //----------------------------------------------------------------------------
208 // Top left arc.
209 //
210 AddRoundedRectTopLeft(rect, radii);
211
212 Close();
213
214 return *this;
215}
216
217PathBuilder& PathBuilder::AddRoundedRectTopLeft(Rect rect,
218 RoundingRadii radii) {
219 const auto magic_top_left = radii.top_left * kArcApproximationMagic;
220 const auto corner = rect.GetOrigin();
221 AddCubicComponent({corner.x, corner.y + radii.top_left.y},
222 {corner.x, corner.y + radii.top_left.y - magic_top_left.y},
223 {corner.x + radii.top_left.x - magic_top_left.x, corner.y},
224 {corner.x + radii.top_left.x, corner.y});
225 return *this;
226}
227
228PathBuilder& PathBuilder::AddRoundedRectTopRight(Rect rect,
229 RoundingRadii radii) {
230 const auto magic_top_right = radii.top_right * kArcApproximationMagic;
231 const auto corner = rect.GetOrigin() + Point{rect.GetWidth(), 0};
232 AddCubicComponent(
233 {corner.x - radii.top_right.x, corner.y},
234 {corner.x - radii.top_right.x + magic_top_right.x, corner.y},
235 {corner.x, corner.y + radii.top_right.y - magic_top_right.y},
236 {corner.x, corner.y + radii.top_right.y});
237 return *this;
238}
239
240PathBuilder& PathBuilder::AddRoundedRectBottomRight(Rect rect,
241 RoundingRadii radii) {
242 const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic;
243 const auto corner = rect.GetOrigin() + rect.GetSize();
244 AddCubicComponent(
245 {corner.x, corner.y - radii.bottom_right.y},
246 {corner.x, corner.y - radii.bottom_right.y + magic_bottom_right.y},
247 {corner.x - radii.bottom_right.x + magic_bottom_right.x, corner.y},
248 {corner.x - radii.bottom_right.x, corner.y});
249 return *this;
250}
251
252PathBuilder& PathBuilder::AddRoundedRectBottomLeft(Rect rect,
253 RoundingRadii radii) {
254 const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic;
255 const auto corner = rect.GetOrigin() + Point{0, rect.GetHeight()};
256 AddCubicComponent(
257 {corner.x + radii.bottom_left.x, corner.y},
258 {corner.x + radii.bottom_left.x - magic_bottom_left.x, corner.y},
259 {corner.x, corner.y - radii.bottom_left.y + magic_bottom_left.y},
260 {corner.x, corner.y - radii.bottom_left.y});
261 return *this;
262}
263
264void PathBuilder::AddContourComponent(const Point& destination,
265 bool is_closed) {
266 auto& components = prototype_.components;
267 auto& contours = prototype_.contours;
268 if (components.size() > 0 &&
269 components.back().type == Path::ComponentType::kContour) {
270 // Never insert contiguous contours.
271 contours.back() = ContourComponent(destination, is_closed);
272 } else {
273 contours.emplace_back(ContourComponent(destination, is_closed));
274 components.emplace_back(Path::ComponentType::kContour, contours.size() - 1);
275 }
276 prototype_.bounds.reset();
277}
278
279void PathBuilder::AddLinearComponent(const Point& p1, const Point& p2) {
280 auto& points = prototype_.points;
281 auto index = points.size();
282 points.emplace_back(p1);
283 points.emplace_back(p2);
284 prototype_.components.emplace_back(Path::ComponentType::kLinear, index);
285 prototype_.bounds.reset();
286}
287
288void PathBuilder::AddQuadraticComponent(const Point& p1,
289 const Point& cp,
290 const Point& p2) {
291 auto& points = prototype_.points;
292 auto index = points.size();
293 points.emplace_back(p1);
294 points.emplace_back(cp);
295 points.emplace_back(p2);
296 prototype_.components.emplace_back(Path::ComponentType::kQuadratic, index);
297 prototype_.bounds.reset();
298}
299
300void PathBuilder::AddCubicComponent(const Point& p1,
301 const Point& cp1,
302 const Point& cp2,
303 const Point& p2) {
304 auto& points = prototype_.points;
305 auto index = points.size();
306 points.emplace_back(p1);
307 points.emplace_back(cp1);
308 points.emplace_back(cp2);
309 points.emplace_back(p2);
310 prototype_.components.emplace_back(Path::ComponentType::kCubic, index);
311 prototype_.bounds.reset();
312}
313
314void PathBuilder::SetContourClosed(bool is_closed) {
315 prototype_.contours.back().is_closed = is_closed;
316}
317
319 Radians start,
320 Radians sweep,
321 bool use_center) {
322 if (sweep.radians < 0) {
323 start.radians += sweep.radians;
324 sweep.radians *= -1;
325 }
326 sweep.radians = std::min(k2Pi, sweep.radians);
327 start.radians = std::fmod(start.radians, k2Pi);
328
329 const Point center = oval_bounds.GetCenter();
330 const Point radius = center - oval_bounds.GetOrigin();
331
332 Vector2 p1_unit(std::cos(start.radians), std::sin(start.radians));
333
334 if (use_center) {
335 MoveTo(center);
336 LineTo(center + p1_unit * radius);
337 } else {
338 MoveTo(center + p1_unit * radius);
339 }
340
341 while (sweep.radians > 0) {
342 Vector2 p2_unit;
343 Scalar quadrant_angle;
344 if (sweep.radians < kPiOver2) {
345 quadrant_angle = sweep.radians;
346 p2_unit = Vector2(std::cos(start.radians + quadrant_angle),
347 std::sin(start.radians + quadrant_angle));
348 } else {
349 quadrant_angle = kPiOver2;
350 p2_unit = Vector2(-p1_unit.y, p1_unit.x);
351 }
352
353 Vector2 arc_cp_lengths =
354 (quadrant_angle / kPiOver2) * kArcApproximationMagic * radius;
355
356 Point p1 = center + p1_unit * radius;
357 Point p2 = center + p2_unit * radius;
358 Point cp1 = p1 + Vector2(-p1_unit.y, p1_unit.x) * arc_cp_lengths;
359 Point cp2 = p2 + Vector2(p2_unit.y, -p2_unit.x) * arc_cp_lengths;
360
361 AddCubicComponent(p1, cp1, cp2, p2);
362 current_ = p2;
363
364 start.radians += quadrant_angle;
365 sweep.radians -= quadrant_angle;
366 p1_unit = p2_unit;
367 }
368
369 if (use_center) {
370 Close();
371 }
372
373 return *this;
374}
375
377 const Point c = container.GetCenter();
378 const Point r = c - container.GetOrigin();
379 const Point m = r * kArcApproximationMagic;
380
381 MoveTo({c.x, c.y - r.y});
382
383 //----------------------------------------------------------------------------
384 // Top right arc.
385 //
386 AddCubicComponent({c.x, c.y - r.y}, // p1
387 {c.x + m.x, c.y - r.y}, // cp1
388 {c.x + r.x, c.y - m.y}, // cp2
389 {c.x + r.x, c.y} // p2
390 );
391
392 //----------------------------------------------------------------------------
393 // Bottom right arc.
394 //
395 AddCubicComponent({c.x + r.x, c.y}, // p1
396 {c.x + r.x, c.y + m.y}, // cp1
397 {c.x + m.x, c.y + r.y}, // cp2
398 {c.x, c.y + r.y} // p2
399 );
400
401 //----------------------------------------------------------------------------
402 // Bottom left arc.
403 //
404 AddCubicComponent({c.x, c.y + r.y}, // p1
405 {c.x - m.x, c.y + r.y}, // cp1
406 {c.x - r.x, c.y + m.y}, // cp2
407 {c.x - r.x, c.y} // p2
408 );
409
410 //----------------------------------------------------------------------------
411 // Top left arc.
412 //
413 AddCubicComponent({c.x - r.x, c.y}, // p1
414 {c.x - r.x, c.y - m.y}, // cp1
415 {c.x - m.x, c.y - r.y}, // cp2
416 {c.x, c.y - r.y} // p2
417 );
418
419 Close();
420
421 return *this;
422}
423
425 MoveTo(p1);
426 AddLinearComponent(p1, p2);
427 return *this;
428}
429
431 auto linear = [&](size_t index, const LinearPathComponent& l) {
432 AddLinearComponent(l.p1, l.p2);
433 };
434 auto quadratic = [&](size_t index, const QuadraticPathComponent& q) {
435 AddQuadraticComponent(q.p1, q.cp, q.p2);
436 };
437 auto cubic = [&](size_t index, const CubicPathComponent& c) {
438 AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2);
439 };
440 auto move = [&](size_t index, const ContourComponent& m) {
441 AddContourComponent(m.destination);
442 };
443 path.EnumerateComponents(linear, quadratic, cubic, move);
444 return *this;
445}
446
448 for (auto& point : prototype_.points) {
449 point += offset;
450 }
451 for (auto& contour : prototype_.contours) {
452 contour.destination += offset;
453 }
454 prototype_.bounds.reset();
455 return *this;
456}
457
459 prototype_.bounds = bounds;
460 return *this;
461}
462
463void PathBuilder::UpdateBounds() {
464 if (!prototype_.bounds.has_value()) {
465 auto min_max = GetMinMaxCoveragePoints();
466 if (!min_max.has_value()) {
467 prototype_.bounds.reset();
468 return;
469 }
470 auto min = min_max->first;
471 auto max = min_max->second;
472 const auto difference = max - min;
473 prototype_.bounds =
475 }
476}
477
478std::optional<std::pair<Point, Point>> PathBuilder::GetMinMaxCoveragePoints()
479 const {
480 auto& points = prototype_.points;
481
482 if (points.empty()) {
483 return std::nullopt;
484 }
485
486 std::optional<Point> min, max;
487
488 auto clamp = [&min, &max](const Point& point) {
489 if (min.has_value()) {
490 min = min->Min(point);
491 } else {
492 min = point;
493 }
494
495 if (max.has_value()) {
496 max = max->Max(point);
497 } else {
498 max = point;
499 }
500 };
501
502 for (const auto& component : prototype_.components) {
503 switch (component.type) {
505 auto* linear = reinterpret_cast<const LinearPathComponent*>(
506 &points[component.index]);
507 clamp(linear->p1);
508 clamp(linear->p2);
509 break;
510 }
512 for (const auto& extrema :
513 reinterpret_cast<const QuadraticPathComponent*>(
514 &points[component.index])
515 ->Extrema()) {
516 clamp(extrema);
517 }
518 break;
520 for (const auto& extrema : reinterpret_cast<const CubicPathComponent*>(
521 &points[component.index])
522 ->Extrema()) {
523 clamp(extrema);
524 }
525 break;
527 break;
528 }
529 }
530
531 if (!min.has_value() || !max.has_value()) {
532 return std::nullopt;
533 }
534
535 return std::make_pair(min.value(), max.value());
536}
537
538} // namespace impeller
static const int points[]
static unsigned clamp(SkFixed fx, int max)
static size_t difference(size_t minuend, size_t subtrahend)
PathBuilder & AddRect(Rect rect)
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:52
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
void Reserve(size_t point_size, size_t verb_size)
Reserve [point_size] points and [verb_size] verbs in the underlying path buffer.
Definition: path_builder.cc:28
PathBuilder & AddOval(const Rect &rect)
static constexpr const Scalar kArcApproximationMagic
Definition: path_builder.h:23
PathBuilder & AddCircle(const Point &center, Scalar radius)
PathBuilder & Close()
Definition: path_builder.cc:40
PathBuilder & AddPath(const Path &path)
Path CopyPath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:17
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:67
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
PathBuilder & AddQuadraticCurve(Point p1, Point cp, Point p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
PathBuilder & CubicCurveTo(Point controlPoint1, Point controlPoint2, Point point, bool relative=false)
Insert a cubic curve from the curren position to point using the control points controlPoint1 and con...
Definition: path_builder.cc:90
PathBuilder & AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:59
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:85
PathBuilder & QuadraticCurveTo(Point controlPoint, Point point, bool relative=false)
Insert a quadradic curve from the current position to point using the control point controlPoint.
Definition: path_builder.cc:75
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
uint8_t value
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double y
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
CanvasPath Path
Definition: dart_ui.cc:58
constexpr float k2Pi
Definition: constants.h:29
Point Vector2
Definition: point.h:326
float Scalar
Definition: scalar.h:18
FillType
Definition: path.h:30
TRect< Scalar > Rect
Definition: rect.h:769
TPoint< Scalar > Point
Definition: point.h:322
Convexity
Definition: path.h:35
constexpr float kPiOver2
Definition: constants.h:32
AI float quadratic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:156
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:195
SeparatedVector2 offset
Scalar radians
Definition: scalar.h:39
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:373
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
Definition: rect.h:310
Type height
Definition: size.h:23
Type width
Definition: size.h:22
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)