Flutter Impeller
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 <array>
8 #include <cmath>
9 
12 
13 namespace impeller {
14 
16  AddContourComponent({});
17 }
18 
19 PathBuilder::~PathBuilder() = default;
20 
22  prototype_.fill = fill;
23  prototype_.single_contour =
24  current_contour_location_ == 0u ||
25  (contour_count_ == 2 &&
26  prototype_.components.back() == Path::ComponentType::kContour);
27  return Path(prototype_);
28 }
29 
31  prototype_.fill = fill;
32  UpdateBounds();
33  prototype_.single_contour =
34  current_contour_location_ == 0u ||
35  (contour_count_ == 2 &&
36  prototype_.components.back() == Path::ComponentType::kContour);
37  current_contour_location_ = 0u;
38  contour_count_ = 1;
39  return Path(std::move(prototype_));
40 }
41 
42 void PathBuilder::Reserve(size_t point_size, size_t verb_size) {
43  prototype_.points.reserve(point_size);
44  prototype_.components.reserve(verb_size);
45 }
46 
47 PathBuilder& PathBuilder::MoveTo(Point point, bool relative) {
48  current_ = relative ? current_ + point : point;
49  subpath_start_ = current_;
50  AddContourComponent(current_);
51  return *this;
52 }
53 
55  // If the subpath start is the same as the current position, this
56  // is an empty contour and inserting a line segment will just
57  // confuse the tessellator.
58  if (subpath_start_ != current_) {
59  LineTo(subpath_start_);
60  }
61  SetContourClosed(true);
62  AddContourComponent(current_);
63  return *this;
64 }
65 
66 PathBuilder& PathBuilder::LineTo(Point point, bool relative) {
67  point = relative ? current_ + point : point;
68  AddLinearComponent(current_, point);
69  current_ = point;
70  return *this;
71 }
72 
74  Point endpoint =
75  relative ? Point{current_.x + x, current_.y} : Point{x, current_.y};
76  AddLinearComponent(current_, endpoint);
77  current_ = endpoint;
78  return *this;
79 }
80 
82  Point endpoint =
83  relative ? Point{current_.x, current_.y + y} : Point{current_.x, y};
84  AddLinearComponent(current_, endpoint);
85  current_ = endpoint;
86  return *this;
87 }
88 
90  Point point,
91  bool relative) {
92  point = relative ? current_ + point : point;
93  controlPoint = relative ? current_ + controlPoint : controlPoint;
94  AddQuadraticComponent(current_, controlPoint, point);
95  current_ = point;
96  return *this;
97 }
98 
100  Point point,
101  Scalar weight,
102  bool relative) {
103  point = relative ? current_ + point : point;
104  controlPoint = relative ? current_ + controlPoint : controlPoint;
105  AddConicComponent(current_, controlPoint, point, weight);
106  current_ = point;
107  return *this;
108 }
109 
111  prototype_.convexity = value;
112  return *this;
113 }
114 
116  Point controlPoint2,
117  Point point,
118  bool relative) {
119  controlPoint1 = relative ? current_ + controlPoint1 : controlPoint1;
120  controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2;
121  point = relative ? current_ + point : point;
122  AddCubicComponent(current_, controlPoint1, controlPoint2, point);
123  current_ = point;
124  return *this;
125 }
126 
128  const Point& cp,
129  const Point& p2) {
130  MoveTo(p1);
131  AddQuadraticComponent(p1, cp, p2);
132  return *this;
133 }
134 
136  const Point& cp,
137  const Point& p2,
138  Scalar weight) {
139  MoveTo(p1);
140  AddConicComponent(p1, cp, p2, weight);
141  return *this;
142 }
143 
145  const Point& cp1,
146  const Point& cp2,
147  const Point& p2) {
148  MoveTo(p1);
149  AddCubicComponent(p1, cp1, cp2, p2);
150  return *this;
151 }
152 
154  auto origin = rect.GetOrigin();
155  auto size = rect.GetSize();
156 
157  auto tl = origin;
158  auto bl = origin + Point{0.0, size.height};
159  auto br = origin + size;
160  auto tr = origin + Point{size.width, 0.0};
161 
162  MoveTo(tl);
163  LineTo(tr);
164  LineTo(br);
165  LineTo(bl);
166  Close();
167 
168  return *this;
169 }
170 
172  return AddOval(Rect::MakeXYWH(c.x - r, c.y - r, 2.0f * r, 2.0f * r));
173 }
174 
176  auto rect = round_rect.GetBounds();
177  auto radii = round_rect.GetRadii();
178  if (radii.AreAllCornersEmpty()) {
179  return AddRect(rect);
180  }
181 
182  auto rect_origin = rect.GetOrigin();
183  auto rect_size = rect.GetSize();
184 
185  current_ = rect_origin + Point{radii.top_left.width, 0.0};
186 
187  MoveTo({rect_origin.x + radii.top_left.width, rect_origin.y});
188 
189  //----------------------------------------------------------------------------
190  // Top line.
191  //
192  AddLinearComponentIfNeeded(
193  {rect_origin.x + radii.top_left.width, rect_origin.y},
194  {rect_origin.x + rect_size.width - radii.top_right.width, rect_origin.y});
195 
196  //----------------------------------------------------------------------------
197  // Top right arc.
198  //
199  AddRoundedRectTopRight(rect, radii);
200 
201  //----------------------------------------------------------------------------
202  // Right line.
203  //
204  AddLinearComponentIfNeeded(
205  {rect_origin.x + rect_size.width, rect_origin.y + radii.top_right.height},
206  {rect_origin.x + rect_size.width,
207  rect_origin.y + rect_size.height - radii.bottom_right.height});
208 
209  //----------------------------------------------------------------------------
210  // Bottom right arc.
211  //
212  AddRoundedRectBottomRight(rect, radii);
213 
214  //----------------------------------------------------------------------------
215  // Bottom line.
216  //
217  AddLinearComponentIfNeeded(
218  {rect_origin.x + rect_size.width - radii.bottom_right.width,
219  rect_origin.y + rect_size.height},
220  {rect_origin.x + radii.bottom_left.width,
221  rect_origin.y + rect_size.height});
222 
223  //----------------------------------------------------------------------------
224  // Bottom left arc.
225  //
226  AddRoundedRectBottomLeft(rect, radii);
227 
228  //----------------------------------------------------------------------------
229  // Left line.
230  //
231  AddLinearComponentIfNeeded(
232  {rect_origin.x,
233  rect_origin.y + rect_size.height - radii.bottom_left.height},
234  {rect_origin.x, rect_origin.y + radii.top_left.height});
235 
236  //----------------------------------------------------------------------------
237  // Top left arc.
238  //
239  AddRoundedRectTopLeft(rect, radii);
240 
241  Close();
242 
243  return *this;
244 }
245 
247  if (rse.IsRect()) {
248  AddRect(rse.GetBounds());
249  } else if (rse.IsOval()) {
250  AddOval(rse.GetBounds());
251  } else {
253  rse.GetRadii())
254  .AddToPath(*this);
255  }
256  return *this;
257 }
258 
259 PathBuilder& PathBuilder::AddRoundedRectTopLeft(Rect rect,
260  RoundingRadii radii) {
261  const auto magic_top_left = radii.top_left * kArcApproximationMagic;
262  const auto corner = rect.GetOrigin();
263  AddCubicComponent(
264  {corner.x, corner.y + radii.top_left.height},
265  {corner.x, corner.y + radii.top_left.height - magic_top_left.height},
266  {corner.x + radii.top_left.width - magic_top_left.width, corner.y},
267  {corner.x + radii.top_left.width, corner.y});
268  return *this;
269 }
270 
271 PathBuilder& PathBuilder::AddRoundedRectTopRight(Rect rect,
272  RoundingRadii radii) {
273  const auto magic_top_right = radii.top_right * kArcApproximationMagic;
274  const auto corner = rect.GetOrigin() + Point{rect.GetWidth(), 0};
275  AddCubicComponent(
276  {corner.x - radii.top_right.width, corner.y},
277  {corner.x - radii.top_right.width + magic_top_right.width, corner.y},
278  {corner.x, corner.y + radii.top_right.height - magic_top_right.height},
279  {corner.x, corner.y + radii.top_right.height});
280  return *this;
281 }
282 
283 PathBuilder& PathBuilder::AddRoundedRectBottomRight(Rect rect,
284  RoundingRadii radii) {
285  const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic;
286  const auto corner = rect.GetOrigin() + rect.GetSize();
287  AddCubicComponent(
288  {corner.x, corner.y - radii.bottom_right.height},
289  {corner.x,
290  corner.y - radii.bottom_right.height + magic_bottom_right.height},
291  {corner.x - radii.bottom_right.width + magic_bottom_right.width,
292  corner.y},
293  {corner.x - radii.bottom_right.width, corner.y});
294  return *this;
295 }
296 
297 PathBuilder& PathBuilder::AddRoundedRectBottomLeft(Rect rect,
298  RoundingRadii radii) {
299  const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic;
300  const auto corner = rect.GetOrigin() + Point{0, rect.GetHeight()};
301  AddCubicComponent(
302  {corner.x + radii.bottom_left.width, corner.y},
303  {corner.x + radii.bottom_left.width - magic_bottom_left.width, corner.y},
304  {corner.x,
305  corner.y - radii.bottom_left.height + magic_bottom_left.height},
306  {corner.x, corner.y - radii.bottom_left.height});
307  return *this;
308 }
309 
310 void PathBuilder::AddContourComponent(const Point& destination,
311  bool is_closed) {
312  auto& components = prototype_.components;
313  auto& points = prototype_.points;
314  auto closed = is_closed ? Point{0, 0} : Point{1, 1};
315  if (components.size() > 0 &&
316  components.back() == Path::ComponentType::kContour) {
317  // Never insert contiguous contours.
318  points[current_contour_location_] = destination;
319  points[current_contour_location_ + 1] = closed;
320  } else {
321  current_contour_location_ = points.size();
322  points.push_back(destination);
323  points.push_back(closed);
324  components.push_back(Path::ComponentType::kContour);
325  contour_count_ += 1;
326  }
327  prototype_.bounds.reset();
328 }
329 
330 void PathBuilder::AddLinearComponentIfNeeded(const Point& p1, const Point& p2) {
331  if (ScalarNearlyEqual(p1.x, p2.x, 1e-4f) &&
332  ScalarNearlyEqual(p1.y, p2.y, 1e-4f)) {
333  return;
334  }
335  AddLinearComponent(p1, p2);
336 }
337 
338 void PathBuilder::AddLinearComponent(const Point& p1, const Point& p2) {
339  auto& points = prototype_.points;
340  points.push_back(p1);
341  points.push_back(p2);
342  prototype_.components.push_back(Path::ComponentType::kLinear);
343  prototype_.bounds.reset();
344 }
345 
346 void PathBuilder::AddQuadraticComponent(const Point& p1,
347  const Point& cp,
348  const Point& p2) {
349  auto& points = prototype_.points;
350  points.push_back(p1);
351  points.push_back(cp);
352  points.push_back(p2);
353  prototype_.components.push_back(Path::ComponentType::kQuadratic);
354  prototype_.bounds.reset();
355 }
356 
357 void PathBuilder::AddConicComponent(const Point& p1,
358  const Point& cp,
359  const Point& p2,
360  Scalar weight) {
361  if (!std::isfinite(weight)) {
362  AddLinearComponent(p1, cp);
363  AddLinearComponent(cp, p2);
364  } else if (weight <= 0) {
365  AddLinearComponent(p1, p2);
366  } else if (weight == 1) {
367  AddQuadraticComponent(p1, cp, p2);
368  } else {
369  auto& points = prototype_.points;
370  points.push_back(p1);
371  points.push_back(cp);
372  points.push_back(p2);
373  points.emplace_back(weight, weight);
374  prototype_.components.push_back(Path::ComponentType::kConic);
375  prototype_.bounds.reset();
376  }
377 }
378 
379 void PathBuilder::AddCubicComponent(const Point& p1,
380  const Point& cp1,
381  const Point& cp2,
382  const Point& p2) {
383  auto& points = prototype_.points;
384  points.push_back(p1);
385  points.push_back(cp1);
386  points.push_back(cp2);
387  points.push_back(p2);
388  prototype_.components.push_back(Path::ComponentType::kCubic);
389  prototype_.bounds.reset();
390 }
391 
392 void PathBuilder::SetContourClosed(bool is_closed) {
393  prototype_.points[current_contour_location_ + 1] =
394  is_closed ? Point{0, 0} : Point{1, 1};
395 }
396 
398  Radians start,
399  Radians sweep,
400  bool use_center) {
401  if (sweep.radians < 0) {
402  start.radians += sweep.radians;
403  sweep.radians *= -1;
404  }
405  sweep.radians = std::min(k2Pi, sweep.radians);
406  start.radians = std::fmod(start.radians, k2Pi);
407 
408  const Point center = oval_bounds.GetCenter();
409  const Point radius = center - oval_bounds.GetOrigin();
410 
411  Vector2 p1_unit(std::cos(start.radians), std::sin(start.radians));
412 
413  if (use_center) {
414  MoveTo(center);
415  LineTo(center + p1_unit * radius);
416  } else {
417  MoveTo(center + p1_unit * radius);
418  }
419 
420  while (sweep.radians > 0) {
421  Vector2 p2_unit;
422  Scalar quadrant_angle;
423  if (sweep.radians < kPiOver2) {
424  quadrant_angle = sweep.radians;
425  p2_unit = Vector2(std::cos(start.radians + quadrant_angle),
426  std::sin(start.radians + quadrant_angle));
427  } else {
428  quadrant_angle = kPiOver2;
429  p2_unit = Vector2(-p1_unit.y, p1_unit.x);
430  }
431 
432  Vector2 arc_cp_lengths =
433  (quadrant_angle / kPiOver2) * kArcApproximationMagic * radius;
434 
435  Point p1 = center + p1_unit * radius;
436  Point p2 = center + p2_unit * radius;
437  Point cp1 = p1 + Vector2(-p1_unit.y, p1_unit.x) * arc_cp_lengths;
438  Point cp2 = p2 + Vector2(p2_unit.y, -p2_unit.x) * arc_cp_lengths;
439 
440  AddCubicComponent(p1, cp1, cp2, p2);
441  current_ = p2;
442 
443  start.radians += quadrant_angle;
444  sweep.radians -= quadrant_angle;
445  p1_unit = p2_unit;
446  }
447 
448  if (use_center) {
449  Close();
450  }
451 
452  return *this;
453 }
454 
456  const Point c = container.GetCenter();
457  const Point r = c - container.GetOrigin();
458  const Point m = r * kArcApproximationMagic;
459 
460  MoveTo({c.x, c.y - r.y});
461 
462  //----------------------------------------------------------------------------
463  // Top right arc.
464  //
465  AddCubicComponent({c.x, c.y - r.y}, // p1
466  {c.x + m.x, c.y - r.y}, // cp1
467  {c.x + r.x, c.y - m.y}, // cp2
468  {c.x + r.x, c.y} // p2
469  );
470 
471  //----------------------------------------------------------------------------
472  // Bottom right arc.
473  //
474  AddCubicComponent({c.x + r.x, c.y}, // p1
475  {c.x + r.x, c.y + m.y}, // cp1
476  {c.x + m.x, c.y + r.y}, // cp2
477  {c.x, c.y + r.y} // p2
478  );
479 
480  //----------------------------------------------------------------------------
481  // Bottom left arc.
482  //
483  AddCubicComponent({c.x, c.y + r.y}, // p1
484  {c.x - m.x, c.y + r.y}, // cp1
485  {c.x - r.x, c.y + m.y}, // cp2
486  {c.x - r.x, c.y} // p2
487  );
488 
489  //----------------------------------------------------------------------------
490  // Top left arc.
491  //
492  AddCubicComponent({c.x - r.x, c.y}, // p1
493  {c.x - r.x, c.y - m.y}, // cp1
494  {c.x - m.x, c.y - r.y}, // cp2
495  {c.x, c.y - r.y} // p2
496  );
497 
498  Close();
499 
500  return *this;
501 }
502 
503 PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) {
504  MoveTo(p1);
505  AddLinearComponent(p1, p2);
506  return *this;
507 }
508 
510  auto& points = prototype_.points;
511  auto& components = prototype_.components;
512  size_t source_offset = points.size();
513 
514  points.insert(points.end(), path.data_->points.begin(),
515  path.data_->points.end());
516  components.insert(components.end(), path.data_->components.begin(),
517  path.data_->components.end());
518 
519  for (auto component : path.data_->components) {
520  if (component == Path::ComponentType::kContour) {
521  current_contour_location_ = source_offset;
522  contour_count_ += 1;
523  }
524  source_offset += Path::VerbToOffset(component);
525  }
526  return *this;
527 }
528 
530  auto& points = prototype_.points;
531  size_t storage_offset = 0u;
532  for (const auto& component : prototype_.components) {
533  switch (component) {
535  auto* linear =
536  reinterpret_cast<LinearPathComponent*>(&points[storage_offset]);
537  linear->p1 += offset;
538  linear->p2 += offset;
539  break;
540  }
542  auto* quad =
543  reinterpret_cast<QuadraticPathComponent*>(&points[storage_offset]);
544  quad->p1 += offset;
545  quad->p2 += offset;
546  quad->cp += offset;
547  } break;
549  auto* conic =
550  reinterpret_cast<ConicPathComponent*>(&points[storage_offset]);
551  conic->p1 += offset;
552  conic->p2 += offset;
553  conic->cp += offset;
554  } break;
556  auto* cubic =
557  reinterpret_cast<CubicPathComponent*>(&points[storage_offset]);
558  cubic->p1 += offset;
559  cubic->p2 += offset;
560  cubic->cp1 += offset;
561  cubic->cp2 += offset;
562  } break;
564  auto* contour =
565  reinterpret_cast<ContourComponent*>(&points[storage_offset]);
566  contour->destination += offset;
567  break;
568  }
569  storage_offset += Path::VerbToOffset(component);
570  }
571 
572  prototype_.bounds.reset();
573  return *this;
574 }
575 
577  prototype_.bounds = bounds;
578  return *this;
579 }
580 
581 void PathBuilder::UpdateBounds() {
582  if (!prototype_.bounds.has_value()) {
583  auto min_max = GetMinMaxCoveragePoints();
584  if (!min_max.has_value()) {
585  prototype_.bounds.reset();
586  return;
587  }
588  auto min = min_max->first;
589  auto max = min_max->second;
590  const auto difference = max - min;
591  prototype_.bounds =
592  Rect::MakeXYWH(min.x, min.y, difference.x, difference.y);
593  }
594 }
595 
596 std::optional<std::pair<Point, Point>> PathBuilder::GetMinMaxCoveragePoints()
597  const {
598  auto& points = prototype_.points;
599 
600  if (points.empty()) {
601  return std::nullopt;
602  }
603 
604  std::optional<Point> min, max;
605 
606  auto clamp = [&min, &max](const Point& point) {
607  if (min.has_value()) {
608  min = min->Min(point);
609  } else {
610  min = point;
611  }
612 
613  if (max.has_value()) {
614  max = max->Max(point);
615  } else {
616  max = point;
617  }
618  };
619 
620  size_t storage_offset = 0u;
621  for (const auto& component : prototype_.components) {
622  switch (component) {
624  auto* linear = reinterpret_cast<const LinearPathComponent*>(
625  &points[storage_offset]);
626  clamp(linear->p1);
627  clamp(linear->p2);
628  break;
629  }
631  for (const auto& extrema :
632  reinterpret_cast<const QuadraticPathComponent*>(
633  &points[storage_offset])
634  ->Extrema()) {
635  clamp(extrema);
636  }
637  break;
639  for (const auto& extrema : reinterpret_cast<const ConicPathComponent*>(
640  &points[storage_offset])
641  ->Extrema()) {
642  clamp(extrema);
643  }
644  break;
646  for (const auto& extrema : reinterpret_cast<const CubicPathComponent*>(
647  &points[storage_offset])
648  ->Extrema()) {
649  clamp(extrema);
650  }
651  break;
653  break;
654  }
655  storage_offset += Path::VerbToOffset(component);
656  }
657 
658  if (!min.has_value() || !max.has_value()) {
659  return std::nullopt;
660  }
661 
662  return std::make_pair(min.value(), max.value());
663 }
664 
665 } // namespace impeller
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:30
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
PathBuilder & AddRoundRect(RoundRect rect)
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:66
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:47
PathBuilder & AddRect(const Rect &rect)
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:42
PathBuilder & AddCubicCurve(const Point &p1, const Point &cp1, const Point &cp2, const Point &p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
PathBuilder & AddOval(const Rect &rect)
PathBuilder & AddCircle(const Point &center, Scalar radius)
PathBuilder & Close()
Definition: path_builder.cc:54
PathBuilder & AddPath(const Path &path)
PathBuilder & AddConicCurve(const Point &p1, const Point &cp, const Point &p2, Scalar weight)
Move to point p1, then insert a conic curve from p1 to p2 with the control point cp and weight weight...
Path CopyPath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:81
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.
constexpr static const Scalar kArcApproximationMagic
Definition: path_builder.h:25
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 & ConicCurveTo(Point controlPoint, Point point, Scalar weight, bool relative=false)
Insert a conic curve from the current position to point using the control point controlPoint and the ...
Definition: path_builder.cc:99
PathBuilder & AddQuadraticCurve(const Point &p1, const Point &cp, const Point &p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:73
PathBuilder & AddRoundSuperellipse(RoundSuperellipse rse)
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.
Definition: path_builder.cc:89
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:54
static constexpr size_t VerbToOffset(Path::ComponentType verb)
Definition: path.h:95
int32_t value
int32_t x
constexpr float k2Pi
Definition: constants.h:29
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:18
FillType
Definition: path.h:32
TRect< Scalar > Rect
Definition: rect.h:792
TPoint< Scalar > Point
Definition: point.h:327
Convexity
Definition: path.h:37
constexpr float kPiOver2
Definition: constants.h:32
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:35
SeparatedVector2 offset
Scalar radians
Definition: scalar.h:44
constexpr const RoundingRadii & GetRadii() const
Definition: round_rect.h:53
constexpr const Rect & GetBounds() const
Definition: round_rect.h:52
constexpr const RoundingRadii & GetRadii() const
constexpr bool IsOval() const
constexpr bool IsRect() const
constexpr const Rect & GetBounds() const
static RoundSuperellipseParam MakeBoundsRadii(const Rect &bounds, const RoundingRadii &radii)
void AddToPath(PathBuilder &path) const
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:324
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:331
constexpr static 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:386
Type height
Definition: size.h:29
Type width
Definition: size.h:28