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