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