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