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