1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 /** 18 * @author Denis M. Kishenko 19 * @version $Revision$ 20 */ 21 package org.apache.harmony.awt.gl; 22 23 import java.awt.Rectangle; 24 import java.awt.Shape; 25 import java.awt.geom.AffineTransform; 26 import java.awt.geom.PathIterator; 27 import java.awt.geom.Point2D; 28 import java.awt.geom.Rectangle2D; 29 import java.util.ArrayList; 30 import java.util.NoSuchElementException; 31 32 import org.apache.harmony.awt.internal.nls.Messages; 33 34 public class MultiRectArea implements Shape { 35 36 /** 37 * If CHECK is true validation check active 38 */ 39 private static final boolean CHECK = false; 40 41 boolean sorted = true; 42 43 /** 44 * Rectangle buffer 45 */ 46 public int[] rect; 47 48 /** 49 * Bounding box 50 */ 51 Rectangle bounds; 52 53 /** 54 * Result rectangle array 55 */ 56 Rectangle[] rectangles; 57 58 /** 59 * LineCash provides creating MultiRectArea line by line. Used in JavaShapeRasterizer. 60 */ 61 public static class LineCash extends MultiRectArea { 62 63 int lineY; 64 int bottomCount; 65 int[] bottom; 66 67 public LineCash(int size) { 68 super(); 69 bottom = new int[size]; 70 bottomCount = 0; 71 } 72 73 public void setLine(int y) { 74 lineY = y; 75 } 76 77 public void skipLine() { 78 lineY++; 79 bottomCount = 0; 80 } 81 82 public void addLine(int[] points, int pointCount) { 83 int bottomIndex = 0; 84 int pointIndex = 0; 85 int rectIndex = 0; 86 int pointX1 = 0; 87 int pointX2 = 0; 88 int bottomX1 = 0; 89 int bottomX2 = 0; 90 boolean appendRect = false; 91 boolean deleteRect = false; 92 int lastCount = bottomCount; 93 94 while (bottomIndex < lastCount || pointIndex < pointCount) { 95 96 appendRect = false; 97 deleteRect = false; 98 99 if (bottomIndex < lastCount) { 100 rectIndex = bottom[bottomIndex]; 101 bottomX1 = rect[rectIndex]; 102 bottomX2 = rect[rectIndex + 2]; 103 } else { 104 appendRect = true; 105 } 106 107 if (pointIndex < pointCount) { 108 pointX1 = points[pointIndex]; 109 pointX2 = points[pointIndex + 1]; 110 } else { 111 deleteRect = true; 112 } 113 114 if (!deleteRect && !appendRect) { 115 if (pointX1 == bottomX1 && pointX2 == bottomX2) { 116 rect[rectIndex + 3] = rect[rectIndex + 3] + 1; 117 pointIndex += 2; 118 bottomIndex++; 119 continue; 120 } 121 deleteRect = pointX2 >= bottomX1; 122 appendRect = pointX1 <= bottomX2; 123 } 124 125 if (deleteRect) { 126 if (bottomIndex < bottomCount - 1) { 127 System.arraycopy(bottom, bottomIndex + 1, bottom, bottomIndex, bottomCount - bottomIndex - 1); 128 rectIndex -= 4; 129 } 130 bottomCount--; 131 lastCount--; 132 } 133 134 if (appendRect) { 135 int i = rect[0]; 136 bottom[bottomCount++] = i; 137 rect = MultiRectAreaOp.checkBufSize(rect, 4); 138 rect[i++] = pointX1; 139 rect[i++] = lineY; 140 rect[i++] = pointX2; 141 rect[i++] = lineY; 142 pointIndex += 2; 143 } 144 } 145 lineY++; 146 147 invalidate(); 148 } 149 150 } 151 152 /** 153 * RectCash provides simple creating MultiRectArea 154 */ 155 public static class RectCash extends MultiRectArea { 156 157 int[] cash; 158 159 public RectCash() { 160 super(); 161 cash = new int[MultiRectAreaOp.RECT_CAPACITY]; 162 cash[0] = 1; 163 } 164 165 public void addRectCashed(int x1, int y1, int x2, int y2) { 166 addRect(x1, y1, x2, y2); 167 invalidate(); 168 /* 169 // Exclude from cash unnecessary rectangles 170 int i = 1; 171 while(i < cash[0]) { 172 if (rect[cash[i] + 3] >= y1 - 1) { 173 if (i > 1) { 174 System.arraycopy(cash, i, cash, 1, cash[0] - i); 175 } 176 break; 177 } 178 i++; 179 } 180 cash[0] -= i - 1; 181 182 // Find in cash rectangle to concatinate 183 i = 1; 184 while(i < cash[0]) { 185 int index = cash[i]; 186 if (rect[index + 3] != y1 - 1) { 187 break; 188 } 189 if (rect[index] == x1 && rect[index + 2] == x2) { 190 rect[index + 3] += y2 - y1 + 1; 191 192 int pos = i + 1; 193 while(pos < cash[0]) { 194 if (rect[index + 3] <= rect[cash[i] + 3]) { 195 System.arraycopy(cash, i + 1, cash, i, pos - i); 196 break; 197 } 198 i++; 199 } 200 cash[pos - 1] = index; 201 202 invalidate(); 203 return; 204 } 205 i++; 206 } 207 208 // Add rectangle to buffer 209 int index = rect[0]; 210 rect = MultiRectAreaOp.checkBufSize(rect, 4); 211 rect[index + 0] = x1; 212 rect[index + 1] = y1; 213 rect[index + 2] = x2; 214 rect[index + 3] = y2; 215 216 // Add rectangle to cash 217 int length = cash[0]; 218 cash = MultiRectAreaOp.checkBufSize(cash, 1); 219 while(i < length) { 220 if (y2 <= rect[cash[i] + 3]) { 221 System.arraycopy(cash, i, cash, i + 1, length - i); 222 break; 223 } 224 i++; 225 } 226 cash[i] = index; 227 invalidate(); 228 */ 229 } 230 231 public void addRectCashed(int[] rect, int rectOff, int rectLength) { 232 for(int i = rectOff; i < rectOff + rectLength;) { 233 addRect(rect[i++], rect[i++], rect[i++], rect[i++]); 234 // addRectCashed(rect[i++], rect[i++], rect[i++], rect[i++]); 235 } 236 } 237 238 } 239 240 /** 241 * MultiRectArea path iterator 242 */ 243 class Iterator implements PathIterator { 244 245 int type; 246 int index; 247 int pos; 248 249 int[] rect; 250 AffineTransform t; 251 252 Iterator(MultiRectArea mra, AffineTransform t) { 253 rect = new int[mra.rect[0] - 1]; 254 System.arraycopy(mra.rect, 1, rect, 0, rect.length); 255 this.t = t; 256 } 257 258 public int getWindingRule() { 259 return WIND_NON_ZERO; 260 } 261 262 public boolean isDone() { 263 return pos >= rect.length; 264 } 265 266 public void next() { 267 if (index == 4) { 268 pos += 4; 269 } 270 index = (index + 1) % 5; 271 } 272 273 public int currentSegment(double[] coords) { 274 if (isDone()) { 275 // awt.4B=Iiterator out of bounds 276 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ 277 } 278 int type = 0; 279 280 switch(index) { 281 case 0 : 282 type = SEG_MOVETO; 283 coords[0] = rect[pos + 0]; 284 coords[1] = rect[pos + 1]; 285 break; 286 case 1: 287 type = SEG_LINETO; 288 coords[0] = rect[pos + 2]; 289 coords[1] = rect[pos + 1]; 290 break; 291 case 2: 292 type = SEG_LINETO; 293 coords[0] = rect[pos + 2]; 294 coords[1] = rect[pos + 3]; 295 break; 296 case 3: 297 type = SEG_LINETO; 298 coords[0] = rect[pos + 0]; 299 coords[1] = rect[pos + 3]; 300 break; 301 case 4: 302 type = SEG_CLOSE; 303 break; 304 } 305 306 if (t != null) { 307 t.transform(coords, 0, coords, 0, 1); 308 } 309 return type; 310 } 311 312 public int currentSegment(float[] coords) { 313 if (isDone()) { 314 // awt.4B=Iiterator out of bounds 315 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ 316 } 317 int type = 0; 318 319 switch(index) { 320 case 0 : 321 type = SEG_MOVETO; 322 coords[0] = rect[pos + 0]; 323 coords[1] = rect[pos + 1]; 324 break; 325 case 1: 326 type = SEG_LINETO; 327 coords[0] = rect[pos + 2]; 328 coords[1] = rect[pos + 1]; 329 break; 330 case 2: 331 type = SEG_LINETO; 332 coords[0] = rect[pos + 2]; 333 coords[1] = rect[pos + 3]; 334 break; 335 case 3: 336 type = SEG_LINETO; 337 coords[0] = rect[pos + 0]; 338 coords[1] = rect[pos + 3]; 339 break; 340 case 4: 341 type = SEG_CLOSE; 342 break; 343 } 344 345 if (t != null) { 346 t.transform(coords, 0, coords, 0, 1); 347 } 348 return type; 349 } 350 351 } 352 353 /** 354 * Constructs a new empty MultiRectArea 355 */ 356 public MultiRectArea() { 357 rect = MultiRectAreaOp.createBuf(0); 358 } 359 360 public MultiRectArea(boolean sorted) { 361 this(); 362 this.sorted = sorted; 363 } 364 365 /** 366 * Constructs a new MultiRectArea as a copy of another one 367 */ 368 public MultiRectArea(MultiRectArea mra) { 369 if (mra == null) { 370 rect = MultiRectAreaOp.createBuf(0); 371 } else { 372 rect = new int[mra.rect.length]; 373 System.arraycopy(mra.rect, 0, rect, 0, mra.rect.length); 374 check(this, "MultiRectArea(MRA)"); //$NON-NLS-1$ 375 } 376 } 377 378 /** 379 * Constructs a new MultiRectArea consists of single rectangle 380 */ 381 public MultiRectArea(Rectangle r) { 382 rect = MultiRectAreaOp.createBuf(0); 383 if (r != null && !r.isEmpty()) { 384 rect[0] = 5; 385 rect[1] = r.x; 386 rect[2] = r.y; 387 rect[3] = r.x + r.width - 1; 388 rect[4] = r.y + r.height - 1; 389 } 390 check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$ 391 } 392 393 /** 394 * Constructs a new MultiRectArea consists of single rectangle 395 */ 396 public MultiRectArea(int x0, int y0, int x1, int y1) { 397 rect = MultiRectAreaOp.createBuf(0); 398 if (x1 >= x0 && y1 >= y0) { 399 rect[0] = 5; 400 rect[1] = x0; 401 rect[2] = y0; 402 rect[3] = x1; 403 rect[4] = y1; 404 } 405 check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$ 406 } 407 408 /** 409 * Constructs a new MultiRectArea and append rectangle from buffer 410 */ 411 public MultiRectArea(Rectangle[] buf) { 412 this(); 413 for (Rectangle element : buf) { 414 add(element); 415 } 416 } 417 418 /** 419 * Constructs a new MultiRectArea and append rectangle from array 420 */ 421 public MultiRectArea(ArrayList<Rectangle> buf) { 422 this(); 423 for(int i = 0; i < buf.size(); i++) { 424 add(buf.get(i)); 425 } 426 } 427 428 /** 429 * Sort rectangle buffer 430 */ 431 void resort() { 432 int[] buf = new int[4]; 433 for(int i = 1; i < rect[0]; i += 4) { 434 int k = i; 435 int x1 = rect[k]; 436 int y1 = rect[k + 1]; 437 for(int j = i + 4; j < rect[0]; j += 4) { 438 int x2 = rect[j]; 439 int y2 = rect[j + 1]; 440 if (y1 > y2 || (y1 == y2 && x1 > x2)) { 441 x1 = x2; 442 y1 = y2; 443 k = j; 444 } 445 } 446 if (k != i) { 447 System.arraycopy(rect, i, buf, 0, 4); 448 System.arraycopy(rect, k, rect, i, 4); 449 System.arraycopy(buf, 0, rect, k, 4); 450 } 451 } 452 invalidate(); 453 } 454 455 /** 456 * Tests equals with another object 457 */ 458 @Override 459 public boolean equals(Object obj) { 460 if (obj == this) { 461 return true; 462 } 463 if (obj instanceof MultiRectArea) { 464 MultiRectArea mra = (MultiRectArea) obj; 465 for(int i = 0; i < rect[0]; i++) { 466 if (rect[i] != mra.rect[i]) { 467 return false; 468 } 469 } 470 return true; 471 } 472 return false; 473 } 474 475 /** 476 * Checks validation of MultiRectArea object 477 */ 478 static MultiRectArea check(MultiRectArea mra, String msg) { 479 if (CHECK && mra != null) { 480 if (MultiRectArea.checkValidation(mra.getRectangles(), mra.sorted) != -1) { 481 // awt.4C=Invalid MultiRectArea in method {0} 482 new RuntimeException(Messages.getString("awt.4C", msg)); //$NON-NLS-1$ 483 } 484 } 485 return mra; 486 } 487 488 /** 489 * Checks validation of MultiRectArea object 490 */ 491 public static int checkValidation(Rectangle[] r, boolean sorted) { 492 493 // Check width and height 494 for(int i = 0; i < r.length; i++) { 495 if (r[i].width <= 0 || r[i].height <= 0) { 496 return i; 497 } 498 } 499 500 // Check order 501 if (sorted) { 502 for(int i = 1; i < r.length; i++) { 503 if (r[i - 1].y > r[i].y) { 504 return i; 505 } 506 if (r[i - 1].y == r[i].y) { 507 if (r[i - 1].x > r[i].x) { 508 return i; 509 } 510 } 511 } 512 } 513 514 // Check override 515 for(int i = 0; i < r.length; i++) { 516 for(int j = i + 1; j < r.length; j++) { 517 if (r[i].intersects(r[j])) { 518 return i; 519 } 520 } 521 } 522 523 return -1; 524 } 525 526 /** 527 * Assigns rectangle from another buffer 528 */ 529 protected void setRect(int[] buf, boolean copy) { 530 if (copy) { 531 rect = new int[buf.length]; 532 System.arraycopy(buf, 0, rect, 0, buf.length); 533 } else { 534 rect = buf; 535 } 536 invalidate(); 537 } 538 539 /** 540 * Union with another MultiRectArea object 541 */ 542 public void add(MultiRectArea mra) { 543 setRect(union(this, mra).rect, false); 544 invalidate(); 545 } 546 547 /** 548 * Intersect with another MultiRectArea object 549 */ 550 public void intersect(MultiRectArea mra) { 551 setRect(intersect(this, mra).rect, false); 552 invalidate(); 553 } 554 555 /** 556 * Subtract another MultiRectArea object 557 */ 558 public void substract(MultiRectArea mra) { 559 setRect(subtract(this, mra).rect, false); 560 invalidate(); 561 } 562 563 /** 564 * Union with Rectangle object 565 */ 566 public void add(Rectangle rect) { 567 setRect(union(this, new MultiRectArea(rect)).rect, false); 568 invalidate(); 569 } 570 571 /** 572 * Intersect with Rectangle object 573 */ 574 public void intersect(Rectangle rect) { 575 setRect(intersect(this, new MultiRectArea(rect)).rect, false); 576 invalidate(); 577 } 578 579 /** 580 * Subtract rectangle object 581 */ 582 public void substract(Rectangle rect) { 583 setRect(subtract(this, new MultiRectArea(rect)).rect, false); 584 } 585 586 /** 587 * Union two MutliRectareArea objects 588 */ 589 public static MultiRectArea intersect(MultiRectArea src1, MultiRectArea src2) { 590 MultiRectArea res = check(MultiRectAreaOp.Intersection.getResult(src1, src2), "intersect(MRA,MRA)"); //$NON-NLS-1$ 591 return res; 592 } 593 594 /** 595 * Intersect two MultiRectArea objects 596 */ 597 public static MultiRectArea union(MultiRectArea src1, MultiRectArea src2) { 598 MultiRectArea res = check(new MultiRectAreaOp.Union().getResult(src1, src2), "union(MRA,MRA)"); //$NON-NLS-1$ 599 return res; 600 } 601 602 /** 603 * Subtract two MultiRectArea objects 604 */ 605 public static MultiRectArea subtract(MultiRectArea src1, MultiRectArea src2) { 606 MultiRectArea res = check(MultiRectAreaOp.Subtraction.getResult(src1, src2), "subtract(MRA,MRA)"); //$NON-NLS-1$ 607 return res; 608 } 609 610 /** 611 * Print MultiRectArea object to output stream 612 */ 613 public static void print(MultiRectArea mra, String msg) { 614 if (mra == null) { 615 System.out.println(msg + "=null"); //$NON-NLS-1$ 616 } else { 617 Rectangle[] rects = mra.getRectangles(); 618 System.out.println(msg + "(" + rects.length + ")"); //$NON-NLS-1$ //$NON-NLS-2$ 619 for (Rectangle element : rects) { 620 System.out.println( 621 element.x + "," + //$NON-NLS-1$ 622 element.y + "," + //$NON-NLS-1$ 623 (element.x + element.width - 1) + "," + //$NON-NLS-1$ 624 (element.y + element.height - 1)); 625 } 626 } 627 } 628 629 /** 630 * Translate MultiRectArea object by (x, y) 631 */ 632 public void translate(int x, int y) { 633 for(int i = 1; i < rect[0];) { 634 rect[i++] += x; 635 rect[i++] += y; 636 rect[i++] += x; 637 rect[i++] += y; 638 } 639 640 if (bounds != null && !bounds.isEmpty()) { 641 bounds.translate(x, y); 642 } 643 644 if (rectangles != null) { 645 for (Rectangle element : rectangles) { 646 element.translate(x, y); 647 } 648 } 649 } 650 651 /** 652 * Add rectangle to the buffer without any checking 653 */ 654 public void addRect(int x1, int y1, int x2, int y2) { 655 int i = rect[0]; 656 rect = MultiRectAreaOp.checkBufSize(rect, 4); 657 rect[i++] = x1; 658 rect[i++] = y1; 659 rect[i++] = x2; 660 rect[i++] = y2; 661 } 662 663 /** 664 * Tests is MultiRectArea empty 665 */ 666 public boolean isEmpty() { 667 return rect[0] == 1; 668 } 669 670 void invalidate() { 671 bounds = null; 672 rectangles = null; 673 } 674 675 /** 676 * Returns bounds of MultiRectArea object 677 */ 678 public Rectangle getBounds() { 679 if (bounds != null) { 680 return bounds; 681 } 682 683 if (isEmpty()) { 684 return bounds = new Rectangle(); 685 } 686 687 int x1 = rect[1]; 688 int y1 = rect[2]; 689 int x2 = rect[3]; 690 int y2 = rect[4]; 691 692 for(int i = 5; i < rect[0]; i += 4) { 693 int rx1 = rect[i + 0]; 694 int ry1 = rect[i + 1]; 695 int rx2 = rect[i + 2]; 696 int ry2 = rect[i + 3]; 697 if (rx1 < x1) { 698 x1 = rx1; 699 } 700 if (rx2 > x2) { 701 x2 = rx2; 702 } 703 if (ry1 < y1) { 704 y1 = ry1; 705 } 706 if (ry2 > y2) { 707 y2 = ry2; 708 } 709 } 710 711 return bounds = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1); 712 } 713 714 /** 715 * Recturn rectangle count in the buffer 716 */ 717 public int getRectCount() { 718 return (rect[0] - 1) / 4; 719 } 720 721 /** 722 * Returns Rectangle array 723 */ 724 public Rectangle[] getRectangles() { 725 if (rectangles != null) { 726 return rectangles; 727 } 728 729 rectangles = new Rectangle[(rect[0] - 1) / 4]; 730 int j = 0; 731 for(int i = 1; i < rect[0]; i += 4) { 732 rectangles[j++] = new Rectangle( 733 rect[i], 734 rect[i + 1], 735 rect[i + 2] - rect[i] + 1, 736 rect[i + 3] - rect[i + 1] + 1); 737 } 738 return rectangles; 739 } 740 741 /** 742 * Returns Bounds2D 743 */ 744 public Rectangle2D getBounds2D() { 745 return getBounds(); 746 } 747 748 /** 749 * Tests does point lie inside MultiRectArea object 750 */ 751 public boolean contains(double x, double y) { 752 for(int i = 1; i < rect[0]; i+= 4) { 753 if (rect[i] <= x && x <= rect[i + 2] && rect[i + 1] <= y && y <= rect[i + 3]) { 754 return true; 755 } 756 } 757 return false; 758 } 759 760 /** 761 * Tests does Point2D lie inside MultiRectArea object 762 */ 763 public boolean contains(Point2D p) { 764 return contains(p.getX(), p.getY()); 765 } 766 767 /** 768 * Tests does rectangle lie inside MultiRectArea object 769 */ 770 public boolean contains(double x, double y, double w, double h) { 771 throw new RuntimeException("Not implemented"); //$NON-NLS-1$ 772 } 773 774 /** 775 * Tests does Rectangle2D lie inside MultiRectArea object 776 */ 777 public boolean contains(Rectangle2D r) { 778 throw new RuntimeException("Not implemented"); //$NON-NLS-1$ 779 } 780 781 /** 782 * Tests does rectangle intersect MultiRectArea object 783 */ 784 public boolean intersects(double x, double y, double w, double h) { 785 Rectangle r = new Rectangle(); 786 r.setRect(x, y, w, h); 787 return intersects(r); 788 } 789 790 /** 791 * Tests does Rectangle2D intersect MultiRectArea object 792 */ 793 public boolean intersects(Rectangle2D r) { 794 if (r == null || r.isEmpty()) { 795 return false; 796 } 797 for(int i = 1; i < rect[0]; i+= 4) { 798 if (r.intersects(rect[i], rect[i+1], rect[i + 2]-rect[i]+1, rect[i + 3]-rect[i + 1]+1)) { 799 return true; 800 } 801 } 802 return false; 803 } 804 805 /** 806 * Returns path iterator 807 */ 808 public PathIterator getPathIterator(AffineTransform t, double flatness) { 809 return new Iterator(this, t); 810 } 811 812 /** 813 * Returns path iterator 814 */ 815 public PathIterator getPathIterator(AffineTransform t) { 816 return new Iterator(this, t); 817 } 818 819 /** 820 * Returns MultiRectArea object converted to string 821 */ 822 @Override 823 public String toString() { 824 int cnt = getRectCount(); 825 StringBuffer sb = new StringBuffer((cnt << 5) + 128); 826 sb.append(getClass().getName()).append(" ["); //$NON-NLS-1$ 827 for(int i = 1; i < rect[0]; i += 4) { 828 sb.append(i > 1 ? ", [" : "[").append(rect[i]).append(", ").append(rect[i + 1]). //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 829 append(", ").append(rect[i + 2] - rect[i] + 1).append(", "). //$NON-NLS-1$ //$NON-NLS-2$ 830 append(rect[i + 3] - rect[i + 1] + 1).append("]"); //$NON-NLS-1$ 831 } 832 return sb.append("]").toString(); //$NON-NLS-1$ 833 } 834 835 } 836 837