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