xref: /aosp_15_r20/external/skia/src/gpu/graphite/geom/Rect.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_geom_Rect_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_geom_Rect_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAttributes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUtils.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkVx.h"
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #define AI SK_ALWAYS_INLINE
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker /**
23*c8dee2aaSAndroid Build Coastguard Worker  * SIMD rect implementation. Values are stored internally in the form: [left, top, -right, -bot].
24*c8dee2aaSAndroid Build Coastguard Worker  *
25*c8dee2aaSAndroid Build Coastguard Worker  * Some operations (e.g., intersect, inset) may return a negative or empty rect
26*c8dee2aaSAndroid Build Coastguard Worker  * (negative meaning, left >= right or top >= bot).
27*c8dee2aaSAndroid Build Coastguard Worker  *
28*c8dee2aaSAndroid Build Coastguard Worker  * Operations on a rect that is either negative or empty, while well-defined, might not give the
29*c8dee2aaSAndroid Build Coastguard Worker  * intended result. It is the caller's responsibility to check isEmptyOrNegative() if needed.
30*c8dee2aaSAndroid Build Coastguard Worker  */
31*c8dee2aaSAndroid Build Coastguard Worker class Rect {
32*c8dee2aaSAndroid Build Coastguard Worker     using float2 = skvx::float2;
33*c8dee2aaSAndroid Build Coastguard Worker     using float4 = skvx::float4;
34*c8dee2aaSAndroid Build Coastguard Worker public:
35*c8dee2aaSAndroid Build Coastguard Worker     AI Rect() = default;
Rect(float l,float t,float r,float b)36*c8dee2aaSAndroid Build Coastguard Worker     AI Rect(float l, float t, float r, float b) : fVals(NegateBotRight({l,t,r,b})) {}
Rect(float2 topLeft,float2 botRight)37*c8dee2aaSAndroid Build Coastguard Worker     AI Rect(float2 topLeft, float2 botRight) : fVals(topLeft, -botRight) {}
Rect(const SkRect & r)38*c8dee2aaSAndroid Build Coastguard Worker     AI Rect(const SkRect& r) : fVals(NegateBotRight(float4::Load(r.asScalars()))) {}
39*c8dee2aaSAndroid Build Coastguard Worker 
LTRB(float4 ltrb)40*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect LTRB(float4 ltrb) {
41*c8dee2aaSAndroid Build Coastguard Worker         return Rect(NegateBotRight(ltrb));
42*c8dee2aaSAndroid Build Coastguard Worker     }
43*c8dee2aaSAndroid Build Coastguard Worker 
XYWH(float x,float y,float w,float h)44*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect XYWH(float x, float y, float w, float h) {
45*c8dee2aaSAndroid Build Coastguard Worker         return Rect(x, y, x + w, y + h);
46*c8dee2aaSAndroid Build Coastguard Worker     }
XYWH(float2 topLeft,float2 size)47*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect XYWH(float2 topLeft, float2 size) {
48*c8dee2aaSAndroid Build Coastguard Worker         return Rect(topLeft, topLeft + size);
49*c8dee2aaSAndroid Build Coastguard Worker     }
WH(float w,float h)50*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect WH(float w, float h) {
51*c8dee2aaSAndroid Build Coastguard Worker         return Rect(0, 0, w, h);
52*c8dee2aaSAndroid Build Coastguard Worker     }
WH(float2 size)53*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect WH(float2 size) {
54*c8dee2aaSAndroid Build Coastguard Worker         return Rect(float2(0), size);
55*c8dee2aaSAndroid Build Coastguard Worker     }
Point(float2 p)56*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect Point(float2 p) {
57*c8dee2aaSAndroid Build Coastguard Worker         return Rect(p, p);
58*c8dee2aaSAndroid Build Coastguard Worker     }
FromVals(float4 vals)59*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect FromVals(float4 vals) {  // vals.zw must already be negated.
60*c8dee2aaSAndroid Build Coastguard Worker         return Rect(vals);
61*c8dee2aaSAndroid Build Coastguard Worker     }
62*c8dee2aaSAndroid Build Coastguard Worker 
63*c8dee2aaSAndroid Build Coastguard Worker     // Constructs a Rect with ltrb = [-inf, -inf, inf, inf], useful for accumulating intersections
Infinite()64*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect Infinite() {
65*c8dee2aaSAndroid Build Coastguard Worker         return FromVals(float4(SK_FloatNegativeInfinity));
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker     // Constructs a negative Rect with ltrb = [inf, inf, -inf, -inf], useful for accumulating unions
InfiniteInverted()68*c8dee2aaSAndroid Build Coastguard Worker     AI static Rect InfiniteInverted() {
69*c8dee2aaSAndroid Build Coastguard Worker         return FromVals(float4(SK_FloatInfinity));
70*c8dee2aaSAndroid Build Coastguard Worker     }
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker     AI bool operator==(Rect rect) const { return all(fVals == rect.fVals); }
73*c8dee2aaSAndroid Build Coastguard Worker     AI bool operator!=(Rect rect) const { return any(fVals != rect.fVals); }
74*c8dee2aaSAndroid Build Coastguard Worker 
75*c8dee2aaSAndroid Build Coastguard Worker     AI bool nearlyEquals(const Rect& r, float epsilon = SK_ScalarNearlyZero) const {
76*c8dee2aaSAndroid Build Coastguard Worker         return all(abs(fVals - r.fVals) <= epsilon);
77*c8dee2aaSAndroid Build Coastguard Worker     }
78*c8dee2aaSAndroid Build Coastguard Worker 
vals()79*c8dee2aaSAndroid Build Coastguard Worker     AI const float4& vals() const { return fVals; }  // [left, top, -right, -bot].
vals()80*c8dee2aaSAndroid Build Coastguard Worker     AI float4& vals() { return fVals; }  // [left, top, -right, -bot].
81*c8dee2aaSAndroid Build Coastguard Worker 
x()82*c8dee2aaSAndroid Build Coastguard Worker     AI float x() const { return fVals.x(); }
y()83*c8dee2aaSAndroid Build Coastguard Worker     AI float y() const { return fVals.y(); }
left()84*c8dee2aaSAndroid Build Coastguard Worker     AI float left() const { return fVals.x(); }
top()85*c8dee2aaSAndroid Build Coastguard Worker     AI float top() const { return fVals.y(); }
right()86*c8dee2aaSAndroid Build Coastguard Worker     AI float right() const { return -fVals.z(); }
bot()87*c8dee2aaSAndroid Build Coastguard Worker     AI float bot() const { return -fVals.w(); }
topLeft()88*c8dee2aaSAndroid Build Coastguard Worker     AI float2 topLeft() const { return fVals.xy(); }
botRight()89*c8dee2aaSAndroid Build Coastguard Worker     AI float2 botRight() const { return -fVals.zw(); }
ltrb()90*c8dee2aaSAndroid Build Coastguard Worker     AI float4 ltrb() const { return NegateBotRight(fVals); }
91*c8dee2aaSAndroid Build Coastguard Worker 
setLeft(float left)92*c8dee2aaSAndroid Build Coastguard Worker     AI void setLeft(float left) { fVals.x() = left; }
setTop(float top)93*c8dee2aaSAndroid Build Coastguard Worker     AI void setTop(float top) { fVals.y() = top; }
setRight(float right)94*c8dee2aaSAndroid Build Coastguard Worker     AI void setRight(float right) { fVals.z() = -right; }
setBot(float bot)95*c8dee2aaSAndroid Build Coastguard Worker     AI void setBot(float bot) { fVals.w() = -bot; }
setTopLeft(float2 topLeft)96*c8dee2aaSAndroid Build Coastguard Worker     AI void setTopLeft(float2 topLeft) { fVals.xy() = topLeft; }
setBotRight(float2 botRight)97*c8dee2aaSAndroid Build Coastguard Worker     AI void setBotRight(float2 botRight) { fVals.zw() = -botRight; }
98*c8dee2aaSAndroid Build Coastguard Worker 
asSkRect()99*c8dee2aaSAndroid Build Coastguard Worker     AI SkRect asSkRect() const {
100*c8dee2aaSAndroid Build Coastguard Worker         SkRect rect;
101*c8dee2aaSAndroid Build Coastguard Worker         this->ltrb().store(&rect);
102*c8dee2aaSAndroid Build Coastguard Worker         return rect;
103*c8dee2aaSAndroid Build Coastguard Worker     }
asSkIRect()104*c8dee2aaSAndroid Build Coastguard Worker     AI SkIRect asSkIRect() const {
105*c8dee2aaSAndroid Build Coastguard Worker         SkIRect rect;
106*c8dee2aaSAndroid Build Coastguard Worker         skvx::cast<int>(this->ltrb()).store(&rect);
107*c8dee2aaSAndroid Build Coastguard Worker         return rect;
108*c8dee2aaSAndroid Build Coastguard Worker     }
109*c8dee2aaSAndroid Build Coastguard Worker 
isEmptyNegativeOrNaN()110*c8dee2aaSAndroid Build Coastguard Worker     AI bool isEmptyNegativeOrNaN() const {
111*c8dee2aaSAndroid Build Coastguard Worker         return !all(fVals.xy() + fVals.zw() < 0);  // !([l-r, r-b] < 0) == ([w, h] <= 0)
112*c8dee2aaSAndroid Build Coastguard Worker                                                    // Use "!(-size < 0)" in order to detect NaN.
113*c8dee2aaSAndroid Build Coastguard Worker     }
114*c8dee2aaSAndroid Build Coastguard Worker 
size()115*c8dee2aaSAndroid Build Coastguard Worker     AI float2 size() const { return -(fVals.xy() + fVals.zw()); }  // == [-(l-r), -(t-b)] == [w, h]
116*c8dee2aaSAndroid Build Coastguard Worker 
center()117*c8dee2aaSAndroid Build Coastguard Worker     AI float2 center() const {
118*c8dee2aaSAndroid Build Coastguard Worker         float4 p = fVals * float4(.5f, .5f, -.5f, -.5f);  // == [l, t, r, b] * .5
119*c8dee2aaSAndroid Build Coastguard Worker         return p.xy() + p.zw();  // == [(l + r)/2, (t + b)/2]
120*c8dee2aaSAndroid Build Coastguard Worker     }
121*c8dee2aaSAndroid Build Coastguard Worker 
area()122*c8dee2aaSAndroid Build Coastguard Worker     AI float area() const {
123*c8dee2aaSAndroid Build Coastguard Worker         float2 negativeSize = fVals.xy() + fVals.zw();  // == [l-r, t-b] == [-w, -h]
124*c8dee2aaSAndroid Build Coastguard Worker         return negativeSize.x() * negativeSize.y();
125*c8dee2aaSAndroid Build Coastguard Worker     }
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker     // A rect stored in a complementary form of: [right, bottom, -left, -top]. Store a local
128*c8dee2aaSAndroid Build Coastguard Worker     // ComplementRect object if intersects() will be called many times.
129*c8dee2aaSAndroid Build Coastguard Worker     struct ComplementRect {
ComplementRectComplementRect130*c8dee2aaSAndroid Build Coastguard Worker         AI ComplementRect(Rect rect) : fVals(-rect.fVals.zwxy()) {}
131*c8dee2aaSAndroid Build Coastguard Worker         float4 fVals;  // [right, bottom, -left, -top]
132*c8dee2aaSAndroid Build Coastguard Worker     };
133*c8dee2aaSAndroid Build Coastguard Worker 
intersects(ComplementRect comp)134*c8dee2aaSAndroid Build Coastguard Worker     AI bool intersects(ComplementRect comp) const { return all(fVals < comp.fVals); }
contains(Rect rect)135*c8dee2aaSAndroid Build Coastguard Worker     AI bool contains(Rect rect) const { return all(fVals <= rect.fVals); }
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker     // Some operations may return a negative or empty rect. Operations on a rect that either is
138*c8dee2aaSAndroid Build Coastguard Worker     // negative or empty, while well-defined, might not give the intended result. It is the caller's
139*c8dee2aaSAndroid Build Coastguard Worker     // responsibility to check isEmptyOrNegative() if needed.
makeRoundIn()140*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeRoundIn() const { return ceil(fVals); }
makeRoundOut()141*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeRoundOut() const { return floor(fVals); }
makeRound()142*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeRound() const {
143*c8dee2aaSAndroid Build Coastguard Worker         // To match SkRect::round(), which is implemented as floor(x+.5), we don't use std::round.
144*c8dee2aaSAndroid Build Coastguard Worker         // But this means we have to undo the negative R and B components before flooring.
145*c8dee2aaSAndroid Build Coastguard Worker         return LTRB(floor(this->ltrb() + 0.5f));
146*c8dee2aaSAndroid Build Coastguard Worker     }
makeInset(float inset)147*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeInset(float inset) const { return fVals + inset; }
makeInset(float2 inset)148*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeInset(float2 inset) const { return fVals + inset.xyxy(); }
makeOutset(float outset)149*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeOutset(float outset) const { return fVals - outset; }
makeOutset(float2 outset)150*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeOutset(float2 outset) const { return fVals - outset.xyxy(); }
makeOffset(float2 offset)151*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeOffset(float2 offset) const { return fVals + float4(offset, -offset); }
makeJoin(Rect rect)152*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeJoin(Rect rect) const { return min(fVals, rect.fVals); }
makeIntersect(Rect rect)153*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeIntersect(Rect rect) const { return max(fVals, rect.fVals); }
makeSorted()154*c8dee2aaSAndroid Build Coastguard Worker     AI Rect makeSorted() const { return min(fVals, -fVals.zwxy()); }
155*c8dee2aaSAndroid Build Coastguard Worker 
roundIn()156*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& roundIn() { return *this = this->makeRoundIn(); }
roundOut()157*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& roundOut() { return *this = this->makeRoundOut(); }
round()158*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& round() { return *this = this->makeRound(); }
inset(float inset)159*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& inset(float inset) { return *this = this->makeInset(inset); }
inset(float2 inset)160*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& inset(float2 inset) { return *this = this->makeInset(inset); }
outset(float outset)161*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& outset(float outset) { return *this = this->makeOutset(outset); }
outset(float2 outset)162*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& outset(float2 outset) { return *this = this->makeOutset(outset); }
offset(float2 offset)163*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& offset(float2 offset) { return *this = this->makeOffset(offset); }
join(Rect rect)164*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& join(Rect rect) { return *this = this->makeJoin(rect); }
intersect(Rect rect)165*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& intersect(Rect rect) { return *this = this->makeIntersect(rect); }
sort()166*c8dee2aaSAndroid Build Coastguard Worker     AI Rect& sort() { return *this = this->makeSorted(); }
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker private:
NegateBotRight(float4 vals)169*c8dee2aaSAndroid Build Coastguard Worker     AI static float4 NegateBotRight(float4 vals) {  // Returns [vals.xy, -vals.zw].
170*c8dee2aaSAndroid Build Coastguard Worker         using uint4 = skvx::uint4;
171*c8dee2aaSAndroid Build Coastguard Worker         return sk_bit_cast<float4>(sk_bit_cast<uint4>(vals) ^ uint4(0, 0, 1u << 31, 1u << 31));
172*c8dee2aaSAndroid Build Coastguard Worker     }
173*c8dee2aaSAndroid Build Coastguard Worker 
Rect(float4 vals)174*c8dee2aaSAndroid Build Coastguard Worker     AI Rect(float4 vals) : fVals(vals) {}  // vals.zw must already be negated.
175*c8dee2aaSAndroid Build Coastguard Worker 
176*c8dee2aaSAndroid Build Coastguard Worker     float4 fVals;  // [left, top, -right, -bottom]
177*c8dee2aaSAndroid Build Coastguard Worker };
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker #undef AI
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_geom_Rect_DEFINED
184