Rect.h revision 034a10bf216cdef251928edf72d93668d81515f8
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 <algorithm>
24#include <cmath>
25#include <iomanip>
26#include <ostream>
27#include <SkRect.h>
28
29namespace android {
30namespace uirenderer {
31
32#define RECT_STRING "%5.2f %5.2f %5.2f %5.2f"
33#define RECT_ARGS(r) \
34    (r).left, (r).top, (r).right, (r).bottom
35#define SK_RECT_ARGS(r) \
36    (r).left(), (r).top(), (r).right(), (r).bottom()
37
38///////////////////////////////////////////////////////////////////////////////
39// Structs
40///////////////////////////////////////////////////////////////////////////////
41
42class Rect {
43public:
44    float left;
45    float top;
46    float right;
47    float bottom;
48
49    // Used by Region
50    typedef float value_type;
51
52    // we don't provide copy-ctor and operator= on purpose
53    // because we want the compiler generated versions
54
55    inline Rect():
56            left(0),
57            top(0),
58            right(0),
59            bottom(0) {
60    }
61
62    inline Rect(float left, float top, float right, float bottom):
63            left(left),
64            top(top),
65            right(right),
66            bottom(bottom) {
67    }
68
69    inline Rect(float width, float height):
70            left(0.0f),
71            top(0.0f),
72            right(width),
73            bottom(height) {
74    }
75
76    inline Rect(const SkRect& rect):
77            left(rect.fLeft),
78            top(rect.fTop),
79            right(rect.fRight),
80            bottom(rect.fBottom) {
81    }
82
83    friend int operator==(const Rect& a, const Rect& b) {
84        return !memcmp(&a, &b, sizeof(a));
85    }
86
87    friend int operator!=(const Rect& a, const Rect& b) {
88        return memcmp(&a, &b, sizeof(a));
89    }
90
91    inline void clear() {
92        left = top = right = bottom = 0.0f;
93    }
94
95    inline bool isEmpty() const {
96        // this is written in such way this it'll handle NANs to return
97        // true (empty)
98        return !((left < right) && (top < bottom));
99    }
100
101    inline void setEmpty() {
102        left = top = right = bottom = 0.0f;
103    }
104
105    inline void set(float left, float top, float right, float bottom) {
106        this->left = left;
107        this->right = right;
108        this->top = top;
109        this->bottom = bottom;
110    }
111
112    inline void set(const Rect& r) {
113        set(r.left, r.top, r.right, r.bottom);
114    }
115
116    inline void set(const SkIRect& r) {
117        set(r.left(), r.top(), r.right(), r.bottom());
118    }
119
120    inline float getWidth() const {
121        return right - left;
122    }
123
124    inline float getHeight() const {
125        return bottom - top;
126    }
127
128    bool intersects(float l, float t, float r, float b) const {
129        float tempLeft = std::max(left, l);
130        float tempTop = std::max(top, t);
131        float tempRight = std::min(right, r);
132        float tempBottom = std::min(bottom, b);
133
134        return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty
135    }
136
137    bool intersects(const Rect& r) const {
138        return intersects(r.left, r.top, r.right, r.bottom);
139    }
140
141    /**
142     * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with
143     * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object
144     * if the intersection of the rects would be empty.
145     */
146    void doIntersect(float l, float t, float r, float b) {
147        left = std::max(left, l);
148        top = std::max(top, t);
149        right = std::min(right, r);
150        bottom = std::min(bottom, b);
151    }
152
153    void doIntersect(const Rect& r) {
154        doIntersect(r.left, r.top, r.right, r.bottom);
155    }
156
157    inline bool contains(float l, float t, float r, float b) const {
158        return l >= left && t >= top && r <= right && b <= bottom;
159    }
160
161    inline bool contains(const Rect& r) const {
162        return contains(r.left, r.top, r.right, r.bottom);
163    }
164
165    bool unionWith(const Rect& r) {
166        if (r.left < r.right && r.top < r.bottom) {
167            if (left < right && top < bottom) {
168                if (left > r.left) left = r.left;
169                if (top > r.top) top = r.top;
170                if (right < r.right) right = r.right;
171                if (bottom < r.bottom) bottom = r.bottom;
172                return true;
173            } else {
174                left = r.left;
175                top = r.top;
176                right = r.right;
177                bottom = r.bottom;
178                return true;
179            }
180        }
181        return false;
182    }
183
184    void translate(float dx, float dy) {
185        left += dx;
186        right += dx;
187        top += dy;
188        bottom += dy;
189    }
190
191    void inset(float delta) {
192        outset(-delta);
193    }
194
195    void outset(float delta) {
196        left -= delta;
197        top -= delta;
198        right += delta;
199        bottom += delta;
200    }
201
202    void outset(float xdelta, float ydelta) {
203        left -= xdelta;
204        top -= ydelta;
205        right += xdelta;
206        bottom += ydelta;
207    }
208
209    /**
210     * Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding
211     * errors.
212     *
213     * This function should be used whenever estimating the damage rect of geometry already mapped
214     * into layer space.
215     */
216    void snapGeometryToPixelBoundaries(bool snapOut) {
217        if (snapOut) {
218            /* For AA geometry with a ramp perimeter, don't snap by rounding - AA geometry will have
219             * a 0.5 pixel perimeter not accounted for in its bounds. Instead, snap by
220             * conservatively rounding out the bounds with floor/ceil.
221             *
222             * In order to avoid changing integer bounds with floor/ceil due to rounding errors
223             * inset the bounds first by the fudge factor. Very small fraction-of-a-pixel errors
224             * from this inset will only incur similarly small errors in output, due to transparency
225             * in extreme outside of the geometry.
226             */
227            left = floorf(left + Vertex::GeometryFudgeFactor());
228            top = floorf(top + Vertex::GeometryFudgeFactor());
229            right = ceilf(right - Vertex::GeometryFudgeFactor());
230            bottom = ceilf(bottom - Vertex::GeometryFudgeFactor());
231        } else {
232            /* For other geometry, we do the regular rounding in order to snap, but also outset the
233             * bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect
234             * with top left at (0.5, 0.5)) will err on the side of a larger damage rect.
235             */
236            left = floorf(left + 0.5f - Vertex::GeometryFudgeFactor());
237            top = floorf(top + 0.5f - Vertex::GeometryFudgeFactor());
238            right = floorf(right + 0.5f + Vertex::GeometryFudgeFactor());
239            bottom = floorf(bottom + 0.5f + Vertex::GeometryFudgeFactor());
240        }
241    }
242
243    void snapToPixelBoundaries() {
244        left = floorf(left + 0.5f);
245        top = floorf(top + 0.5f);
246        right = floorf(right + 0.5f);
247        bottom = floorf(bottom + 0.5f);
248    }
249
250    void roundOut() {
251        left = floorf(left);
252        top = floorf(top);
253        right = ceilf(right);
254        bottom = ceilf(bottom);
255    }
256
257    /*
258     * Similar to unionWith, except this makes the assumption that both rects are non-empty
259     * to avoid both emptiness checks.
260     */
261    void expandToCover(const Rect& other) {
262        left = std::min(left, other.left);
263        top = std::min(top, other.top);
264        right = std::max(right, other.right);
265        bottom = std::max(bottom, other.bottom);
266    }
267
268    void expandToCover(float x, float y) {
269        left = std::min(left, x);
270        top = std::min(top, y);
271        right = std::max(right, x);
272        bottom = std::max(bottom, y);
273    }
274
275    SkRect toSkRect() const {
276        return SkRect::MakeLTRB(left, top, right, bottom);
277    }
278
279    SkIRect toSkIRect() const {
280        return SkIRect::MakeLTRB(left, top, right, bottom);
281    }
282
283    void dump(const char* label = nullptr) const {
284        ALOGD("%s[l=%.2f t=%.2f r=%.2f b=%.2f]", label ? label : "Rect", left, top, right, bottom);
285    }
286
287    friend std::ostream& operator<<(std::ostream& os, const Rect& rect) {
288        if (rect.isEmpty()) {
289            // Print empty, but continue, since empty rects may still have useful coordinate info
290            os << "(empty)";
291        }
292
293        if (rect.left == 0 && rect.top == 0) {
294            return os << "[" << rect.right << " x " << rect.bottom << "]";
295        }
296
297        return os << "[" << rect.left
298                << " " << rect.top
299                << " " << rect.right
300                << " " << rect.bottom << "]";
301    }
302}; // class Rect
303
304}; // namespace uirenderer
305}; // namespace android
306
307