1 /* 2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.java2d.pipe; 27 28 import java.awt.Rectangle; 29 import java.awt.Shape; 30 import java.awt.geom.AffineTransform; 31 import java.awt.geom.RectangularShape; 32 33 /** 34 * This class encapsulates a definition of a two dimensional region which 35 * consists of a number of Y ranges each containing multiple X bands. 36 * <p> 37 * A rectangular Region is allowed to have a null band list in which 38 * case the rectangular shape is defined by the bounding box parameters 39 * (lox, loy, hix, hiy). 40 * <p> 41 * The band list, if present, consists of a list of rows in ascending Y 42 * order, ending at endIndex which is the index beyond the end of the 43 * last row. Each row consists of at least 3 + 2n entries (n >= 1) 44 * where the first 3 entries specify the Y range as start, end, and 45 * the number of X ranges in that Y range. These 3 entries are 46 * followed by pairs of X coordinates in ascending order: 47 * <pre> 48 * bands[rowstart+0] = Y0; // starting Y coordinate 49 * bands[rowstart+1] = Y1; // ending Y coordinate - endY > startY 50 * bands[rowstart+2] = N; // number of X bands - N >= 1 51 * 52 * bands[rowstart+3] = X10; // starting X coordinate of first band 53 * bands[rowstart+4] = X11; // ending X coordinate of first band 54 * bands[rowstart+5] = X20; // starting X coordinate of second band 55 * bands[rowstart+6] = X21; // ending X coordinate of second band 56 * ... 57 * bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band 58 * bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band 59 * 60 * bands[rowstart+3+N*2] = ... // start of next Y row 61 * </pre> 62 */ 63 public class Region { 64 static final int INIT_SIZE = 50; 65 static final int GROW_SIZE = 50; 66 67 /** 68 * Immutable Region. 69 */ 70 private static final class ImmutableRegion extends Region { 71 protected ImmutableRegion(int lox, int loy, int hix, int hiy) { 72 super(lox, loy, hix, hiy); 73 } 74 75 // Override all the methods that mutate the object 76 public void appendSpans(sun.java2d.pipe.SpanIterator si) {} 77 public void setOutputArea(java.awt.Rectangle r) {} 78 public void setOutputAreaXYWH(int x, int y, int w, int h) {} 79 public void setOutputArea(int[] box) {} 80 public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {} 81 } 82 83 public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0); 84 public static final Region WHOLE_REGION = new ImmutableRegion( 85 Integer.MIN_VALUE, 86 Integer.MIN_VALUE, 87 Integer.MAX_VALUE, 88 Integer.MAX_VALUE); 89 90 int lox; 91 int loy; 92 int hix; 93 int hiy; 94 95 int endIndex; 96 int[] bands; 97 98 private static native void initIDs(); 99 100 static { 101 initIDs(); 102 } 103 104 /** 105 * Adds the dimension <code>dim</code> to the coordinate 106 * <code>start</code> with appropriate clipping. If 107 * <code>dim</code> is non-positive then the method returns 108 * the start coordinate. If the sum overflows an integer 109 * data type then the method returns <code>Integer.MAX_VALUE</code>. 110 */ 111 public static int dimAdd(int start, int dim) { 112 if (dim <= 0) return start; 113 if ((dim += start) < start) return Integer.MAX_VALUE; 114 return dim; 115 } 116 117 /** 118 * Adds the delta {@code dv} to the value {@code v} with 119 * appropriate clipping to the bounds of Integer resolution. 120 * If the answer would be greater than {@code Integer.MAX_VALUE} 121 * then {@code Integer.MAX_VALUE} is returned. 122 * If the answer would be less than {@code Integer.MIN_VALUE} 123 * then {@code Integer.MIN_VALUE} is returned. 124 * Otherwise the sum is returned. 125 */ 126 public static int clipAdd(int v, int dv) { 127 int newv = v + dv; 128 if ((newv > v) != (dv > 0)) { 129 newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; 130 } 131 return newv; 132 } 133 134 protected Region(int lox, int loy, int hix, int hiy) { 135 this.lox = lox; 136 this.loy = loy; 137 this.hix = hix; 138 this.hiy = hiy; 139 } 140 141 /** 142 * Returns a Region object covering the pixels which would be 143 * touched by a fill or clip operation on a Graphics implementation 144 * on the specified Shape object under the optionally specified 145 * AffineTransform object. 146 * 147 * @param s a non-null Shape object specifying the geometry enclosing 148 * the pixels of interest 149 * @param at an optional <code>AffineTransform</code> to be applied to the 150 * coordinates as they are returned in the iteration, or 151 * <code>null</code> if untransformed coordinates are desired 152 */ 153 public static Region getInstance(Shape s, AffineTransform at) { 154 return getInstance(WHOLE_REGION, false, s, at); 155 } 156 157 /** 158 * Returns a Region object covering the pixels which would be 159 * touched by a fill or clip operation on a Graphics implementation 160 * on the specified Shape object under the optionally specified 161 * AffineTransform object further restricted by the specified 162 * device bounds. 163 * <p> 164 * Note that only the bounds of the specified Region are used to 165 * restrict the resulting Region. 166 * If devBounds is non-rectangular and clipping to the specific 167 * bands of devBounds is needed, then an intersection of the 168 * resulting Region with devBounds must be performed in a 169 * subsequent step. 170 * 171 * @param devBounds a non-null Region specifying some bounds to 172 * clip the geometry to 173 * @param s a non-null Shape object specifying the geometry enclosing 174 * the pixels of interest 175 * @param at an optional <code>AffineTransform</code> to be applied to the 176 * coordinates as they are returned in the iteration, or 177 * <code>null</code> if untransformed coordinates are desired 178 */ 179 public static Region getInstance(Region devBounds, 180 Shape s, AffineTransform at) 181 { 182 return getInstance(devBounds, false, s, at); 183 } 184 185 /** 186 * Returns a Region object covering the pixels which would be 187 * touched by a fill or clip operation on a Graphics implementation 188 * on the specified Shape object under the optionally specified 189 * AffineTransform object further restricted by the specified 190 * device bounds. 191 * If the normalize parameter is true then coordinate normalization 192 * is performed as per the 2D Graphics non-antialiasing implementation 193 * of the VALUE_STROKE_NORMALIZE hint. 194 * <p> 195 * Note that only the bounds of the specified Region are used to 196 * restrict the resulting Region. 197 * If devBounds is non-rectangular and clipping to the specific 198 * bands of devBounds is needed, then an intersection of the 199 * resulting Region with devBounds must be performed in a 200 * subsequent step. 201 * 202 * @param devBounds a non-null Region specifying some bounds to 203 * clip the geometry to 204 * @param normalize a boolean indicating whether or not to apply 205 * normalization 206 * @param s a non-null Shape object specifying the geometry enclosing 207 * the pixels of interest 208 * @param at an optional <code>AffineTransform</code> to be applied to the 209 * coordinates as they are returned in the iteration, or 210 * <code>null</code> if untransformed coordinates are desired 211 */ 212 public static Region getInstance(Region devBounds, boolean normalize, 213 Shape s, AffineTransform at) 214 { 215 // Optimize for empty shapes to avoid involving the SpanIterator 216 if (s instanceof RectangularShape && 217 ((RectangularShape)s).isEmpty()) 218 { 219 return EMPTY_REGION; 220 } 221 222 int box[] = new int[4]; 223 ShapeSpanIterator sr = new ShapeSpanIterator(normalize); 224 try { 225 sr.setOutputArea(devBounds); 226 sr.appendPath(s.getPathIterator(at)); 227 sr.getPathBox(box); 228 Region r = Region.getInstance(box); 229 r.appendSpans(sr); 230 return r; 231 } finally { 232 sr.dispose(); 233 } 234 } 235 236 /** 237 * Returns a Region object with a rectangle of interest specified 238 * by the indicated Rectangle object. 239 * <p> 240 * This method can also be used to create a simple rectangular 241 * region. 242 */ 243 public static Region getInstance(Rectangle r) { 244 return Region.getInstanceXYWH(r.x, r.y, r.width, r.height); 245 } 246 247 /** 248 * Returns a Region object with a rectangle of interest specified 249 * by the indicated rectangular area in x, y, width, height format. 250 * <p> 251 * This method can also be used to create a simple rectangular 252 * region. 253 */ 254 public static Region getInstanceXYWH(int x, int y, int w, int h) { 255 return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); 256 } 257 258 /** 259 * Returns a Region object with a rectangle of interest specified 260 * by the indicated span array. 261 * <p> 262 * This method can also be used to create a simple rectangular 263 * region. 264 */ 265 public static Region getInstance(int box[]) { 266 return new Region(box[0], box[1], box[2], box[3]); 267 } 268 269 /** 270 * Returns a Region object with a rectangle of interest specified 271 * by the indicated rectangular area in lox, loy, hix, hiy format. 272 * <p> 273 * This method can also be used to create a simple rectangular 274 * region. 275 */ 276 public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) { 277 return new Region(lox, loy, hix, hiy); 278 } 279 280 /** 281 * Sets the rectangle of interest for storing and returning 282 * region bands. 283 * <p> 284 * This method can also be used to initialize a simple rectangular 285 * region. 286 */ 287 public void setOutputArea(Rectangle r) { 288 setOutputAreaXYWH(r.x, r.y, r.width, r.height); 289 } 290 291 /** 292 * Sets the rectangle of interest for storing and returning 293 * region bands. The rectangle is specified in x, y, width, height 294 * format and appropriate clipping is performed as per the method 295 * <code>dimAdd</code>. 296 * <p> 297 * This method can also be used to initialize a simple rectangular 298 * region. 299 */ 300 public void setOutputAreaXYWH(int x, int y, int w, int h) { 301 setOutputAreaXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); 302 } 303 304 /** 305 * Sets the rectangle of interest for storing and returning 306 * region bands. The rectangle is specified as a span array. 307 * <p> 308 * This method can also be used to initialize a simple rectangular 309 * region. 310 */ 311 public void setOutputArea(int box[]) { 312 this.lox = box[0]; 313 this.loy = box[1]; 314 this.hix = box[2]; 315 this.hiy = box[3]; 316 } 317 318 /** 319 * Sets the rectangle of interest for storing and returning 320 * region bands. The rectangle is specified in lox, loy, 321 * hix, hiy format. 322 * <p> 323 * This method can also be used to initialize a simple rectangular 324 * region. 325 */ 326 public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) { 327 this.lox = lox; 328 this.loy = loy; 329 this.hix = hix; 330 this.hiy = hiy; 331 } 332 333 /** 334 * Appends the list of spans returned from the indicated 335 * SpanIterator. Each span must be at a higher starting 336 * Y coordinate than the previous data or it must have a 337 * Y range equal to the highest Y band in the region and a 338 * higher X coordinate than any of the spans in that band. 339 */ 340 public void appendSpans(SpanIterator si) { 341 int[] box = new int[6]; 342 343 while (si.nextSpan(box)) { 344 appendSpan(box); 345 } 346 347 endRow(box); 348 calcBBox(); 349 } 350 351 /** 352 * Returns a Region object that represents the same list of 353 * rectangles as the current Region object, translated by 354 * the specified dx, dy translation factors. 355 */ 356 public Region getTranslatedRegion(int dx, int dy) { 357 if ((dx | dy) == 0) { 358 return this; 359 } 360 int tlox = lox + dx; 361 int tloy = loy + dy; 362 int thix = hix + dx; 363 int thiy = hiy + dy; 364 if ((tlox > lox) != (dx > 0) || 365 (tloy > loy) != (dy > 0) || 366 (thix > hix) != (dx > 0) || 367 (thiy > hiy) != (dy > 0)) 368 { 369 return getSafeTranslatedRegion(dx, dy); 370 } 371 Region ret = new Region(tlox, tloy, thix, thiy); 372 int bands[] = this.bands; 373 if (bands != null) { 374 int end = endIndex; 375 ret.endIndex = end; 376 int newbands[] = new int[end]; 377 ret.bands = newbands; 378 int i = 0; 379 int ncol; 380 while (i < end) { 381 newbands[i] = bands[i] + dy; i++; 382 newbands[i] = bands[i] + dy; i++; 383 newbands[i] = ncol = bands[i]; i++; 384 while (--ncol >= 0) { 385 newbands[i] = bands[i] + dx; i++; 386 newbands[i] = bands[i] + dx; i++; 387 } 388 } 389 } 390 return ret; 391 } 392 393 private Region getSafeTranslatedRegion(int dx, int dy) { 394 int tlox = clipAdd(lox, dx); 395 int tloy = clipAdd(loy, dy); 396 int thix = clipAdd(hix, dx); 397 int thiy = clipAdd(hiy, dy); 398 Region ret = new Region(tlox, tloy, thix, thiy); 399 int bands[] = this.bands; 400 if (bands != null) { 401 int end = endIndex; 402 int newbands[] = new int[end]; 403 int i = 0; // index for source bands 404 int j = 0; // index for translated newbands 405 int ncol; 406 while (i < end) { 407 int y1, y2; 408 newbands[j++] = y1 = clipAdd(bands[i++], dy); 409 newbands[j++] = y2 = clipAdd(bands[i++], dy); 410 newbands[j++] = ncol = bands[i++]; 411 int savej = j; 412 if (y1 < y2) { 413 while (--ncol >= 0) { 414 int x1 = clipAdd(bands[i++], dx); 415 int x2 = clipAdd(bands[i++], dx); 416 if (x1 < x2) { 417 newbands[j++] = x1; 418 newbands[j++] = x2; 419 } 420 } 421 } else { 422 i += ncol * 2; 423 } 424 // Did we get any non-empty bands in this row? 425 if (j > savej) { 426 newbands[savej-1] = (j - savej) / 2; 427 } else { 428 j = savej - 3; 429 } 430 } 431 if (j <= 5) { 432 if (j < 5) { 433 // No rows or bands were generated... 434 ret.lox = ret.loy = ret.hix = ret.hiy = 0; 435 } else { 436 // Only generated one single rect in the end... 437 ret.loy = newbands[0]; 438 ret.hiy = newbands[1]; 439 ret.lox = newbands[3]; 440 ret.hix = newbands[4]; 441 } 442 // ret.endIndex and ret.bands were never initialized... 443 // ret.endIndex = 0; 444 // ret.newbands = null; 445 } else { 446 // Generated multiple bands and/or multiple rows... 447 ret.endIndex = j; 448 ret.bands = newbands; 449 } 450 } 451 return ret; 452 } 453 454 /** 455 * Returns a Region object that represents the intersection of 456 * this object with the specified Rectangle. The return value 457 * may be this same object if no clipping occurs. 458 */ 459 public Region getIntersection(Rectangle r) { 460 return getIntersectionXYWH(r.x, r.y, r.width, r.height); 461 } 462 463 /** 464 * Returns a Region object that represents the intersection of 465 * this object with the specified rectangular area. The return 466 * value may be this same object if no clipping occurs. 467 */ 468 public Region getIntersectionXYWH(int x, int y, int w, int h) { 469 return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); 470 } 471 472 /** 473 * Returns a Region object that represents the intersection of 474 * this object with the specified rectangular area. The return 475 * value may be this same object if no clipping occurs. 476 */ 477 public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) { 478 if (isInsideXYXY(lox, loy, hix, hiy)) { 479 return this; 480 } 481 Region ret = new Region((lox < this.lox) ? this.lox : lox, 482 (loy < this.loy) ? this.loy : loy, 483 (hix > this.hix) ? this.hix : hix, 484 (hiy > this.hiy) ? this.hiy : hiy); 485 if (bands != null) { 486 ret.appendSpans(this.getSpanIterator()); 487 } 488 return ret; 489 } 490 491 /** 492 * Returns a Region object that represents the intersection of this 493 * object with the specified Region object. 494 * <p> 495 * If {@code A} and {@code B} are both Region Objects and 496 * <code>C = A.getIntersection(B);</code> then a point will 497 * be contained in {@code C} iff it is contained in both 498 * {@code A} and {@code B}. 499 * <p> 500 * The return value may be this same object or the argument 501 * Region object if no clipping occurs. 502 */ 503 public Region getIntersection(Region r) { 504 if (this.isInsideQuickCheck(r)) { 505 return this; 506 } 507 if (r.isInsideQuickCheck(this)) { 508 return r; 509 } 510 Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox, 511 (r.loy < this.loy) ? this.loy : r.loy, 512 (r.hix > this.hix) ? this.hix : r.hix, 513 (r.hiy > this.hiy) ? this.hiy : r.hiy); 514 if (!ret.isEmpty()) { 515 ret.filterSpans(this, r, INCLUDE_COMMON); 516 } 517 return ret; 518 } 519 520 /** 521 * Returns a Region object that represents the union of this 522 * object with the specified Region object. 523 * <p> 524 * If {@code A} and {@code B} are both Region Objects and 525 * <code>C = A.getUnion(B);</code> then a point will 526 * be contained in {@code C} iff it is contained in either 527 * {@code A} or {@code B}. 528 * <p> 529 * The return value may be this same object or the argument 530 * Region object if no augmentation occurs. 531 */ 532 public Region getUnion(Region r) { 533 if (r.isEmpty() || r.isInsideQuickCheck(this)) { 534 return this; 535 } 536 if (this.isEmpty() || this.isInsideQuickCheck(r)) { 537 return r; 538 } 539 Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, 540 (r.loy > this.loy) ? this.loy : r.loy, 541 (r.hix < this.hix) ? this.hix : r.hix, 542 (r.hiy < this.hiy) ? this.hiy : r.hiy); 543 ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON); 544 return ret; 545 } 546 547 /** 548 * Returns a Region object that represents the difference of the 549 * specified Region object subtracted from this object. 550 * <p> 551 * If {@code A} and {@code B} are both Region Objects and 552 * <code>C = A.getDifference(B);</code> then a point will 553 * be contained in {@code C} iff it is contained in 554 * {@code A} but not contained in {@code B}. 555 * <p> 556 * The return value may be this same object or the argument 557 * Region object if no clipping occurs. 558 */ 559 public Region getDifference(Region r) { 560 if (!r.intersectsQuickCheck(this)) { 561 return this; 562 } 563 if (this.isInsideQuickCheck(r)) { 564 return EMPTY_REGION; 565 } 566 Region ret = new Region(this.lox, this.loy, this.hix, this.hiy); 567 ret.filterSpans(this, r, INCLUDE_A); 568 return ret; 569 } 570 571 /** 572 * Returns a Region object that represents the exclusive or of this 573 * object with the specified Region object. 574 * <p> 575 * If {@code A} and {@code B} are both Region Objects and 576 * <code>C = A.getExclusiveOr(B);</code> then a point will 577 * be contained in {@code C} iff it is contained in either 578 * {@code A} or {@code B}, but not if it is contained in both. 579 * <p> 580 * The return value may be this same object or the argument 581 * Region object if either is empty. 582 */ 583 public Region getExclusiveOr(Region r) { 584 if (r.isEmpty()) { 585 return this; 586 } 587 if (this.isEmpty()) { 588 return r; 589 } 590 Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, 591 (r.loy > this.loy) ? this.loy : r.loy, 592 (r.hix < this.hix) ? this.hix : r.hix, 593 (r.hiy < this.hiy) ? this.hiy : r.hiy); 594 ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B); 595 return ret; 596 } 597 598 static final int INCLUDE_A = 1; 599 static final int INCLUDE_B = 2; 600 static final int INCLUDE_COMMON = 4; 601 602 private void filterSpans(Region ra, Region rb, int flags) { 603 int abands[] = ra.bands; 604 int bbands[] = rb.bands; 605 if (abands == null) { 606 abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix}; 607 } 608 if (bbands == null) { 609 bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix}; 610 } 611 int box[] = new int[6]; 612 int acolstart = 0; 613 int ay1 = abands[acolstart++]; 614 int ay2 = abands[acolstart++]; 615 int acolend = abands[acolstart++]; 616 acolend = acolstart + 2 * acolend; 617 int bcolstart = 0; 618 int by1 = bbands[bcolstart++]; 619 int by2 = bbands[bcolstart++]; 620 int bcolend = bbands[bcolstart++]; 621 bcolend = bcolstart + 2 * bcolend; 622 int y = loy; 623 while (y < hiy) { 624 if (y >= ay2) { 625 if (acolend < ra.endIndex) { 626 acolstart = acolend; 627 ay1 = abands[acolstart++]; 628 ay2 = abands[acolstart++]; 629 acolend = abands[acolstart++]; 630 acolend = acolstart + 2 * acolend; 631 } else { 632 if ((flags & INCLUDE_B) == 0) break; 633 ay1 = ay2 = hiy; 634 } 635 continue; 636 } 637 if (y >= by2) { 638 if (bcolend < rb.endIndex) { 639 bcolstart = bcolend; 640 by1 = bbands[bcolstart++]; 641 by2 = bbands[bcolstart++]; 642 bcolend = bbands[bcolstart++]; 643 bcolend = bcolstart + 2 * bcolend; 644 } else { 645 if ((flags & INCLUDE_A) == 0) break; 646 by1 = by2 = hiy; 647 } 648 continue; 649 } 650 int yend; 651 if (y < by1) { 652 if (y < ay1) { 653 y = Math.min(ay1, by1); 654 continue; 655 } 656 // We are in a set of rows that belong only to A 657 yend = Math.min(ay2, by1); 658 if ((flags & INCLUDE_A) != 0) { 659 box[1] = y; 660 box[3] = yend; 661 int acol = acolstart; 662 while (acol < acolend) { 663 box[0] = abands[acol++]; 664 box[2] = abands[acol++]; 665 appendSpan(box); 666 } 667 } 668 } else if (y < ay1) { 669 // We are in a set of rows that belong only to B 670 yend = Math.min(by2, ay1); 671 if ((flags & INCLUDE_B) != 0) { 672 box[1] = y; 673 box[3] = yend; 674 int bcol = bcolstart; 675 while (bcol < bcolend) { 676 box[0] = bbands[bcol++]; 677 box[2] = bbands[bcol++]; 678 appendSpan(box); 679 } 680 } 681 } else { 682 // We are in a set of rows that belong to both A and B 683 yend = Math.min(ay2, by2); 684 box[1] = y; 685 box[3] = yend; 686 int acol = acolstart; 687 int bcol = bcolstart; 688 int ax1 = abands[acol++]; 689 int ax2 = abands[acol++]; 690 int bx1 = bbands[bcol++]; 691 int bx2 = bbands[bcol++]; 692 int x = Math.min(ax1, bx1); 693 if (x < lox) x = lox; 694 while (x < hix) { 695 if (x >= ax2) { 696 if (acol < acolend) { 697 ax1 = abands[acol++]; 698 ax2 = abands[acol++]; 699 } else { 700 if ((flags & INCLUDE_B) == 0) break; 701 ax1 = ax2 = hix; 702 } 703 continue; 704 } 705 if (x >= bx2) { 706 if (bcol < bcolend) { 707 bx1 = bbands[bcol++]; 708 bx2 = bbands[bcol++]; 709 } else { 710 if ((flags & INCLUDE_A) == 0) break; 711 bx1 = bx2 = hix; 712 } 713 continue; 714 } 715 int xend; 716 boolean appendit; 717 if (x < bx1) { 718 if (x < ax1) { 719 xend = Math.min(ax1, bx1); 720 appendit = false; 721 } else { 722 xend = Math.min(ax2, bx1); 723 appendit = ((flags & INCLUDE_A) != 0); 724 } 725 } else if (x < ax1) { 726 xend = Math.min(ax1, bx2); 727 appendit = ((flags & INCLUDE_B) != 0); 728 } else { 729 xend = Math.min(ax2, bx2); 730 appendit = ((flags & INCLUDE_COMMON) != 0); 731 } 732 if (appendit) { 733 box[0] = x; 734 box[2] = xend; 735 appendSpan(box); 736 } 737 x = xend; 738 } 739 } 740 y = yend; 741 } 742 endRow(box); 743 calcBBox(); 744 } 745 746 /** 747 * Returns a Region object that represents the bounds of the 748 * intersection of this object with the bounds of the specified 749 * Region object. 750 * <p> 751 * The return value may be this same object if no clipping occurs 752 * and this Region is rectangular. 753 */ 754 public Region getBoundsIntersection(Rectangle r) { 755 return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height); 756 } 757 758 /** 759 * Returns a Region object that represents the bounds of the 760 * intersection of this object with the bounds of the specified 761 * rectangular area in x, y, width, height format. 762 * <p> 763 * The return value may be this same object if no clipping occurs 764 * and this Region is rectangular. 765 */ 766 public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) { 767 return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); 768 } 769 770 /** 771 * Returns a Region object that represents the bounds of the 772 * intersection of this object with the bounds of the specified 773 * rectangular area in lox, loy, hix, hiy format. 774 * <p> 775 * The return value may be this same object if no clipping occurs 776 * and this Region is rectangular. 777 */ 778 public Region getBoundsIntersectionXYXY(int lox, int loy, 779 int hix, int hiy) 780 { 781 if (this.bands == null && 782 this.lox >= lox && this.loy >= loy && 783 this.hix <= hix && this.hiy <= hiy) 784 { 785 return this; 786 } 787 return new Region((lox < this.lox) ? this.lox : lox, 788 (loy < this.loy) ? this.loy : loy, 789 (hix > this.hix) ? this.hix : hix, 790 (hiy > this.hiy) ? this.hiy : hiy); 791 } 792 793 /** 794 * Returns a Region object that represents the intersection of 795 * this object with the bounds of the specified Region object. 796 * <p> 797 * The return value may be this same object or the argument 798 * Region object if no clipping occurs and the Regions are 799 * rectangular. 800 */ 801 public Region getBoundsIntersection(Region r) { 802 if (this.encompasses(r)) { 803 return r; 804 } 805 if (r.encompasses(this)) { 806 return this; 807 } 808 return new Region((r.lox < this.lox) ? this.lox : r.lox, 809 (r.loy < this.loy) ? this.loy : r.loy, 810 (r.hix > this.hix) ? this.hix : r.hix, 811 (r.hiy > this.hiy) ? this.hiy : r.hiy); 812 } 813 814 /** 815 * Appends a single span defined by the 4 parameters 816 * spanlox, spanloy, spanhix, spanhiy. 817 * This span must be at a higher starting Y coordinate than 818 * the previous data or it must have a Y range equal to the 819 * highest Y band in the region and a higher X coordinate 820 * than any of the spans in that band. 821 */ 822 private void appendSpan(int box[]) { 823 int spanlox, spanloy, spanhix, spanhiy; 824 if ((spanlox = box[0]) < lox) spanlox = lox; 825 if ((spanloy = box[1]) < loy) spanloy = loy; 826 if ((spanhix = box[2]) > hix) spanhix = hix; 827 if ((spanhiy = box[3]) > hiy) spanhiy = hiy; 828 if (spanhix <= spanlox || spanhiy <= spanloy) { 829 return; 830 } 831 832 int curYrow = box[4]; 833 if (endIndex == 0 || spanloy >= bands[curYrow + 1]) { 834 if (bands == null) { 835 bands = new int[INIT_SIZE]; 836 } else { 837 needSpace(5); 838 endRow(box); 839 curYrow = box[4]; 840 } 841 bands[endIndex++] = spanloy; 842 bands[endIndex++] = spanhiy; 843 bands[endIndex++] = 0; 844 } else if (spanloy == bands[curYrow] && 845 spanhiy == bands[curYrow + 1] && 846 spanlox >= bands[endIndex - 1]) { 847 if (spanlox == bands[endIndex - 1]) { 848 bands[endIndex - 1] = spanhix; 849 return; 850 } 851 needSpace(2); 852 } else { 853 throw new InternalError("bad span"); 854 } 855 bands[endIndex++] = spanlox; 856 bands[endIndex++] = spanhix; 857 bands[curYrow + 2]++; 858 } 859 860 private void needSpace(int num) { 861 if (endIndex + num >= bands.length) { 862 int[] newbands = new int[bands.length + GROW_SIZE]; 863 System.arraycopy(bands, 0, newbands, 0, endIndex); 864 bands = newbands; 865 } 866 } 867 868 private void endRow(int box[]) { 869 int cur = box[4]; 870 int prev = box[5]; 871 if (cur > prev) { 872 int[] bands = this.bands; 873 if (bands[prev + 1] == bands[cur] && 874 bands[prev + 2] == bands[cur + 2]) 875 { 876 int num = bands[cur + 2] * 2; 877 cur += 3; 878 prev += 3; 879 while (num > 0) { 880 if (bands[cur++] != bands[prev++]) { 881 break; 882 } 883 num--; 884 } 885 if (num == 0) { 886 // prev == box[4] 887 bands[box[5] + 1] = bands[prev + 1]; 888 endIndex = prev; 889 return; 890 } 891 } 892 } 893 box[5] = box[4]; 894 box[4] = endIndex; 895 } 896 897 private void calcBBox() { 898 int[] bands = this.bands; 899 if (endIndex <= 5) { 900 if (endIndex == 0) { 901 lox = loy = hix = hiy = 0; 902 } else { 903 loy = bands[0]; 904 hiy = bands[1]; 905 lox = bands[3]; 906 hix = bands[4]; 907 endIndex = 0; 908 } 909 this.bands = null; 910 return; 911 } 912 int lox = this.hix; 913 int hix = this.lox; 914 int hiyindex = 0; 915 916 int i = 0; 917 while (i < endIndex) { 918 hiyindex = i; 919 int numbands = bands[i + 2]; 920 i += 3; 921 if (lox > bands[i]) { 922 lox = bands[i]; 923 } 924 i += numbands * 2; 925 if (hix < bands[i - 1]) { 926 hix = bands[i - 1]; 927 } 928 } 929 930 this.lox = lox; 931 this.loy = bands[0]; 932 this.hix = hix; 933 this.hiy = bands[hiyindex + 1]; 934 } 935 936 /** 937 * Returns the lowest X coordinate in the Region. 938 */ 939 public final int getLoX() { 940 return lox; 941 } 942 943 /** 944 * Returns the lowest Y coordinate in the Region. 945 */ 946 public final int getLoY() { 947 return loy; 948 } 949 950 /** 951 * Returns the highest X coordinate in the Region. 952 */ 953 public final int getHiX() { 954 return hix; 955 } 956 957 /** 958 * Returns the highest Y coordinate in the Region. 959 */ 960 public final int getHiY() { 961 return hiy; 962 } 963 964 /** 965 * Returns the width of this Region clipped to the range (0 - MAX_INT). 966 */ 967 public final int getWidth() { 968 if (hix < lox) return 0; 969 int w; 970 if ((w = hix - lox) < 0) { 971 w = Integer.MAX_VALUE; 972 } 973 return w; 974 } 975 976 /** 977 * Returns the height of this Region clipped to the range (0 - MAX_INT). 978 */ 979 public final int getHeight() { 980 if (hiy < loy) return 0; 981 int h; 982 if ((h = hiy - loy) < 0) { 983 h = Integer.MAX_VALUE; 984 } 985 return h; 986 } 987 988 /** 989 * Returns true iff this Region encloses no area. 990 */ 991 public boolean isEmpty() { 992 return (hix <= lox || hiy <= loy); 993 } 994 995 /** 996 * Returns true iff this Region represents a single simple 997 * rectangular area. 998 */ 999 public boolean isRectangular() { 1000 return (bands == null); 1001 } 1002 1003 /** 1004 * Returns true iff this Region contains the specified coordinate. 1005 */ 1006 public boolean contains(int x, int y) { 1007 if (x < lox || x >= hix || y < loy || y >= hiy) return false; 1008 if (bands == null) return true; 1009 int i = 0; 1010 while (i < endIndex) { 1011 if (y < bands[i++]) { 1012 return false; 1013 } 1014 if (y >= bands[i++]) { 1015 int numspans = bands[i++]; 1016 i += numspans * 2; 1017 } else { 1018 int end = bands[i++]; 1019 end = i + end * 2; 1020 while (i < end) { 1021 if (x < bands[i++]) return false; 1022 if (x < bands[i++]) return true; 1023 } 1024 return false; 1025 } 1026 } 1027 return false; 1028 } 1029 1030 /** 1031 * Returns true iff this Region lies inside the indicated 1032 * rectangular area specified in x, y, width, height format 1033 * with appropriate clipping performed as per the dimAdd method. 1034 */ 1035 public boolean isInsideXYWH(int x, int y, int w, int h) { 1036 return isInsideXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); 1037 } 1038 1039 /** 1040 * Returns true iff this Region lies inside the indicated 1041 * rectangular area specified in lox, loy, hix, hiy format. 1042 */ 1043 public boolean isInsideXYXY(int lox, int loy, int hix, int hiy) { 1044 return (this.lox >= lox && this.loy >= loy && 1045 this.hix <= hix && this.hiy <= hiy); 1046 1047 } 1048 1049 /** 1050 * Quickly checks if this Region lies inside the specified 1051 * Region object. 1052 * <p> 1053 * This method will return false if the specified Region 1054 * object is not a simple rectangle. 1055 */ 1056 public boolean isInsideQuickCheck(Region r) { 1057 return (r.bands == null && 1058 r.lox <= this.lox && r.loy <= this.loy && 1059 r.hix >= this.hix && r.hiy >= this.hiy); 1060 } 1061 1062 /** 1063 * Quickly checks if this Region intersects the specified 1064 * rectangular area specified in lox, loy, hix, hiy format. 1065 * <p> 1066 * This method tests only against the bounds of this region 1067 * and does not bother to test if the rectangular region 1068 * actually intersects any bands. 1069 */ 1070 public boolean intersectsQuickCheckXYXY(int lox, int loy, 1071 int hix, int hiy) 1072 { 1073 return (hix > this.lox && lox < this.hix && 1074 hiy > this.loy && loy < this.hiy); 1075 } 1076 1077 /** 1078 * Quickly checks if this Region intersects the specified 1079 * Region object. 1080 * <p> 1081 * This method tests only against the bounds of this region 1082 * and does not bother to test if the rectangular region 1083 * actually intersects any bands. 1084 */ 1085 public boolean intersectsQuickCheck(Region r) { 1086 return (r.hix > this.lox && r.lox < this.hix && 1087 r.hiy > this.loy && r.loy < this.hiy); 1088 } 1089 1090 /** 1091 * Quickly checks if this Region surrounds the specified 1092 * Region object. 1093 * <p> 1094 * This method will return false if this Region object is 1095 * not a simple rectangle. 1096 */ 1097 public boolean encompasses(Region r) { 1098 return (this.bands == null && 1099 this.lox <= r.lox && this.loy <= r.loy && 1100 this.hix >= r.hix && this.hiy >= r.hiy); 1101 } 1102 1103 /** 1104 * Quickly checks if this Region surrounds the specified 1105 * rectangular area specified in x, y, width, height format. 1106 * <p> 1107 * This method will return false if this Region object is 1108 * not a simple rectangle. 1109 */ 1110 public boolean encompassesXYWH(int x, int y, int w, int h) { 1111 return encompassesXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); 1112 } 1113 1114 /** 1115 * Quickly checks if this Region surrounds the specified 1116 * rectangular area specified in lox, loy, hix, hiy format. 1117 * <p> 1118 * This method will return false if this Region object is 1119 * not a simple rectangle. 1120 */ 1121 public boolean encompassesXYXY(int lox, int loy, int hix, int hiy) { 1122 return (this.bands == null && 1123 this.lox <= lox && this.loy <= loy && 1124 this.hix >= hix && this.hiy >= hiy); 1125 } 1126 1127 /** 1128 * Gets the bbox of the available spans, clipped to the OutputArea. 1129 */ 1130 public void getBounds(int pathbox[]) { 1131 pathbox[0] = lox; 1132 pathbox[1] = loy; 1133 pathbox[2] = hix; 1134 pathbox[3] = hiy; 1135 } 1136 1137 /** 1138 * Clips the indicated bbox array to the bounds of this Region. 1139 */ 1140 public void clipBoxToBounds(int bbox[]) { 1141 if (bbox[0] < lox) bbox[0] = lox; 1142 if (bbox[1] < loy) bbox[1] = loy; 1143 if (bbox[2] > hix) bbox[2] = hix; 1144 if (bbox[3] > hiy) bbox[3] = hiy; 1145 } 1146 1147 /** 1148 * Gets an iterator object to iterate over the spans in this region. 1149 */ 1150 public RegionIterator getIterator() { 1151 return new RegionIterator(this); 1152 } 1153 1154 /** 1155 * Gets a span iterator object that iterates over the spans in this region 1156 */ 1157 public SpanIterator getSpanIterator() { 1158 return new RegionSpanIterator(this); 1159 } 1160 1161 /** 1162 * Gets a span iterator object that iterates over the spans in this region 1163 * but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi). 1164 */ 1165 public SpanIterator getSpanIterator(int bbox[]) { 1166 SpanIterator result = getSpanIterator(); 1167 result.intersectClipBox(bbox[0], bbox[1], bbox[2], bbox[3]); 1168 return result; 1169 } 1170 1171 /** 1172 * Returns a SpanIterator that is the argument iterator filtered by 1173 * this region. 1174 */ 1175 public SpanIterator filter(SpanIterator si) { 1176 if (bands == null) { 1177 si.intersectClipBox(lox, loy, hix, hiy); 1178 } else { 1179 si = new RegionClipSpanIterator(this, si); 1180 } 1181 return si; 1182 } 1183 1184 public String toString() { 1185 StringBuffer sb = new StringBuffer(); 1186 sb.append("Region[["); 1187 sb.append(lox); 1188 sb.append(", "); 1189 sb.append(loy); 1190 sb.append(" => "); 1191 sb.append(hix); 1192 sb.append(", "); 1193 sb.append(hiy); 1194 sb.append("]"); 1195 if (bands != null) { 1196 int col = 0; 1197 while (col < endIndex) { 1198 sb.append("y{"); 1199 sb.append(bands[col++]); 1200 sb.append(","); 1201 sb.append(bands[col++]); 1202 sb.append("}["); 1203 int end = bands[col++]; 1204 end = col + end * 2; 1205 while (col < end) { 1206 sb.append("x("); 1207 sb.append(bands[col++]); 1208 sb.append(", "); 1209 sb.append(bands[col++]); 1210 sb.append(")"); 1211 } 1212 sb.append("]"); 1213 } 1214 } 1215 sb.append("]"); 1216 return sb.toString(); 1217 } 1218 1219 public int hashCode() { 1220 return (isEmpty() ? 0 : (lox * 3 + loy * 5 + hix * 7 + hiy * 9)); 1221 } 1222 1223 public boolean equals(Object o) { 1224 if (!(o instanceof Region)) { 1225 return false; 1226 } 1227 Region r = (Region) o; 1228 if (this.isEmpty()) { 1229 return r.isEmpty(); 1230 } else if (r.isEmpty()) { 1231 return false; 1232 } 1233 if (r.lox != this.lox || r.loy != this.loy || 1234 r.hix != this.hix || r.hiy != this.hiy) 1235 { 1236 return false; 1237 } 1238 if (this.bands == null) { 1239 return (r.bands == null); 1240 } else if (r.bands == null) { 1241 return false; 1242 } 1243 if (this.endIndex != r.endIndex) { 1244 return false; 1245 } 1246 int abands[] = this.bands; 1247 int bbands[] = r.bands; 1248 for (int i = 0; i < endIndex; i++) { 1249 if (abands[i] != bbands[i]) { 1250 return false; 1251 } 1252 } 1253 return true; 1254 } 1255 }