1 /*
2  * Copyright (C) 2006 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 package android.graphics;
18 
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 
22 import java.io.PrintWriter;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 
26 /**
27  * Rect holds four integer coordinates for a rectangle. The rectangle is
28  * represented by the coordinates of its 4 edges (left, top, right bottom).
29  * These fields can be accessed directly. Use width() and height() to retrieve
30  * the rectangle's width and height. Note: most methods do not check to see that
31  * the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
32  */
33 public final class Rect implements Parcelable {
34     public int left;
35     public int top;
36     public int right;
37     public int bottom;
38 
39     /**
40      * A helper class for flattened rectange pattern recognition. A separate
41      * class to avoid an initialization dependency on a regular expression
42      * causing Rect to not be initializable with an ahead-of-time compilation
43      * scheme.
44      */
45     private static final class UnflattenHelper {
46         private static final Pattern FLATTENED_PATTERN = Pattern.compile(
47             "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)");
48 
getMatcher(String str)49         static Matcher getMatcher(String str) {
50             return FLATTENED_PATTERN.matcher(str);
51         }
52     }
53 
54     /**
55      * Create a new empty Rect. All coordinates are initialized to 0.
56      */
Rect()57     public Rect() {}
58 
59     /**
60      * Create a new rectangle with the specified coordinates. Note: no range
61      * checking is performed, so the caller must ensure that left <= right and
62      * top <= bottom.
63      *
64      * @param left   The X coordinate of the left side of the rectangle
65      * @param top    The Y coordinate of the top of the rectangle
66      * @param right  The X coordinate of the right side of the rectangle
67      * @param bottom The Y coordinate of the bottom of the rectangle
68      */
Rect(int left, int top, int right, int bottom)69     public Rect(int left, int top, int right, int bottom) {
70         this.left = left;
71         this.top = top;
72         this.right = right;
73         this.bottom = bottom;
74     }
75 
76     /**
77      * Create a new rectangle, initialized with the values in the specified
78      * rectangle (which is left unmodified).
79      *
80      * @param r The rectangle whose coordinates are copied into the new
81      *          rectangle.
82      */
Rect(Rect r)83     public Rect(Rect r) {
84         if (r == null) {
85             left = top = right = bottom = 0;
86         } else {
87             left = r.left;
88             top = r.top;
89             right = r.right;
90             bottom = r.bottom;
91         }
92     }
93 
94     @Override
equals(Object o)95     public boolean equals(Object o) {
96         if (this == o) return true;
97         if (o == null || getClass() != o.getClass()) return false;
98 
99         Rect r = (Rect) o;
100         return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
101     }
102 
103     @Override
hashCode()104     public int hashCode() {
105         int result = left;
106         result = 31 * result + top;
107         result = 31 * result + right;
108         result = 31 * result + bottom;
109         return result;
110     }
111 
112     @Override
toString()113     public String toString() {
114         StringBuilder sb = new StringBuilder(32);
115         sb.append("Rect("); sb.append(left); sb.append(", ");
116         sb.append(top); sb.append(" - "); sb.append(right);
117         sb.append(", "); sb.append(bottom); sb.append(")");
118         return sb.toString();
119     }
120 
121     /**
122      * Return a string representation of the rectangle in a compact form.
123      */
toShortString()124     public String toShortString() {
125         return toShortString(new StringBuilder(32));
126     }
127 
128     /**
129      * Return a string representation of the rectangle in a compact form.
130      * @hide
131      */
toShortString(StringBuilder sb)132     public String toShortString(StringBuilder sb) {
133         sb.setLength(0);
134         sb.append('['); sb.append(left); sb.append(',');
135         sb.append(top); sb.append("]["); sb.append(right);
136         sb.append(','); sb.append(bottom); sb.append(']');
137         return sb.toString();
138     }
139 
140     /**
141      * Return a string representation of the rectangle in a well-defined format.
142      *
143      * <p>You can later recover the Rect from this string through
144      * {@link #unflattenFromString(String)}.
145      *
146      * @return Returns a new String of the form "left top right bottom"
147      */
flattenToString()148     public String flattenToString() {
149         StringBuilder sb = new StringBuilder(32);
150         // WARNING: Do not change the format of this string, it must be
151         // preserved because Rects are saved in this flattened format.
152         sb.append(left);
153         sb.append(' ');
154         sb.append(top);
155         sb.append(' ');
156         sb.append(right);
157         sb.append(' ');
158         sb.append(bottom);
159         return sb.toString();
160     }
161 
162     /**
163      * Returns a Rect from a string of the form returned by {@link #flattenToString},
164      * or null if the string is not of that form.
165      */
unflattenFromString(String str)166     public static Rect unflattenFromString(String str) {
167         Matcher matcher = UnflattenHelper.getMatcher(str);
168         if (!matcher.matches()) {
169             return null;
170         }
171         return new Rect(Integer.parseInt(matcher.group(1)),
172                 Integer.parseInt(matcher.group(2)),
173                 Integer.parseInt(matcher.group(3)),
174                 Integer.parseInt(matcher.group(4)));
175     }
176 
177     /**
178      * Print short representation to given writer.
179      * @hide
180      */
printShortString(PrintWriter pw)181     public void printShortString(PrintWriter pw) {
182         pw.print('['); pw.print(left); pw.print(',');
183         pw.print(top); pw.print("]["); pw.print(right);
184         pw.print(','); pw.print(bottom); pw.print(']');
185     }
186 
187     /**
188      * Returns true if the rectangle is empty (left >= right or top >= bottom)
189      */
isEmpty()190     public final boolean isEmpty() {
191         return left >= right || top >= bottom;
192     }
193 
194     /**
195      * @return the rectangle's width. This does not check for a valid rectangle
196      * (i.e. left <= right) so the result may be negative.
197      */
width()198     public final int width() {
199         return right - left;
200     }
201 
202     /**
203      * @return the rectangle's height. This does not check for a valid rectangle
204      * (i.e. top <= bottom) so the result may be negative.
205      */
height()206     public final int height() {
207         return bottom - top;
208     }
209 
210     /**
211      * @return the horizontal center of the rectangle. If the computed value
212      *         is fractional, this method returns the largest integer that is
213      *         less than the computed value.
214      */
centerX()215     public final int centerX() {
216         return (left + right) >> 1;
217     }
218 
219     /**
220      * @return the vertical center of the rectangle. If the computed value
221      *         is fractional, this method returns the largest integer that is
222      *         less than the computed value.
223      */
centerY()224     public final int centerY() {
225         return (top + bottom) >> 1;
226     }
227 
228     /**
229      * @return the exact horizontal center of the rectangle as a float.
230      */
exactCenterX()231     public final float exactCenterX() {
232         return (left + right) * 0.5f;
233     }
234 
235     /**
236      * @return the exact vertical center of the rectangle as a float.
237      */
exactCenterY()238     public final float exactCenterY() {
239         return (top + bottom) * 0.5f;
240     }
241 
242     /**
243      * Set the rectangle to (0,0,0,0)
244      */
setEmpty()245     public void setEmpty() {
246         left = right = top = bottom = 0;
247     }
248 
249     /**
250      * Set the rectangle's coordinates to the specified values. Note: no range
251      * checking is performed, so it is up to the caller to ensure that
252      * left <= right and top <= bottom.
253      *
254      * @param left   The X coordinate of the left side of the rectangle
255      * @param top    The Y coordinate of the top of the rectangle
256      * @param right  The X coordinate of the right side of the rectangle
257      * @param bottom The Y coordinate of the bottom of the rectangle
258      */
set(int left, int top, int right, int bottom)259     public void set(int left, int top, int right, int bottom) {
260         this.left = left;
261         this.top = top;
262         this.right = right;
263         this.bottom = bottom;
264     }
265 
266     /**
267      * Copy the coordinates from src into this rectangle.
268      *
269      * @param src The rectangle whose coordinates are copied into this
270      *           rectangle.
271      */
set(Rect src)272     public void set(Rect src) {
273         this.left = src.left;
274         this.top = src.top;
275         this.right = src.right;
276         this.bottom = src.bottom;
277     }
278 
279     /**
280      * Offset the rectangle by adding dx to its left and right coordinates, and
281      * adding dy to its top and bottom coordinates.
282      *
283      * @param dx The amount to add to the rectangle's left and right coordinates
284      * @param dy The amount to add to the rectangle's top and bottom coordinates
285      */
offset(int dx, int dy)286     public void offset(int dx, int dy) {
287         left += dx;
288         top += dy;
289         right += dx;
290         bottom += dy;
291     }
292 
293     /**
294      * Offset the rectangle to a specific (left, top) position,
295      * keeping its width and height the same.
296      *
297      * @param newLeft   The new "left" coordinate for the rectangle
298      * @param newTop    The new "top" coordinate for the rectangle
299      */
offsetTo(int newLeft, int newTop)300     public void offsetTo(int newLeft, int newTop) {
301         right += newLeft - left;
302         bottom += newTop - top;
303         left = newLeft;
304         top = newTop;
305     }
306 
307     /**
308      * Inset the rectangle by (dx,dy). If dx is positive, then the sides are
309      * moved inwards, making the rectangle narrower. If dx is negative, then the
310      * sides are moved outwards, making the rectangle wider. The same holds true
311      * for dy and the top and bottom.
312      *
313      * @param dx The amount to add(subtract) from the rectangle's left(right)
314      * @param dy The amount to add(subtract) from the rectangle's top(bottom)
315      */
inset(int dx, int dy)316     public void inset(int dx, int dy) {
317         left += dx;
318         top += dy;
319         right -= dx;
320         bottom -= dy;
321     }
322 
323     /**
324      * Returns true if (x,y) is inside the rectangle. The left and top are
325      * considered to be inside, while the right and bottom are not. This means
326      * that for a x,y to be contained: left <= x < right and top <= y < bottom.
327      * An empty rectangle never contains any point.
328      *
329      * @param x The X coordinate of the point being tested for containment
330      * @param y The Y coordinate of the point being tested for containment
331      * @return true iff (x,y) are contained by the rectangle, where containment
332      *              means left <= x < right and top <= y < bottom
333      */
contains(int x, int y)334     public boolean contains(int x, int y) {
335         return left < right && top < bottom  // check for empty first
336                && x >= left && x < right && y >= top && y < bottom;
337     }
338 
339     /**
340      * Returns true iff the 4 specified sides of a rectangle are inside or equal
341      * to this rectangle. i.e. is this rectangle a superset of the specified
342      * rectangle. An empty rectangle never contains another rectangle.
343      *
344      * @param left The left side of the rectangle being tested for containment
345      * @param top The top of the rectangle being tested for containment
346      * @param right The right side of the rectangle being tested for containment
347      * @param bottom The bottom of the rectangle being tested for containment
348      * @return true iff the the 4 specified sides of a rectangle are inside or
349      *              equal to this rectangle
350      */
contains(int left, int top, int right, int bottom)351     public boolean contains(int left, int top, int right, int bottom) {
352                // check for empty first
353         return this.left < this.right && this.top < this.bottom
354                // now check for containment
355                 && this.left <= left && this.top <= top
356                 && this.right >= right && this.bottom >= bottom;
357     }
358 
359     /**
360      * Returns true iff the specified rectangle r is inside or equal to this
361      * rectangle. An empty rectangle never contains another rectangle.
362      *
363      * @param r The rectangle being tested for containment.
364      * @return true iff the specified rectangle r is inside or equal to this
365      *              rectangle
366      */
contains(Rect r)367     public boolean contains(Rect r) {
368                // check for empty first
369         return this.left < this.right && this.top < this.bottom
370                // now check for containment
371                && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
372     }
373 
374     /**
375      * If the rectangle specified by left,top,right,bottom intersects this
376      * rectangle, return true and set this rectangle to that intersection,
377      * otherwise return false and do not change this rectangle. No check is
378      * performed to see if either rectangle is empty. Note: To just test for
379      * intersection, use {@link #intersects(Rect, Rect)}.
380      *
381      * @param left The left side of the rectangle being intersected with this
382      *             rectangle
383      * @param top The top of the rectangle being intersected with this rectangle
384      * @param right The right side of the rectangle being intersected with this
385      *              rectangle.
386      * @param bottom The bottom of the rectangle being intersected with this
387      *             rectangle.
388      * @return true if the specified rectangle and this rectangle intersect
389      *              (and this rectangle is then set to that intersection) else
390      *              return false and do not change this rectangle.
391      */
intersect(int left, int top, int right, int bottom)392     public boolean intersect(int left, int top, int right, int bottom) {
393         if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
394             if (this.left < left) this.left = left;
395             if (this.top < top) this.top = top;
396             if (this.right > right) this.right = right;
397             if (this.bottom > bottom) this.bottom = bottom;
398             return true;
399         }
400         return false;
401     }
402 
403     /**
404      * If the specified rectangle intersects this rectangle, return true and set
405      * this rectangle to that intersection, otherwise return false and do not
406      * change this rectangle. No check is performed to see if either rectangle
407      * is empty. To just test for intersection, use intersects()
408      *
409      * @param r The rectangle being intersected with this rectangle.
410      * @return true if the specified rectangle and this rectangle intersect
411      *              (and this rectangle is then set to that intersection) else
412      *              return false and do not change this rectangle.
413      */
intersect(Rect r)414     public boolean intersect(Rect r) {
415         return intersect(r.left, r.top, r.right, r.bottom);
416     }
417 
418     /**
419      * If rectangles a and b intersect, return true and set this rectangle to
420      * that intersection, otherwise return false and do not change this
421      * rectangle. No check is performed to see if either rectangle is empty.
422      * To just test for intersection, use intersects()
423      *
424      * @param a The first rectangle being intersected with
425      * @param b The second rectangle being intersected with
426      * @return true iff the two specified rectangles intersect. If they do, set
427      *              this rectangle to that intersection. If they do not, return
428      *              false and do not change this rectangle.
429      */
setIntersect(Rect a, Rect b)430     public boolean setIntersect(Rect a, Rect b) {
431         if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) {
432             left = Math.max(a.left, b.left);
433             top = Math.max(a.top, b.top);
434             right = Math.min(a.right, b.right);
435             bottom = Math.min(a.bottom, b.bottom);
436             return true;
437         }
438         return false;
439     }
440 
441     /**
442      * Returns true if this rectangle intersects the specified rectangle.
443      * In no event is this rectangle modified. No check is performed to see
444      * if either rectangle is empty. To record the intersection, use intersect()
445      * or setIntersect().
446      *
447      * @param left The left side of the rectangle being tested for intersection
448      * @param top The top of the rectangle being tested for intersection
449      * @param right The right side of the rectangle being tested for
450      *              intersection
451      * @param bottom The bottom of the rectangle being tested for intersection
452      * @return true iff the specified rectangle intersects this rectangle. In
453      *              no event is this rectangle modified.
454      */
intersects(int left, int top, int right, int bottom)455     public boolean intersects(int left, int top, int right, int bottom) {
456         return this.left < right && left < this.right && this.top < bottom && top < this.bottom;
457     }
458 
459     /**
460      * Returns true iff the two specified rectangles intersect. In no event are
461      * either of the rectangles modified. To record the intersection,
462      * use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}.
463      *
464      * @param a The first rectangle being tested for intersection
465      * @param b The second rectangle being tested for intersection
466      * @return true iff the two specified rectangles intersect. In no event are
467      *              either of the rectangles modified.
468      */
intersects(Rect a, Rect b)469     public static boolean intersects(Rect a, Rect b) {
470         return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
471     }
472 
473     /**
474      * Update this Rect to enclose itself and the specified rectangle. If the
475      * specified rectangle is empty, nothing is done. If this rectangle is empty
476      * it is set to the specified rectangle.
477      *
478      * @param left The left edge being unioned with this rectangle
479      * @param top The top edge being unioned with this rectangle
480      * @param right The right edge being unioned with this rectangle
481      * @param bottom The bottom edge being unioned with this rectangle
482      */
union(int left, int top, int right, int bottom)483     public void union(int left, int top, int right, int bottom) {
484         if ((left < right) && (top < bottom)) {
485             if ((this.left < this.right) && (this.top < this.bottom)) {
486                 if (this.left > left) this.left = left;
487                 if (this.top > top) this.top = top;
488                 if (this.right < right) this.right = right;
489                 if (this.bottom < bottom) this.bottom = bottom;
490             } else {
491                 this.left = left;
492                 this.top = top;
493                 this.right = right;
494                 this.bottom = bottom;
495             }
496         }
497     }
498 
499     /**
500      * Update this Rect to enclose itself and the specified rectangle. If the
501      * specified rectangle is empty, nothing is done. If this rectangle is empty
502      * it is set to the specified rectangle.
503      *
504      * @param r The rectangle being unioned with this rectangle
505      */
union(Rect r)506     public void union(Rect r) {
507         union(r.left, r.top, r.right, r.bottom);
508     }
509 
510     /**
511      * Update this Rect to enclose itself and the [x,y] coordinate. There is no
512      * check to see that this rectangle is non-empty.
513      *
514      * @param x The x coordinate of the point to add to the rectangle
515      * @param y The y coordinate of the point to add to the rectangle
516      */
union(int x, int y)517     public void union(int x, int y) {
518         if (x < left) {
519             left = x;
520         } else if (x > right) {
521             right = x;
522         }
523         if (y < top) {
524             top = y;
525         } else if (y > bottom) {
526             bottom = y;
527         }
528     }
529 
530     /**
531      * Swap top/bottom or left/right if there are flipped (i.e. left > right
532      * and/or top > bottom). This can be called if
533      * the edges are computed separately, and may have crossed over each other.
534      * If the edges are already correct (i.e. left <= right and top <= bottom)
535      * then nothing is done.
536      */
sort()537     public void sort() {
538         if (left > right) {
539             int temp = left;
540             left = right;
541             right = temp;
542         }
543         if (top > bottom) {
544             int temp = top;
545             top = bottom;
546             bottom = temp;
547         }
548     }
549 
550     /**
551      * Parcelable interface methods
552      */
describeContents()553     public int describeContents() {
554         return 0;
555     }
556 
557     /**
558      * Write this rectangle to the specified parcel. To restore a rectangle from
559      * a parcel, use readFromParcel()
560      * @param out The parcel to write the rectangle's coordinates into
561      */
writeToParcel(Parcel out, int flags)562     public void writeToParcel(Parcel out, int flags) {
563         out.writeInt(left);
564         out.writeInt(top);
565         out.writeInt(right);
566         out.writeInt(bottom);
567     }
568 
569     public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
570         /**
571          * Return a new rectangle from the data in the specified parcel.
572          */
573         public Rect createFromParcel(Parcel in) {
574             Rect r = new Rect();
575             r.readFromParcel(in);
576             return r;
577         }
578 
579         /**
580          * Return an array of rectangles of the specified size.
581          */
582         public Rect[] newArray(int size) {
583             return new Rect[size];
584         }
585     };
586 
587     /**
588      * Set the rectangle's coordinates from the data stored in the specified
589      * parcel. To write a rectangle to a parcel, call writeToParcel().
590      *
591      * @param in The parcel to read the rectangle's coordinates from
592      */
readFromParcel(Parcel in)593     public void readFromParcel(Parcel in) {
594         left = in.readInt();
595         top = in.readInt();
596         right = in.readInt();
597         bottom = in.readInt();
598     }
599 
600     /**
601      * Scales up the rect by the given scale.
602      * @hide
603      */
scale(float scale)604     public void scale(float scale) {
605         if (scale != 1.0f) {
606             left = (int) (left * scale + 0.5f);
607             top = (int) (top * scale + 0.5f);
608             right = (int) (right * scale + 0.5f);
609             bottom = (int) (bottom * scale + 0.5f);
610         }
611     }
612 
613     /**
614      * Scales up the rect by the given scale, rounding values toward the inside.
615      * @hide
616      */
scaleRoundIn(float scale)617     public void scaleRoundIn(float scale) {
618         if (scale != 1.0f) {
619             left = (int) Math.ceil(left * scale);
620             top = (int) Math.ceil(top * scale);
621             right = (int) Math.floor(right * scale);
622             bottom = (int) Math.floor(bottom * scale);
623         }
624     }
625 }
626