History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/java2d/pipe/Region.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/java2d/pipe/Region.java+1255−0
@@ -0,0 +1,1255 @@
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
26package sun.java2d.pipe;
27
28import java.awt.Rectangle;
29import java.awt.Shape;
30import java.awt.geom.AffineTransform;
31import 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 */
63public 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}