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