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 25 public class MultiRectAreaOp { 26 27 /** 28 * Rectangle buffer capacity 29 */ 30 public static final int RECT_CAPACITY = 16; 31 32 /** 33 * If number of rectangle in MultiRectArea object less than MAX_SIMPLE simple algorithm applies 34 */ 35 private static final int MAX_SIMPLE = 8; 36 37 /** 38 * Create buffer 39 */ 40 public static int[] createBuf(int capacity) { 41 if (capacity == 0) { 42 capacity = RECT_CAPACITY; 43 } 44 int[] buf = new int[capacity]; 45 buf[0] = 1; 46 return buf; 47 } 48 49 /** 50 * Checks buffer size and reallocate if necessary 51 */ 52 public static int[] checkBufSize(int[] buf, int capacity) { 53 if (buf[0] + capacity >= buf.length) { 54 int length = buf[0] + (capacity > RECT_CAPACITY ? capacity : RECT_CAPACITY); 55 int[] tmp = new int[length]; 56 System.arraycopy(buf, 0, tmp, 0, buf[0]); 57 buf = tmp; 58 } 59 buf[0] += capacity; 60 return buf; 61 } 62 63 /** 64 * Region class provides basic functionlity for MultiRectArea objects to make logical operations 65 */ 66 static class Region { 67 68 int[] region; 69 int[] active; 70 int[] bottom; 71 int index; 72 73 public Region(int[] region) { 74 this.region = region; 75 active = new int[RECT_CAPACITY]; 76 bottom = new int[RECT_CAPACITY]; 77 active[0] = 1; 78 bottom[0] = 1; 79 index = 1; 80 } 81 82 void addActive(int index) { 83 int length = active[0]; 84 active = checkBufSize(active, 4); 85 int i = 1; 86 87 while(i < length) { 88 if (region[index] < active[i]) { 89 // Insert 90 System.arraycopy(active, i, active, i + 4, length - i); 91 length = i; 92 break; 93 } 94 i += 4; 95 } 96 System.arraycopy(region, index, active, length, 4); 97 98 } 99 100 void findActive(int top, int bottom) { 101 while(index < region[0]) { 102 if (region[index + 1] > bottom) { // y1 > bottom 103 return; 104 } 105 if (region[index + 3] >= top) { // y2 >= top 106 addActive(index); 107 } 108 index += 4; 109 } 110 } 111 112 void deleteActive(int bottom) { 113 int length = active[0]; 114 for(int i = 1; i < length;) { 115 if (active[i + 3] == bottom) { 116 length -= 4; 117 if (i < length) { 118 System.arraycopy(active, i + 4, active, i, length - i); 119 } 120 } else { 121 i += 4; 122 } 123 } 124 active[0] = length; 125 } 126 127 void deleteActive() { 128 int length = active[0]; 129 for(int i = length - 4; i > 0; i -= 4) { 130 if (active[i + 1] > active[i + 3]) { 131 length -= 4; 132 if (i < length) { 133 System.arraycopy(active, i + 4, active, i, length - i); 134 } 135 } 136 } 137 active[0] = length; 138 } 139 140 void createLevel(int[] level) { 141 int levelCount = 1; 142 int topIndex = 1; 143 int i = 1; 144 while(i < region[0]) { 145 146 int top = region[i + 1]; 147 int bottom = region[i + 3] + 1; 148 int j = topIndex; 149 150 addTop: { 151 while(j < levelCount) { 152 if (level[j] == top) { 153 break addTop; 154 } 155 if (level[j] > top) { 156 System.arraycopy(level, j, level, j + 1, levelCount - j); 157 break; 158 } 159 j++; 160 } 161 162 level[j] = top; 163 levelCount++; 164 topIndex = j; 165 } 166 167 addBottom: { 168 while(j < levelCount) { 169 if (level[j] == bottom) { 170 break addBottom; 171 } 172 if (level[j] > bottom) { 173 System.arraycopy(level, j, level, j + 1, levelCount - j); 174 break; 175 } 176 j++; 177 }; 178 179 level[j] = bottom; 180 levelCount++; 181 } 182 183 i += 4; 184 } 185 level[0] = levelCount; 186 } 187 188 static void sortOrdered(int[] src1, int[] src2, int[] dst) { 189 int length1 = src1[0]; 190 int length2 = src2[0]; 191 int count = 1; 192 int i1 = 1; 193 int i2 = 1; 194 int v1 = src1[1]; 195 int v2 = src2[1]; 196 while(true) { 197 198 LEFT: { 199 while(i1 < length1) { 200 v1 = src1[i1]; 201 if (v1 >= v2) { 202 break LEFT; 203 } 204 dst[count++] = v1; 205 i1++; 206 } 207 while(i2 < length2) { 208 dst[count++] = src2[i2++]; 209 } 210 dst[0] = count; 211 return; 212 } 213 214 RIGHT: { 215 while(i2 < length2) { 216 v2 = src2[i2]; 217 if (v2 >= v1) { 218 break RIGHT; 219 } 220 dst[count++] = v2; 221 i2++; 222 } 223 while(i1 < length1) { 224 dst[count++] = src1[i1++]; 225 } 226 dst[0] = count; 227 return; 228 } 229 230 if (v1 == v2) { 231 dst[count++] = v1; 232 i1++; 233 i2++; 234 if (i1 < length1) { 235 v1 = src1[i1]; 236 } 237 if (i2 < length2 - 1) { 238 v2 = src2[i2]; 239 } 240 } 241 } 242 // UNREACHABLE 243 } 244 245 } 246 247 /** 248 * Intersection class provides intersection of two MultiRectAre aobjects 249 */ 250 static class Intersection { 251 252 static void intersectRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) { 253 254 Region d1 = new Region(reg1); 255 Region d2 = new Region(reg2); 256 257 int[] level = new int[height1 + height2]; 258 int[] level1 = new int[height1]; 259 int[] level2 = new int[height2]; 260 d1.createLevel(level1); 261 d2.createLevel(level2); 262 Region.sortOrdered(level1, level2, level); 263 264 int top; 265 int bottom = level[1] - 1; 266 for(int i = 2; i < level[0]; i++) { 267 268 top = bottom + 1; 269 bottom = level[i] - 1; 270 271 d1.findActive(top, bottom); 272 d2.findActive(top, bottom); 273 274 int i1 = 1; 275 int i2 = 1; 276 277 while(i1 < d1.active[0] && i2 < d2.active[0]) { 278 279 int x11 = d1.active[i1]; 280 int x12 = d1.active[i1 + 2]; 281 int x21 = d2.active[i2]; 282 int x22 = d2.active[i2 + 2]; 283 284 if (x11 <= x21) { 285 if (x12 >= x21) { 286 if (x12 <= x22) { 287 dst.addRectCashed(x21, top, x12, bottom); 288 i1 += 4; 289 } else { 290 dst.addRectCashed(x21, top, x22, bottom); 291 i2 += 4; 292 } 293 } else { 294 i1 += 4; 295 } 296 } else { 297 if (x22 >= x11) { 298 if (x22 <= x12) { 299 dst.addRectCashed(x11, top, x22, bottom); 300 i2 += 4; 301 } else { 302 dst.addRectCashed(x11, top, x12, bottom); 303 i1 += 4; 304 } 305 } else { 306 i2 += 4; 307 } 308 } 309 } 310 311 d1.deleteActive(bottom); 312 d2.deleteActive(bottom); 313 } 314 } 315 316 static int[] simpleIntersect(MultiRectArea src1, MultiRectArea src2) { 317 int[] rect1 = src1.rect; 318 int[] rect2 = src2.rect; 319 int[] rect = createBuf(0); 320 321 int k = 1; 322 for(int i = 1; i < rect1[0];) { 323 324 int x11 = rect1[i++]; 325 int y11 = rect1[i++]; 326 int x12 = rect1[i++]; 327 int y12 = rect1[i++]; 328 329 for(int j = 1; j < rect2[0];) { 330 331 int x21 = rect2[j++]; 332 int y21 = rect2[j++]; 333 int x22 = rect2[j++]; 334 int y22 = rect2[j++]; 335 336 if (x11 <= x22 && x12 >= x21 && 337 y11 <= y22 && y12 >= y21) 338 { 339 rect = checkBufSize(rect, 4); 340 rect[k++] = x11 > x21 ? x11 : x21; 341 rect[k++] = y11 > y21 ? y11 : y21; 342 rect[k++] = x12 > x22 ? x22 : x12; 343 rect[k++] = y12 > y22 ? y22 : y12; 344 } 345 } 346 } 347 348 rect[0] = k; 349 return rect; 350 } 351 352 public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) { 353 354 if (src1 == null || src2 == null || src1.isEmpty() || src2.isEmpty()) { 355 return new MultiRectArea(); 356 } 357 358 MultiRectArea.RectCash dst = new MultiRectArea.RectCash(); 359 360 if (!src1.sorted || !src2.sorted || 361 src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE) 362 { 363 dst.setRect(simpleIntersect(src1, src2), false); 364 } else { 365 Rectangle bounds1 = src1.getBounds(); 366 Rectangle bounds2 = src2.getBounds(); 367 Rectangle bounds3 = bounds1.intersection(bounds2); 368 if (bounds3.width > 0 && bounds3.height > 0) { 369 intersectRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2); 370 } 371 } 372 373 return dst; 374 } 375 376 } 377 378 /** 379 * Union class provides union of two MultiRectAre aobjects 380 */ 381 static class Union { 382 383 int rx1, rx2; 384 int top, bottom; 385 MultiRectArea.RectCash dst; 386 387 boolean next(Region d, int index) { 388 int x1 = d.active[index]; 389 int x2 = d.active[index + 2]; 390 boolean res = false; 391 392 if (x2 < rx1 - 1) { 393 res = true; 394 dst.addRectCashed(x1, top, x2, bottom); 395 } else 396 if (x1 > rx2 + 1) { 397 res = false; 398 dst.addRectCashed(rx1, top, rx2, bottom); 399 rx1 = x1; 400 rx2 = x2; 401 } else { 402 res = x2 <= rx2; 403 rx1 = Math.min(x1, rx1); 404 rx2 = Math.max(x2, rx2); 405 } 406 407 // Top 408 if (d.active[index + 1] < top) { 409 dst.addRectCashed(x1, d.active[index + 1], x2, top - 1); 410 } 411 // Bottom 412 if (d.active[index + 3] > bottom) { 413 d.active[index + 1] = bottom + 1; 414 } 415 return res; 416 } 417 418 void check(Region d, int index, boolean t) { 419 int x1 = d.active[index]; 420 int x2 = d.active[index + 2]; 421 // Top 422 if (d.active[index + 1] < top) { 423 dst.addRectCashed(x1, d.active[index + 1], x2, top - 1); 424 } 425 if (t) { 426 dst.addRectCashed(x1, top, x2, bottom); 427 } 428 // Bottom 429 if (d.active[index + 3] > bottom) { 430 d.active[index + 1] = bottom + 1; 431 } 432 } 433 434 void unionRegions(int[] reg1, int[] reg2, int height1, int height2) { 435 Region d1 = new Region(reg1); 436 Region d2 = new Region(reg2); 437 438 int[] level = new int[height1 + height2]; 439 int[] level1 = new int[height1]; 440 int[] level2 = new int[height2]; 441 d1.createLevel(level1); 442 d2.createLevel(level2); 443 Region.sortOrdered(level1, level2, level); 444 445 bottom = level[1] - 1; 446 for(int i = 2; i < level[0]; i++) { 447 448 top = bottom + 1; 449 bottom = level[i] - 1; 450 451 d1.findActive(top, bottom); 452 d2.findActive(top, bottom); 453 454 int i1 = 1; 455 int i2 = 1; 456 boolean res1, res2; 457 458 if (d1.active[0] > 1) { 459 check(d1, 1, false); 460 rx1 = d1.active[1]; 461 rx2 = d1.active[3]; 462 i1 += 4; 463 res1 = false; 464 res2 = true; 465 } else 466 if (d2.active[0] > 1) { 467 check(d2, 1, false); 468 rx1 = d2.active[1]; 469 rx2 = d2.active[3]; 470 i2 += 4; 471 res1 = true; 472 res2 = false; 473 } else { 474 continue; 475 } 476 477 outer: 478 while(true) { 479 480 while (res1) { 481 if (i1 >= d1.active[0]) { 482 dst.addRectCashed(rx1, top, rx2, bottom); 483 while(i2 < d2.active[0]) { 484 check(d2, i2, true); 485 i2 += 4; 486 } 487 break outer; 488 } 489 res1 = next(d1, i1); 490 i1 += 4; 491 } 492 493 while (res2) { 494 if (i2 >= d2.active[0]) { 495 dst.addRectCashed(rx1, top, rx2, bottom); 496 while(i1 < d1.active[0]) { 497 check(d1, i1, true); 498 i1 += 4; 499 } 500 break outer; 501 } 502 res2 = next(d2, i2); 503 i2 += 4; 504 } 505 506 res1 = true; 507 res2 = true; 508 } // while 509 510 d1.deleteActive(bottom); 511 d2.deleteActive(bottom); 512 513 } 514 } 515 516 static void simpleUnion(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) { 517 if (src1.getRectCount() < src2.getRectCount()) { 518 simpleUnion(src2, src1, dst); 519 } else { 520 Subtraction.simpleSubtract(src1, src2, dst); 521 int pos = dst.rect[0]; 522 int size = src2.rect[0] - 1; 523 dst.rect = checkBufSize(dst.rect, size); 524 System.arraycopy(src2.rect,1, dst.rect, pos, size); 525 dst.resort(); 526 } 527 } 528 529 MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) { 530 531 if (src1 == null || src1.isEmpty()) { 532 return new MultiRectArea(src2); 533 } 534 535 if (src2 == null || src2.isEmpty()) { 536 return new MultiRectArea(src1); 537 } 538 539 dst = new MultiRectArea.RectCash(); 540 541 if (!src1.sorted || !src2.sorted || 542 src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE) 543 { 544 simpleUnion(src1, src2, dst); 545 } else { 546 Rectangle bounds1 = src1.getBounds(); 547 Rectangle bounds2 = src2.getBounds(); 548 Rectangle bounds3 = bounds1.intersection(bounds2); 549 550 if (bounds3.width < 0 || bounds3.height < 0) { 551 if (bounds1.y + bounds1.height < bounds2.y) { 552 dst.setRect(addVerRegion(src1.rect, src2.rect), false); 553 } else 554 if (bounds2.y + bounds2.height < bounds1.y) { 555 dst.setRect(addVerRegion(src2.rect, src1.rect), false); 556 } else 557 if (bounds1.x < bounds2.x) { 558 dst.setRect(addHorRegion(src1.rect, src2.rect), false); 559 } else { 560 dst.setRect(addHorRegion(src2.rect, src1.rect), false); 561 } 562 } else { 563 unionRegions(src1.rect, src2.rect, bounds1.height + 2, bounds2.height + 2); 564 } 565 } 566 567 return dst; 568 } 569 570 int[] addVerRegion(int[] top, int[] bottom) { 571 int length = top[0] + bottom[0] - 1; 572 int[] dst = new int[length]; 573 dst[0] = length; 574 System.arraycopy(top, 1, dst, 1, top[0] - 1); 575 System.arraycopy(bottom, 1, dst, top[0], bottom[0] - 1); 576 return dst; 577 } 578 579 int[] addHorRegion(int[] left, int[] right) { 580 int count1 = left[0]; 581 int count2 = right[0]; 582 int[] dst = new int[count1 + count2 + 1]; 583 int count = 1; 584 int index1 = 1; 585 int index2 = 1; 586 587 int top1 = left[2]; 588 int top2 = right[2]; 589 int pos1, pos2; 590 591 while(true) { 592 593 if (index1 >= count1) { 594 System.arraycopy(right, index2, dst, count, count2 - index2); 595 count += count2 - index2; 596 break; 597 } 598 if (index2 >= count2) { 599 System.arraycopy(left, index1, dst, count, count1 - index1); 600 count += count1 - index1; 601 break; 602 } 603 604 if (top1 < top2) { 605 pos1 = index1; 606 do { 607 index1 += 4; 608 } while (index1 < count1 && (top1 = left[index1 + 1]) < top2); 609 System.arraycopy(left, pos1, dst, count, index1 - pos1); 610 count += index1 - pos1; 611 continue; 612 } 613 614 if (top1 > top2) { 615 pos2 = index2; 616 do { 617 index2 += 4; 618 } while (index2 < count2 && (top2 = right[index2 + 1]) < top1); 619 System.arraycopy(right, pos2, dst, count, index2 - pos2); 620 count += index2 - pos2; 621 continue; 622 } 623 624 int top = top1; 625 pos1 = index1; 626 pos2 = index2; 627 do { 628 index1 += 4; 629 } while(index1 < count1 && (top1 = left[index1 + 1]) == top); 630 do { 631 index2 += 4; 632 } while(index2 < count2 && (top2 = right[index2 + 1]) == top); 633 634 System.arraycopy(left, pos1, dst, count, index1 - pos1); 635 count += index1 - pos1; 636 System.arraycopy(right, pos2, dst, count, index2 - pos2); 637 count += index2 - pos2; 638 } 639 640 dst[0] = count; 641 return dst; 642 } 643 644 } 645 646 /** 647 * Subtraction class provides subtraction of two MultiRectAre aobjects 648 */ 649 static class Subtraction { 650 651 static void subtractRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) { 652 Region d1 = new Region(reg1); 653 Region d2 = new Region(reg2); 654 655 int[] level = new int[height1 + height2]; 656 int[] level1 = new int[height1]; 657 int[] level2 = new int[height2]; 658 d1.createLevel(level1); 659 d2.createLevel(level2); 660 Region.sortOrdered(level1, level2, level); 661 662 int top; 663 int bottom = level[1] - 1; 664 for(int i = 2; i < level[0]; i++) { 665 666 top = bottom + 1; 667 bottom = level[i] - 1; 668 669 d1.findActive(top, bottom); 670 if (d1.active[0] == 1) { 671 d2.deleteActive(bottom); 672 continue; 673 } 674 675 d2.findActive(top, bottom); 676 677 int i1 = 1; 678 int i2 = 1; 679 680 int rx1 = 0; 681 int rx2 = 0; 682 683 boolean next = true; 684 685 while(true) { 686 687 if (next) { 688 next = false; 689 if (i1 >= d1.active[0]) { 690 break; 691 } 692 // Bottom 693 d1.active[i1 + 1] = bottom + 1; 694 rx1 = d1.active[i1]; 695 rx2 = d1.active[i1 + 2]; 696 i1 += 4; 697 } 698 699 if (i2 >= d2.active[0]) { 700 dst.addRectCashed(rx1, top, rx2, bottom); 701 for(int j = i1; j < d1.active[0]; j += 4) { 702 dst.addRectCashed(d1.active[j], top, d1.active[j + 2], bottom); 703 d1.active[j + 1] = bottom + 1; 704 } 705 break; 706 } 707 708 int x1 = d2.active[i2]; 709 int x2 = d2.active[i2 + 2]; 710 711 if (rx1 < x1) { 712 if (rx2 >= x1) { 713 if (rx2 <= x2) { 714 // [-----------] 715 // [-------------] 716 dst.addRectCashed(rx1, top, x1 - 1, bottom); 717 next = true; 718 } else { 719 // [-----------------] 720 // [------] 721 dst.addRectCashed(rx1, top, x1 - 1, bottom); 722 rx1 = x2 + 1; 723 i2 += 4; 724 } 725 } else { 726 // [-----] 727 // [----] 728 dst.addRectCashed(rx1, top, rx2, bottom); 729 next = true; 730 } 731 } else { 732 if (rx1 <= x2) { 733 if (rx2 <= x2) { 734 // [------] 735 // [-----------] 736 next = true; 737 } else { 738 // [------------] 739 // [---------] 740 rx1 = x2 + 1; 741 i2 += 4; 742 } 743 } else { 744 // [----] 745 // [-----] 746 i2 += 4; 747 } 748 } 749 750 } 751 d1.deleteActive(); 752 d2.deleteActive(bottom); 753 } 754 } 755 756 static void subtractRect(int x11, int y11, int x12, int y12, int[] rect, int index, MultiRectArea dst) { 757 758 for(int i = index; i < rect[0]; i += 4) { 759 int x21 = rect[i + 0]; 760 int y21 = rect[i + 1]; 761 int x22 = rect[i + 2]; 762 int y22 = rect[i + 3]; 763 764 if (x11 <= x22 && x12 >= x21 && y11 <= y22 && y12 >= y21) { 765 int top, bottom; 766 if (y11 < y21) { 767 subtractRect(x11, y11, x12, y21 - 1, rect, i + 4, dst); 768 top = y21; 769 } else { 770 top = y11; 771 } 772 if (y12 > y22) { 773 subtractRect(x11, y22 + 1, x12, y12, rect, i + 4, dst); 774 bottom = y22; 775 } else { 776 bottom = y12; 777 } 778 if (x11 < x21) { 779 subtractRect(x11, top, x21 - 1, bottom, rect, i + 4, dst); 780 } 781 if (x12 > x22) { 782 subtractRect(x22 + 1, top, x12, bottom, rect, i + 4, dst); 783 } 784 return; 785 } 786 } 787 dst.addRect(x11, y11, x12, y12); 788 } 789 790 static void simpleSubtract(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) { 791 for(int i = 1; i < src1.rect[0]; i += 4) { 792 subtractRect( 793 src1.rect[i + 0], 794 src1.rect[i + 1], 795 src1.rect[i + 2], 796 src1.rect[i + 3], 797 src2.rect, 798 1, 799 dst); 800 } 801 dst.resort(); 802 } 803 804 public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) { 805 806 if (src1 == null || src1.isEmpty()) { 807 return new MultiRectArea(); 808 } 809 810 if (src2 == null || src2.isEmpty()) { 811 return new MultiRectArea(src1); 812 } 813 814 MultiRectArea.RectCash dst = new MultiRectArea.RectCash(); 815 816 if (!src1.sorted || !src2.sorted || 817 src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE) 818 { 819 simpleSubtract(src1, src2, dst); 820 } else { 821 Rectangle bounds1 = src1.getBounds(); 822 Rectangle bounds2 = src2.getBounds(); 823 Rectangle bounds3 = bounds1.intersection(bounds2); 824 825 if (bounds3.width > 0 && bounds3.height > 0) { 826 subtractRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2); 827 } else { 828 dst.setRect(src1.rect, true); 829 } 830 } 831 832 return dst; 833 } 834 835 } 836 837 } 838