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