Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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)
141 : AddRoundedRect(rect, RoundingRadii(radius));
142}
143
145 return radii.width <= 0 || radii.height <= 0
146 ? AddRect(rect)
147 : AddRoundedRect(rect, RoundingRadii(radii));
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
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)
static SkScalar center(float pos0, float pos1)
PathBuilder & AddRect(Rect rect)
Path TakePath(FillType fill=FillType::kNonZero)
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.
PathBuilder & MoveTo(Point point, bool relative=false)
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.
PathBuilder & AddOval(const Rect &rect)
static constexpr const Scalar kArcApproximationMagic
PathBuilder & AddCircle(const Point &center, Scalar radius)
PathBuilder & Close()
PathBuilder & AddPath(const Path &path)
Path CopyPath(FillType fill=FillType::kNonZero)
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
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...
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)
PathBuilder & SetConvexity(Convexity value)
PathBuilder & QuadraticCurveTo(Point controlPoint, Point point, bool relative=false)
Insert a quadradic curve from the current position to point using the control point controlPoint.
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition path.h:51
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
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
constexpr float k2Pi
Definition constants.h:29
Point Vector2
Definition point.h:320
float Scalar
Definition scalar.h:18
FillType
Definition path.h:29
TRect< Scalar > Rect
Definition rect.h:746
TPoint< Scalar > Point
Definition point.h:316
Convexity
Definition path.h:34
constexpr float kPiOver2
Definition constants.h:32
Point 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:350
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:287
Type height
Definition size.h:23
Type width
Definition size.h:22
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)