1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include "Vertex.h"
20 
21 #include <utils/Log.h>
22 
23 #include <SkRect.h>
24 #include <algorithm>
25 #include <cmath>
26 #include <iomanip>
27 #include <ostream>
28 
29 namespace android {
30 namespace uirenderer {
31 
32 #define RECT_STRING "%5.2f %5.2f %5.2f %5.2f"
33 #define RECT_ARGS(r) (r).left, (r).top, (r).right, (r).bottom
34 #define SK_RECT_ARGS(r) (r).left(), (r).top(), (r).right(), (r).bottom()
35 
36 ///////////////////////////////////////////////////////////////////////////////
37 // Structs
38 ///////////////////////////////////////////////////////////////////////////////
39 
40 class Rect {
41 public:
42     float left;
43     float top;
44     float right;
45     float bottom;
46 
47     // Used by Region
48     typedef float value_type;
49 
50     // we don't provide copy-ctor and operator= on purpose
51     // because we want the compiler generated versions
52 
Rect()53     inline Rect() : left(0), top(0), right(0), bottom(0) {}
54 
Rect(float left,float top,float right,float bottom)55     inline Rect(float left, float top, float right, float bottom)
56             : left(left), top(top), right(right), bottom(bottom) {}
57 
Rect(float width,float height)58     inline Rect(float width, float height) : left(0.0f), top(0.0f), right(width), bottom(height) {}
59 
Rect(const SkIRect & rect)60     inline Rect(const SkIRect& rect) // NOLINT(google-explicit-constructor)
61             :
62             left(rect.fLeft)
63             , top(rect.fTop)
64             , right(rect.fRight)
65             , bottom(rect.fBottom) {}
66 
Rect(const SkRect & rect)67     inline Rect(const SkRect& rect) // NOLINT(google-explicit-constructor)
68             :
69             left(rect.fLeft)
70             , top(rect.fTop)
71             , right(rect.fRight)
72             , bottom(rect.fBottom) {}
73 
74     friend int operator==(const Rect& a, const Rect& b) { return !memcmp(&a, &b, sizeof(a)); }
75 
76     friend int operator!=(const Rect& a, const Rect& b) { return memcmp(&a, &b, sizeof(a)); }
77 
clear()78     inline void clear() { left = top = right = bottom = 0.0f; }
79 
isEmpty()80     inline bool isEmpty() const {
81         // this is written in such way this it'll handle NANs to return
82         // true (empty)
83         return !((left < right) && (top < bottom));
84     }
85 
setEmpty()86     inline void setEmpty() { left = top = right = bottom = 0.0f; }
87 
set(float left,float top,float right,float bottom)88     inline void set(float left, float top, float right, float bottom) {
89         this->left = left;
90         this->right = right;
91         this->top = top;
92         this->bottom = bottom;
93     }
94 
set(const Rect & r)95     inline void set(const Rect& r) { set(r.left, r.top, r.right, r.bottom); }
96 
set(const SkIRect & r)97     inline void set(const SkIRect& r) { set(r.left(), r.top(), r.right(), r.bottom()); }
98 
getWidth()99     inline float getWidth() const { return right - left; }
100 
getHeight()101     inline float getHeight() const { return bottom - top; }
102 
intersects(float l,float t,float r,float b)103     bool intersects(float l, float t, float r, float b) const {
104         float tempLeft = std::max(left, l);
105         float tempTop = std::max(top, t);
106         float tempRight = std::min(right, r);
107         float tempBottom = std::min(bottom, b);
108 
109         return ((tempLeft < tempRight) && (tempTop < tempBottom));  // !isEmpty
110     }
111 
intersects(const Rect & r)112     bool intersects(const Rect& r) const { return intersects(r.left, r.top, r.right, r.bottom); }
113 
114     /**
115      * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with
116      * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object
117      * if the intersection of the rects would be empty.
118      */
doIntersect(float l,float t,float r,float b)119     void doIntersect(float l, float t, float r, float b) {
120         left = std::max(left, l);
121         top = std::max(top, t);
122         right = std::min(right, r);
123         bottom = std::min(bottom, b);
124     }
125 
doIntersect(const Rect & r)126     void doIntersect(const Rect& r) { doIntersect(r.left, r.top, r.right, r.bottom); }
127 
contains(float l,float t,float r,float b)128     inline bool contains(float l, float t, float r, float b) const {
129         return l >= left && t >= top && r <= right && b <= bottom;
130     }
131 
contains(const Rect & r)132     inline bool contains(const Rect& r) const { return contains(r.left, r.top, r.right, r.bottom); }
133 
unionWith(const Rect & r)134     bool unionWith(const Rect& r) {
135         if (r.left < r.right && r.top < r.bottom) {
136             if (left < right && top < bottom) {
137                 if (left > r.left) left = r.left;
138                 if (top > r.top) top = r.top;
139                 if (right < r.right) right = r.right;
140                 if (bottom < r.bottom) bottom = r.bottom;
141                 return true;
142             } else {
143                 left = r.left;
144                 top = r.top;
145                 right = r.right;
146                 bottom = r.bottom;
147                 return true;
148             }
149         }
150         return false;
151     }
152 
translate(float dx,float dy)153     void translate(float dx, float dy) {
154         left += dx;
155         right += dx;
156         top += dy;
157         bottom += dy;
158     }
159 
inset(float delta)160     void inset(float delta) { outset(-delta); }
161 
outset(float delta)162     void outset(float delta) {
163         left -= delta;
164         top -= delta;
165         right += delta;
166         bottom += delta;
167     }
168 
outset(float xdelta,float ydelta)169     void outset(float xdelta, float ydelta) {
170         left -= xdelta;
171         top -= ydelta;
172         right += xdelta;
173         bottom += ydelta;
174     }
175 
176     /**
177      * Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding
178      * errors.
179      *
180      * This function should be used whenever estimating the damage rect of geometry already mapped
181      * into layer space.
182      */
snapGeometryToPixelBoundaries(bool snapOut)183     void snapGeometryToPixelBoundaries(bool snapOut) {
184         if (snapOut) {
185             /* For AA geometry with a ramp perimeter, don't snap by rounding - AA geometry will have
186              * a 0.5 pixel perimeter not accounted for in its bounds. Instead, snap by
187              * conservatively rounding out the bounds with floor/ceil.
188              *
189              * In order to avoid changing integer bounds with floor/ceil due to rounding errors
190              * inset the bounds first by the fudge factor. Very small fraction-of-a-pixel errors
191              * from this inset will only incur similarly small errors in output, due to transparency
192              * in extreme outside of the geometry.
193              */
194             left = floorf(left + Vertex::GeometryFudgeFactor());
195             top = floorf(top + Vertex::GeometryFudgeFactor());
196             right = ceilf(right - Vertex::GeometryFudgeFactor());
197             bottom = ceilf(bottom - Vertex::GeometryFudgeFactor());
198         } else {
199             /* For other geometry, we do the regular rounding in order to snap, but also outset the
200              * bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect
201              * with top left at (0.5, 0.5)) will err on the side of a larger damage rect.
202              */
203             left = floorf(left + 0.5f - Vertex::GeometryFudgeFactor());
204             top = floorf(top + 0.5f - Vertex::GeometryFudgeFactor());
205             right = floorf(right + 0.5f + Vertex::GeometryFudgeFactor());
206             bottom = floorf(bottom + 0.5f + Vertex::GeometryFudgeFactor());
207         }
208     }
209 
snapToPixelBoundaries()210     void snapToPixelBoundaries() {
211         left = floorf(left + 0.5f);
212         top = floorf(top + 0.5f);
213         right = floorf(right + 0.5f);
214         bottom = floorf(bottom + 0.5f);
215     }
216 
roundOut()217     void roundOut() {
218         left = floorf(left);
219         top = floorf(top);
220         right = ceilf(right);
221         bottom = ceilf(bottom);
222     }
223 
224     /*
225      * Similar to unionWith, except this makes the assumption that both rects are non-empty
226      * to avoid both emptiness checks.
227      */
expandToCover(const Rect & other)228     void expandToCover(const Rect& other) {
229         left = std::min(left, other.left);
230         top = std::min(top, other.top);
231         right = std::max(right, other.right);
232         bottom = std::max(bottom, other.bottom);
233     }
234 
expandToCover(float x,float y)235     void expandToCover(float x, float y) {
236         left = std::min(left, x);
237         top = std::min(top, y);
238         right = std::max(right, x);
239         bottom = std::max(bottom, y);
240     }
241 
toSkRect()242     SkRect toSkRect() const { return SkRect::MakeLTRB(left, top, right, bottom); }
243 
toSkIRect()244     SkIRect toSkIRect() const { return SkIRect::MakeLTRB(left, top, right, bottom); }
245 
246     void dump(const char* label = nullptr) const {
247         ALOGD("%s[l=%.2f t=%.2f r=%.2f b=%.2f]", label ? label : "Rect", left, top, right, bottom);
248     }
249 
250     friend std::ostream& operator<<(std::ostream& os, const Rect& rect) {
251         if (rect.isEmpty()) {
252             // Print empty, but continue, since empty rects may still have useful coordinate info
253             os << "(empty)";
254         }
255 
256         if (rect.left == 0 && rect.top == 0) {
257             return os << "[" << rect.right << " x " << rect.bottom << "]";
258         }
259 
260         return os << "[" << rect.left << " " << rect.top << " " << rect.right << " " << rect.bottom
261                   << "]";
262     }
263 };  // class Rect
264 
265 }  // namespace uirenderer
266 }  // namespace android
267