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