Flutter Engine
The Flutter Engine
rect.h
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#ifndef FLUTTER_IMPELLER_GEOMETRY_RECT_H_
6#define FLUTTER_IMPELLER_GEOMETRY_RECT_H_
7
8#include <array>
9#include <optional>
10#include <ostream>
11#include <vector>
12
13#include "fml/logging.h"
19
20namespace impeller {
21
22#define ONLY_ON_FLOAT_M(Modifiers, Return) \
23 template <typename U = T> \
24 Modifiers std::enable_if_t<std::is_floating_point_v<U>, Return>
25#define ONLY_ON_FLOAT(Return) DL_ONLY_ON_FLOAT_M(, Return)
26
27/// Templated struct for holding an axis-aligned rectangle.
28///
29/// Rectangles are defined as 4 axis-aligned edges that might contain
30/// space. They can be viewed as 2 X coordinates that define the
31/// left and right edges and 2 Y coordinates that define the top and
32/// bottom edges; or they can be viewed as an origin and horizontal
33/// and vertical dimensions (width and height).
34///
35/// When the left and right edges are equal or reversed (right <= left)
36/// or the top and bottom edges are equal or reversed (bottom <= top),
37/// the rectangle is considered empty. Considering the rectangle in XYWH
38/// form, the width and/or the height would be negative or zero. Such
39/// reversed/empty rectangles contain no space and act as such in the
40/// methods that operate on them (Intersection, Union, IntersectsWithRect,
41/// Contains, Cutout, etc.)
42///
43/// Rectangles cannot be modified by any method and a new value can only
44/// be stored into an existing rect using assignment. This keeps the API
45/// clean compared to implementations that might have similar methods
46/// that produce the answer in place, or construct a new object with
47/// the answer, or place the result in an indicated result object.
48///
49/// Methods that might fail to produce an answer will use |std::optional|
50/// to indicate that success or failure (see |Intersection| and |CutOut|).
51/// For convenience, |Intersection| and |Union| both have overloaded
52/// variants that take |std::optional| arguments and treat them as if
53/// the argument was an empty rect to allow chaining multiple such methods
54/// and only needing to check the optional condition of the final result.
55/// The primary methods also provide |...OrEmpty| overloaded variants that
56/// translate an empty optional answer into a simple empty rectangle of the
57/// same type.
58///
59/// Rounding instance methods are not provided as the return value might
60/// be wanted as another floating point rectangle or sometimes as an integer
61/// rectangle. Instead a |RoundOut| factory, defined only for floating point
62/// input rectangles, is provided to provide control over the result type.
63///
64/// NaN and Infinity values
65///
66/// Constructing an LTRB rectangle using Infinity values should work as
67/// expected with either 0 or +Infinity returned as dimensions depending on
68/// which side the Infinity values are on and the sign.
69///
70/// Constructing an XYWH rectangle using Infinity values will usually
71/// not work if the math requires the object to compute a right or bottom
72/// edge from ([xy] -Infinity + [wh] +Infinity). Other combinations might
73/// work.
74///
75/// The special factory |MakeMaximum| is provided to construct a rectangle
76/// of the indicated coordinate type that covers all finite coordinates.
77/// It does not use infinity values, but rather the largest finite values
78/// to avoid math that might produce a NaN value from various getters.
79///
80/// Any rectangle that is constructed with, or computed to have a NaN value
81/// will be considered the same as any empty rectangle.
82///
83/// Empty Rectangle canonical results summary:
84///
85/// Union will ignore any empty rects and return the other rect
86/// Intersection will return nullopt if either rect is empty
87/// IntersectsWithRect will return false if either rect is empty
88/// Cutout will return the source rect if the argument is empty
89/// Cutout will return nullopt if the source rectangle is empty
90/// Contains(Point) will return false if the source rectangle is empty
91/// Contains(Rect) will return false if the source rectangle is empty
92/// Contains(Rect) will otherwise return true if the argument is empty
93/// Specifically, EmptyRect.Contains(EmptyRect) returns false
94///
95/// ---------------
96/// Special notes on problems using the XYWH form of specifying rectangles:
97///
98/// It is possible to have integer rectangles whose dimensions exceed
99/// the maximum number that their coordinates can represent since
100/// (MAX_INT - MIN_INT) overflows the representable positive numbers.
101/// Floating point rectangles technically have a similar issue in that
102/// overflow can occur, but it will be automatically converted into
103/// either an infinity, or a finite-overflow value and still be
104/// representable, just with little to no precision.
105///
106/// Secondly, specifying a rectangle using XYWH leads to cases where the
107/// math for (x+w) and/or (y+h) are also beyond the maximum representable
108/// coordinates. For N-bit integer rectangles declared as XYWH, the
109/// maximum right coordinate will require N+1 signed bits which cannot be
110/// stored in storage that uses N-bit integers.
111///
112/// Saturated math is used when constructing a rectangle from XYWH values
113/// and when returning the dimensions of the rectangle. Constructing an
114/// integer rectangle from values such that xy + wh is beyond the range
115/// of the integer type will place the right or bottom edges at the maximum
116/// value for the integer type. Similarly, constructing an integer rectangle
117/// such that the distance from the left to the right (or top to bottom) is
118/// greater than the range of the integer type will simply return the
119/// maximum integer value as the dimension. Floating point rectangles are
120/// naturally saturated by the rules of IEEE arithmetic.
121template <class T>
122struct TRect {
123 private:
124 using Type = T;
125
126 public:
127 constexpr TRect() : left_(0), top_(0), right_(0), bottom_(0) {}
128
129 constexpr static TRect MakeLTRB(Type left,
130 Type top,
131 Type right,
132 Type bottom) {
133 return TRect(left, top, right, bottom);
134 }
135
136 constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height) {
137 return TRect(x, y, saturated::Add(x, width), saturated::Add(y, height));
138 }
139
140 constexpr static TRect MakeOriginSize(const TPoint<Type>& origin,
141 const TSize<Type>& size) {
142 return MakeXYWH(origin.x, origin.y, size.width, size.height);
143 }
144
145 template <class U>
146 constexpr static TRect MakeSize(const TSize<U>& size) {
147 return TRect(0.0, 0.0, size.width, size.height);
148 }
149
150 template <typename U>
151 constexpr static std::optional<TRect> MakePointBounds(const U& value) {
152 return MakePointBounds(value.begin(), value.end());
153 }
154
155 template <typename PointIter>
156 constexpr static std::optional<TRect> MakePointBounds(const PointIter first,
157 const PointIter last) {
158 if (first == last) {
159 return std::nullopt;
160 }
161 auto left = first->x;
162 auto top = first->y;
163 auto right = first->x;
164 auto bottom = first->y;
165 for (auto it = first + 1; it < last; ++it) {
166 left = std::min(left, it->x);
167 top = std::min(top, it->y);
168 right = std::max(right, it->x);
169 bottom = std::max(bottom, it->y);
170 }
171 return TRect::MakeLTRB(left, top, right, bottom);
172 }
173
174 [[nodiscard]] constexpr static TRect MakeMaximum() {
175 return TRect::MakeLTRB(std::numeric_limits<Type>::lowest(),
176 std::numeric_limits<Type>::lowest(),
179 }
180
181 [[nodiscard]] constexpr bool operator==(const TRect& r) const {
182 return left_ == r.left_ && //
183 top_ == r.top_ && //
184 right_ == r.right_ && //
185 bottom_ == r.bottom_;
186 }
187
188 [[nodiscard]] constexpr bool operator!=(const TRect& r) const {
189 return !(*this == r);
190 }
191
192 [[nodiscard]] constexpr TRect Scale(Type scale) const {
193 return TRect(left_ * scale, //
194 top_ * scale, //
195 right_ * scale, //
196 bottom_ * scale);
197 }
198
199 [[nodiscard]] constexpr TRect Scale(Type scale_x, Type scale_y) const {
200 return TRect(left_ * scale_x, //
201 top_ * scale_y, //
202 right_ * scale_x, //
203 bottom_ * scale_y);
204 }
205
206 [[nodiscard]] constexpr TRect Scale(TPoint<T> scale) const {
207 return Scale(scale.x, scale.y);
208 }
209
210 [[nodiscard]] constexpr TRect Scale(TSize<T> scale) const {
211 return Scale(scale.width, scale.height);
212 }
213
214 /// @brief Returns true iff the provided point |p| is inside the
215 /// half-open interior of this rectangle.
216 ///
217 /// For purposes of containment, a rectangle contains points
218 /// along the top and left edges but not points along the
219 /// right and bottom edges so that a point is only ever
220 /// considered inside one of two abutting rectangles.
221 [[nodiscard]] constexpr bool Contains(const TPoint<Type>& p) const {
222 return !this->IsEmpty() && //
223 p.x >= left_ && //
224 p.y >= top_ && //
225 p.x < right_ && //
226 p.y < bottom_;
227 }
228
229 /// @brief Returns true iff the provided point |p| is inside the
230 /// closed-range interior of this rectangle.
231 ///
232 /// Unlike the regular |Contains(TPoint)| method, this method
233 /// considers all points along the boundary of the rectangle
234 /// to be contained within the rectangle - useful for testing
235 /// if vertices that define a filled shape would carry the
236 /// interior of that shape outside the bounds of the rectangle.
237 /// Since both geometries are defining half-open spaces, their
238 /// defining geometry needs to consider their boundaries to
239 /// be equivalent with respect to interior and exterior.
240 [[nodiscard]] constexpr bool ContainsInclusive(const TPoint<Type>& p) const {
241 return !this->IsEmpty() && //
242 p.x >= left_ && //
243 p.y >= top_ && //
244 p.x <= right_ && //
245 p.y <= bottom_;
246 }
247
248 /// @brief Returns true iff this rectangle is not empty and it also
249 /// contains every point considered inside the provided
250 /// rectangle |o| (as determined by |Contains(TPoint)|).
251 ///
252 /// This is similar to a definition where the result is true iff
253 /// the union of the two rectangles is equal to this rectangle,
254 /// ignoring precision issues with performing those operations
255 /// and assuming that empty rectangles are never equal.
256 ///
257 /// An empty rectangle can contain no other rectangle.
258 ///
259 /// An empty rectangle is, however, contained within any
260 /// other non-empy rectangle as the set of points it contains
261 /// is an empty set and so there are no points to fail the
262 /// containment criteria.
263 [[nodiscard]] constexpr bool Contains(const TRect& o) const {
264 return !this->IsEmpty() && //
265 (o.IsEmpty() || (o.left_ >= left_ && //
266 o.top_ >= top_ && //
267 o.right_ <= right_ && //
268 o.bottom_ <= bottom_));
269 }
270
271 /// @brief Returns true if all of the fields of this floating point
272 /// rectangle are finite.
273 ///
274 /// Note that the results of |GetWidth()| and |GetHeight()| may
275 /// still be infinite due to overflow even if the fields themselves
276 /// are finite.
277 ONLY_ON_FLOAT_M([[nodiscard]] constexpr, bool)
278 IsFinite() const {
279 return std::isfinite(left_) && //
280 std::isfinite(top_) && //
281 std::isfinite(right_) && //
282 std::isfinite(bottom_);
283 }
284
285 /// @brief Returns true if either of the width or height are 0, negative,
286 /// or NaN.
287 [[nodiscard]] constexpr bool IsEmpty() const {
288 // Computing the non-empty condition and negating the result causes any
289 // NaN value to return true - i.e. is considered empty.
290 return !(left_ < right_ && top_ < bottom_);
291 }
292
293 /// @brief Returns true if width and height are equal and neither is NaN.
294 [[nodiscard]] constexpr bool IsSquare() const {
295 // empty rectangles can technically be "square", but would be
296 // misleading to most callers. Using |IsEmpty| also prevents
297 // "non-empty and non-overflowing" computations from happening
298 // to be equal to "empty and overflowing" results.
299 // (Consider LTRB(10, 15, MAX-2, MIN+2) which is empty, but both
300 // w/h subtractions equal "5").
301 return !IsEmpty() && (right_ - left_) == (bottom_ - top_);
302 }
303
304 [[nodiscard]] constexpr bool IsMaximum() const {
305 return *this == MakeMaximum();
306 }
307
308 /// @brief Returns the upper left corner of the rectangle as specified
309 /// by the left/top or x/y values when it was constructed.
310 [[nodiscard]] constexpr TPoint<Type> GetOrigin() const {
311 return {left_, top_};
312 }
313
314 /// @brief Returns the size of the rectangle which may be negative in
315 /// either width or height and may have been clipped to the
316 /// maximum integer values for integer rects whose size overflows.
317 [[nodiscard]] constexpr TSize<Type> GetSize() const {
318 return {GetWidth(), GetHeight()};
319 }
320
321 /// @brief Returns the X coordinate of the upper left corner, equivalent
322 /// to |GetOrigin().x|
323 [[nodiscard]] constexpr Type GetX() const { return left_; }
324
325 /// @brief Returns the Y coordinate of the upper left corner, equivalent
326 /// to |GetOrigin().y|
327 [[nodiscard]] constexpr Type GetY() const { return top_; }
328
329 /// @brief Returns the width of the rectangle, equivalent to
330 /// |GetSize().width|
331 [[nodiscard]] constexpr Type GetWidth() const {
332 return saturated::Sub(right_, left_);
333 }
334
335 /// @brief Returns the height of the rectangle, equivalent to
336 /// |GetSize().height|
337 [[nodiscard]] constexpr Type GetHeight() const {
338 return saturated::Sub(bottom_, top_);
339 }
340
341 [[nodiscard]] constexpr auto GetLeft() const { return left_; }
342
343 [[nodiscard]] constexpr auto GetTop() const { return top_; }
344
345 [[nodiscard]] constexpr auto GetRight() const { return right_; }
346
347 [[nodiscard]] constexpr auto GetBottom() const { return bottom_; }
348
349 [[nodiscard]] constexpr TPoint<T> GetLeftTop() const { //
350 return {left_, top_};
351 }
352
353 [[nodiscard]] constexpr TPoint<T> GetRightTop() const {
354 return {right_, top_};
355 }
356
357 [[nodiscard]] constexpr TPoint<T> GetLeftBottom() const {
358 return {left_, bottom_};
359 }
360
361 [[nodiscard]] constexpr TPoint<T> GetRightBottom() const {
362 return {right_, bottom_};
363 }
364
365 /// @brief Get the area of the rectangle, equivalent to |GetSize().Area()|
366 [[nodiscard]] constexpr T Area() const {
367 // TODO(flutter/flutter#141710) - Use saturated math to avoid overflow
368 // https://github.com/flutter/flutter/issues/141710
369 return IsEmpty() ? 0 : (right_ - left_) * (bottom_ - top_);
370 }
371
372 /// @brief Get the center point as a |Point|.
373 [[nodiscard]] constexpr Point GetCenter() const {
374 return {saturated::AverageScalar(left_, right_),
375 saturated::AverageScalar(top_, bottom_)};
376 }
377
378 [[nodiscard]] constexpr std::array<T, 4> GetLTRB() const {
379 return {left_, top_, right_, bottom_};
380 }
381
382 /// @brief Get the x, y coordinates of the origin and the width and
383 /// height of the rectangle in an array.
384 [[nodiscard]] constexpr std::array<T, 4> GetXYWH() const {
385 return {left_, top_, GetWidth(), GetHeight()};
386 }
387
388 /// @brief Get a version of this rectangle that has a non-negative size.
389 [[nodiscard]] constexpr TRect GetPositive() const {
390 if (!IsEmpty()) {
391 return *this;
392 }
393 return {
394 std::min(left_, right_),
395 std::min(top_, bottom_),
396 std::max(left_, right_),
397 std::max(top_, bottom_),
398 };
399 }
400
401 /// @brief Get the points that represent the 4 corners of this rectangle
402 /// in a Z order that is compatible with triangle strips or a set
403 /// of all zero points if the rectangle is empty.
404 /// The order is: Top left, top right, bottom left, bottom right.
405 [[nodiscard]] constexpr std::array<TPoint<T>, 4> GetPoints() const {
406 if (IsEmpty()) {
407 return {};
408 }
409 return {
410 TPoint{left_, top_},
411 TPoint{right_, top_},
412 TPoint{left_, bottom_},
413 TPoint{right_, bottom_},
414 };
415 }
416
417 [[nodiscard]] constexpr std::array<TPoint<T>, 4> GetTransformedPoints(
418 const Matrix& transform) const {
419 auto points = GetPoints();
420 for (size_t i = 0; i < points.size(); i++) {
421 points[i] = transform * points[i];
422 }
423 return points;
424 }
425
426 /// @brief Creates a new bounding box that contains this transformed
427 /// rectangle, clipped against the near clipping plane if
428 /// necessary.
429 [[nodiscard]] constexpr TRect TransformAndClipBounds(
430 const Matrix& transform) const {
431 if (!transform.HasPerspective2D()) {
433 }
434
435 if (IsEmpty()) {
436 return {};
437 }
438
439 auto ul = transform.TransformHomogenous({left_, top_});
440 auto ur = transform.TransformHomogenous({right_, top_});
441 auto ll = transform.TransformHomogenous({left_, bottom_});
442 auto lr = transform.TransformHomogenous({right_, bottom_});
443
444 // It can probably be proven that we only ever have 5 points at most
445 // which happens when only 1 corner is clipped and we get 2 points
446 // in return for it as we interpolate against its neighbors.
447 Point points[8];
448 int index = 0;
449
450 // Process (clip and interpolate) each point against its 2 neighbors:
451 // left, pt, right
452 index = ClipAndInsert(points, index, ll, ul, ur);
453 index = ClipAndInsert(points, index, ul, ur, lr);
454 index = ClipAndInsert(points, index, ur, lr, ll);
455 index = ClipAndInsert(points, index, lr, ll, ul);
456
458 return bounds.value_or(TRect{});
459 }
460
461 /// @brief Creates a new bounding box that contains this transformed
462 /// rectangle.
463 [[nodiscard]] constexpr TRect TransformBounds(const Matrix& transform) const {
464 if (IsEmpty()) {
465 return {};
466 }
468 auto bounds = TRect::MakePointBounds(points.begin(), points.end());
469 if (bounds.has_value()) {
470 return bounds.value();
471 }
473 }
474
475 /// @brief Constructs a Matrix that will map all points in the coordinate
476 /// space of the rectangle into a new normalized coordinate space
477 /// where the upper left corner of the rectangle maps to (0, 0)
478 /// and the lower right corner of the rectangle maps to (1, 1).
479 ///
480 /// Empty and non-finite rectangles will return a zero-scaling
481 /// transform that maps all points to (0, 0).
482 [[nodiscard]] constexpr Matrix GetNormalizingTransform() const {
483 if (!IsEmpty()) {
484 Scalar sx = 1.0 / GetWidth();
485 Scalar sy = 1.0 / GetHeight();
486 Scalar tx = left_ * -sx;
487 Scalar ty = top_ * -sy;
488
489 // Exclude NaN and infinities and either scale underflowing to zero
490 if (sx != 0.0 && sy != 0.0 && 0.0 * sx * sy * tx * ty == 0.0) {
491 // clang-format off
492 return Matrix( sx, 0.0f, 0.0f, 0.0f,
493 0.0f, sy, 0.0f, 0.0f,
494 0.0f, 0.0f, 1.0f, 0.0f,
495 tx, ty, 0.0f, 1.0f);
496 // clang-format on
497 }
498 }
499
500 // Map all coordinates to the origin.
501 return Matrix::MakeScale({0.0f, 0.0f, 1.0f});
502 }
503
504 [[nodiscard]] constexpr TRect Union(const TRect& o) const {
505 if (IsEmpty()) {
506 return o;
507 }
508 if (o.IsEmpty()) {
509 return *this;
510 }
511 return {
512 std::min(left_, o.left_),
513 std::min(top_, o.top_),
514 std::max(right_, o.right_),
515 std::max(bottom_, o.bottom_),
516 };
517 }
518
519 [[nodiscard]] constexpr std::optional<TRect> Intersection(
520 const TRect& o) const {
521 if (IntersectsWithRect(o)) {
522 return TRect{
523 std::max(left_, o.left_),
524 std::max(top_, o.top_),
525 std::min(right_, o.right_),
526 std::min(bottom_, o.bottom_),
527 };
528 } else {
529 return std::nullopt;
530 }
531 }
532
533 [[nodiscard]] constexpr bool IntersectsWithRect(const TRect& o) const {
534 return !IsEmpty() && //
535 !o.IsEmpty() && //
536 left_ < o.right_ && //
537 top_ < o.bottom_ && //
538 right_ > o.left_ && //
539 bottom_ > o.top_;
540 }
541
542 /// @brief Returns the new boundary rectangle that would result from this
543 /// rectangle being cut out by the specified rectangle.
544 [[nodiscard]] constexpr std::optional<TRect<T>> Cutout(const TRect& o) const {
545 if (IsEmpty()) {
546 // This test isn't just a short-circuit, it also prevents the concise
547 // math below from returning the wrong answer on empty rects.
548 // Once we know that this rectangle is not empty, the math below can
549 // only succeed in computing a value if o is also non-empty and non-nan.
550 // Otherwise, the method returns *this by default.
551 return std::nullopt;
552 }
553
554 const auto& [a_left, a_top, a_right, a_bottom] = GetLTRB(); // Source rect.
555 const auto& [b_left, b_top, b_right, b_bottom] = o.GetLTRB(); // Cutout.
556 if (b_left <= a_left && b_right >= a_right) {
557 if (b_top <= a_top && b_bottom >= a_bottom) {
558 // Full cutout.
559 return std::nullopt;
560 }
561 if (b_top <= a_top && b_bottom > a_top) {
562 // Cuts off the top.
563 return TRect::MakeLTRB(a_left, b_bottom, a_right, a_bottom);
564 }
565 if (b_bottom >= a_bottom && b_top < a_bottom) {
566 // Cuts off the bottom.
567 return TRect::MakeLTRB(a_left, a_top, a_right, b_top);
568 }
569 }
570 if (b_top <= a_top && b_bottom >= a_bottom) {
571 if (b_left <= a_left && b_right > a_left) {
572 // Cuts off the left.
573 return TRect::MakeLTRB(b_right, a_top, a_right, a_bottom);
574 }
575 if (b_right >= a_right && b_left < a_right) {
576 // Cuts off the right.
577 return TRect::MakeLTRB(a_left, a_top, b_left, a_bottom);
578 }
579 }
580
581 return *this;
582 }
583
584 [[nodiscard]] constexpr TRect CutoutOrEmpty(const TRect& o) const {
585 return Cutout(o).value_or(TRect());
586 }
587
588 /// @brief Returns a new rectangle translated by the given offset.
589 [[nodiscard]] constexpr TRect<T> Shift(T dx, T dy) const {
590 return {
591 saturated::Add(left_, dx), //
592 saturated::Add(top_, dy), //
593 saturated::Add(right_, dx), //
594 saturated::Add(bottom_, dy), //
595 };
596 }
597
598 /// @brief Returns a new rectangle translated by the given offset.
599 [[nodiscard]] constexpr TRect<T> Shift(TPoint<T> offset) const {
600 return Shift(offset.x, offset.y);
601 }
602
603 /// @brief Returns a rectangle with expanded edges. Negative expansion
604 /// results in shrinking.
605 [[nodiscard]] constexpr TRect<T> Expand(T left,
606 T top,
607 T right,
608 T bottom) const {
609 return {
610 saturated::Sub(left_, left), //
611 saturated::Sub(top_, top), //
612 saturated::Add(right_, right), //
613 saturated::Add(bottom_, bottom), //
614 };
615 }
616
617 /// @brief Returns a rectangle with expanded edges in all directions.
618 /// Negative expansion results in shrinking.
619 [[nodiscard]] constexpr TRect<T> Expand(T amount) const {
620 return {
621 saturated::Sub(left_, amount), //
622 saturated::Sub(top_, amount), //
623 saturated::Add(right_, amount), //
624 saturated::Add(bottom_, amount), //
625 };
626 }
627
628 /// @brief Returns a rectangle with expanded edges in all directions.
629 /// Negative expansion results in shrinking.
630 [[nodiscard]] constexpr TRect<T> Expand(T horizontal_amount,
631 T vertical_amount) const {
632 return {
633 saturated::Sub(left_, horizontal_amount), //
634 saturated::Sub(top_, vertical_amount), //
635 saturated::Add(right_, horizontal_amount), //
636 saturated::Add(bottom_, vertical_amount), //
637 };
638 }
639
640 /// @brief Returns a rectangle with expanded edges in all directions.
641 /// Negative expansion results in shrinking.
642 [[nodiscard]] constexpr TRect<T> Expand(TPoint<T> amount) const {
643 return Expand(amount.x, amount.y);
644 }
645
646 /// @brief Returns a rectangle with expanded edges in all directions.
647 /// Negative expansion results in shrinking.
648 [[nodiscard]] constexpr TRect<T> Expand(TSize<T> amount) const {
649 return Expand(amount.width, amount.height);
650 }
651
652 /// @brief Returns a new rectangle that represents the projection of the
653 /// source rectangle onto this rectangle. In other words, the source
654 /// rectangle is redefined in terms of the coordinate space of this
655 /// rectangle.
656 [[nodiscard]] constexpr TRect<T> Project(TRect<T> source) const {
657 if (IsEmpty()) {
658 return {};
659 }
660 return source.Shift(-left_, -top_)
661 .Scale(1.0 / static_cast<Scalar>(GetWidth()),
662 1.0 / static_cast<Scalar>(GetHeight()));
663 }
664
665 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
666 RoundOut(const TRect<U>& r) {
667 return TRect::MakeLTRB(saturated::Cast<U, Type>(floor(r.GetLeft())),
668 saturated::Cast<U, Type>(floor(r.GetTop())),
669 saturated::Cast<U, Type>(ceil(r.GetRight())),
670 saturated::Cast<U, Type>(ceil(r.GetBottom())));
671 }
672
673 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
674 Round(const TRect<U>& r) {
675 return TRect::MakeLTRB(saturated::Cast<U, Type>(round(r.GetLeft())),
676 saturated::Cast<U, Type>(round(r.GetTop())),
677 saturated::Cast<U, Type>(round(r.GetRight())),
678 saturated::Cast<U, Type>(round(r.GetBottom())));
679 }
680
681 [[nodiscard]] constexpr static std::optional<TRect> Union(
682 const TRect& a,
683 const std::optional<TRect> b) {
684 return b.has_value() ? a.Union(b.value()) : a;
685 }
686
687 [[nodiscard]] constexpr static std::optional<TRect> Union(
688 const std::optional<TRect> a,
689 const TRect& b) {
690 return a.has_value() ? a->Union(b) : b;
691 }
692
693 [[nodiscard]] constexpr static std::optional<TRect> Union(
694 const std::optional<TRect> a,
695 const std::optional<TRect> b) {
696 return a.has_value() ? Union(a.value(), b) : b;
697 }
698
699 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
700 const TRect& a,
701 const std::optional<TRect> b) {
702 return b.has_value() ? a.Intersection(b.value()) : a;
703 }
704
705 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
706 const std::optional<TRect> a,
707 const TRect& b) {
708 return a.has_value() ? a->Intersection(b) : b;
709 }
710
711 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
712 const std::optional<TRect> a,
713 const std::optional<TRect> b) {
714 return a.has_value() ? Intersection(a.value(), b) : b;
715 }
716
717 private:
718 constexpr TRect(Type left, Type top, Type right, Type bottom)
719 : left_(left), top_(top), right_(right), bottom_(bottom) {}
720
721 Type left_;
722 Type top_;
723 Type right_;
724 Type bottom_;
725
726 static constexpr Scalar kMinimumHomogenous = 1.0f / (1 << 14);
727
728 // Clip p against the near clipping plane (W = kMinimumHomogenous)
729 // and interpolate a crossing point against the nearby neighbors
730 // left and right if p is clipped and either of them is not.
731 // This method can produce 0, 1, or 2 points per call depending on
732 // how many of the points are clipped.
733 // 0 - all points are clipped
734 // 1 - p is unclipped OR
735 // p is clipped and exactly one of the neighbors is not
736 // 2 - p is clipped and both neighbors are not
737 static constexpr int ClipAndInsert(Point clipped[],
738 int index,
739 const Vector3& left,
740 const Vector3& p,
741 const Vector3& right) {
742 if (p.z >= kMinimumHomogenous) {
743 clipped[index++] = {p.x / p.z, p.y / p.z};
744 } else {
745 index = InterpolateAndInsert(clipped, index, p, left);
746 index = InterpolateAndInsert(clipped, index, p, right);
747 }
748 return index;
749 }
750
751 // Interpolate (a clipped) point p against one of its neighbors
752 // and insert the point into the array where the line between them
753 // veers from clipped space to unclipped, if such a point exists.
754 static constexpr int InterpolateAndInsert(Point clipped[],
755 int index,
756 const Vector3& p,
757 const Vector3& neighbor) {
758 if (neighbor.z >= kMinimumHomogenous) {
759 auto t = (kMinimumHomogenous - p.z) / (neighbor.z - p.z);
760 clipped[index++] = {
761 (t * p.x + (1.0f - t) * neighbor.x) / kMinimumHomogenous,
762 (t * p.y + (1.0f - t) * neighbor.y) / kMinimumHomogenous,
763 };
764 }
765 return index;
766 }
767};
768
773
774#undef ONLY_ON_FLOAT
775#undef ONLY_ON_FLOAT_M
776
777} // namespace impeller
778
779namespace std {
780
781template <class T>
782inline std::ostream& operator<<(std::ostream& out,
783 const impeller::TRect<T>& r) {
784 out << "(" << r.GetOrigin() << ", " << r.GetSize() << ")";
785 return out;
786}
787
788} // namespace std
789
790#endif // FLUTTER_IMPELLER_GEOMETRY_RECT_H_
static void round(SkPoint *p)
static const int points[]
SkBitmap source
Definition: examples.cpp:28
static bool b
struct MyStruct a[10]
uint8_t value
#define FML_UNREACHABLE()
Definition: logging.h:109
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
#define ONLY_ON_FLOAT_M(Modifiers, Return)
Definition: rect.h:22
double y
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
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
float Scalar
Definition: scalar.h:18
TPoint< Scalar > Point
Definition: point.h:322
TRect< int64_t > IRect64
Definition: rect.h:771
SK_API sk_sp< PrecompileColorFilter > Matrix()
SINT bool isfinite(const Vec< N, T > &v)
Definition: SkVx.h:1003
SIN Vec< N, float > floor(const Vec< N, float > &x)
Definition: SkVx.h:703
SIN Vec< N, float > ceil(const Vec< N, float > &x)
Definition: SkVx.h:702
Definition: ref_ptr.h:256
std::ostream & operator<<(std::ostream &out, const impeller::Color &c)
Definition: color.h:961
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
#define T
Definition: precompiler.cc:65
int32_t height
int32_t width
const Scalar scale
SeparatedVector2 offset
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
constexpr auto GetBottom() const
Definition: rect.h:347
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition: rect.h:327
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:463
constexpr bool ContainsInclusive(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the closed-range interior of this rectangle.
Definition: rect.h:240
constexpr auto GetTop() const
Definition: rect.h:343
constexpr std::array< TPoint< T >, 4 > GetPoints() const
Get the points that represent the 4 corners of this rectangle in a Z order that is compatible with tr...
Definition: rect.h:405
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:519
constexpr TRect< T > Project(TRect< T > source) const
Returns a new rectangle that represents the projection of the source rectangle onto this rectangle....
Definition: rect.h:656
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:317
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:337
static constexpr std::optional< TRect > Union(const std::optional< TRect > a, const std::optional< TRect > b)
Definition: rect.h:693
constexpr std::optional< TRect< T > > Cutout(const TRect &o) const
Returns the new boundary rectangle that would result from this rectangle being cut out by the specifi...
Definition: rect.h:544
constexpr TRect Scale(TPoint< T > scale) const
Definition: rect.h:206
constexpr bool IsMaximum() const
Definition: rect.h:304
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: rect.h:287
constexpr T Area() const
Get the area of the rectangle, equivalent to |GetSize().Area()|.
Definition: rect.h:366
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition: rect.h:221
static constexpr std::optional< TRect > MakePointBounds(const PointIter first, const PointIter last)
Definition: rect.h:156
constexpr TRect Union(const TRect &o) const
Definition: rect.h:504
constexpr TRect Scale(Type scale_x, Type scale_y) const
Definition: rect.h:199
constexpr bool IntersectsWithRect(const TRect &o) const
Definition: rect.h:533
constexpr auto GetLeft() const
Definition: rect.h:341
constexpr TRect CutoutOrEmpty(const TRect &o) const
Definition: rect.h:584
constexpr TRect< T > Expand(T horizontal_amount, T vertical_amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition: rect.h:630
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:349
static constexpr std::optional< TRect > Union(const TRect &a, const std::optional< TRect > b)
Definition: rect.h:681
Round(const TRect< U > &r)
Definition: rect.h:674
RoundOut(const TRect< U > &r)
Definition: rect.h:666
constexpr TRect GetPositive() const
Get a version of this rectangle that has a non-negative size.
Definition: rect.h:389
static constexpr std::optional< TRect > Intersection(const TRect &a, const std::optional< TRect > b)
Definition: rect.h:699
constexpr TRect Scale(TSize< T > scale) const
Definition: rect.h:210
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition: rect.h:323
constexpr std::array< T, 4 > GetLTRB() const
Definition: rect.h:378
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
constexpr auto GetRight() const
Definition: rect.h:345
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:294
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr bool Contains(const TRect &o) const
Returns true iff this rectangle is not empty and it also contains every point considered inside the p...
Definition: rect.h:263
IsFinite() const
Returns true if all of the fields of this floating point rectangle are finite.
Definition: rect.h:278
constexpr Matrix GetNormalizingTransform() const
Constructs a Matrix that will map all points in the coordinate space of the rectangle into a new norm...
Definition: rect.h:482
constexpr bool operator!=(const TRect &r) const
Definition: rect.h:188
constexpr TRect Scale(Type scale) const
Definition: rect.h:192
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition: rect.h:417
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition: rect.h:429
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:361
static constexpr std::optional< TRect > Intersection(const std::optional< TRect > a, const std::optional< TRect > b)
Definition: rect.h:711
constexpr TPoint< T > GetLeftBottom() const
Definition: rect.h:357
static constexpr TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
static constexpr std::optional< TRect > Union(const std::optional< TRect > a, const TRect &b)
Definition: rect.h:687
constexpr TRect< T > Shift(TPoint< T > offset) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:599
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:331
static constexpr std::optional< TRect > Intersection(const std::optional< TRect > a, const TRect &b)
Definition: rect.h:705
constexpr TPoint< T > GetRightTop() const
Definition: rect.h:353
static constexpr std::optional< TRect > MakePointBounds(const U &value)
Definition: rect.h:151
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition: rect.h:605
static constexpr TRect MakeMaximum()
Definition: rect.h:174
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:373
constexpr TRect()
Definition: rect.h:127
constexpr TRect< T > Expand(TSize< T > amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition: rect.h:648
constexpr bool operator==(const TRect &r) const
Definition: rect.h:181
constexpr TRect< T > Expand(T amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition: rect.h:619
constexpr std::array< T, 4 > GetXYWH() const
Get the x, y coordinates of the origin and the width and height of the rectangle in an array.
Definition: rect.h:384
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:589
constexpr TRect< T > Expand(TPoint< T > amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition: rect.h:642
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
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
Type height
Definition: size.h:23
Type width
Definition: size.h:22