0N/A/*
2362N/A * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.java2d.pipe;
0N/A
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.Shape;
0N/Aimport java.awt.geom.AffineTransform;
886N/Aimport java.awt.geom.RectangularShape;
0N/A
0N/A/**
0N/A * This class encapsulates a definition of a two dimensional region which
0N/A * consists of a number of Y ranges each containing multiple X bands.
0N/A * <p>
0N/A * A rectangular Region is allowed to have a null band list in which
0N/A * case the rectangular shape is defined by the bounding box parameters
0N/A * (lox, loy, hix, hiy).
0N/A * <p>
0N/A * The band list, if present, consists of a list of rows in ascending Y
0N/A * order, ending at endIndex which is the index beyond the end of the
0N/A * last row. Each row consists of at least 3 + 2n entries (n >= 1)
0N/A * where the first 3 entries specify the Y range as start, end, and
0N/A * the number of X ranges in that Y range. These 3 entries are
0N/A * followed by pairs of X coordinates in ascending order:
0N/A * <pre>
0N/A * bands[rowstart+0] = Y0; // starting Y coordinate
0N/A * bands[rowstart+1] = Y1; // ending Y coordinate - endY > startY
0N/A * bands[rowstart+2] = N; // number of X bands - N >= 1
0N/A *
0N/A * bands[rowstart+3] = X10; // starting X coordinate of first band
0N/A * bands[rowstart+4] = X11; // ending X coordinate of first band
0N/A * bands[rowstart+5] = X20; // starting X coordinate of second band
0N/A * bands[rowstart+6] = X21; // ending X coordinate of second band
0N/A * ...
0N/A * bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band
0N/A * bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band
0N/A *
0N/A * bands[rowstart+3+N*2] = ... // start of next Y row
0N/A * </pre>
0N/A */
0N/Apublic class Region {
0N/A static final int INIT_SIZE = 50;
0N/A static final int GROW_SIZE = 50;
0N/A
886N/A /**
886N/A * Immutable Region.
886N/A */
886N/A private static final class ImmutableRegion extends Region {
886N/A protected ImmutableRegion(int lox, int loy, int hix, int hiy) {
886N/A super(lox, loy, hix, hiy);
886N/A }
886N/A
886N/A // Override all the methods that mutate the object
886N/A public void appendSpans(sun.java2d.pipe.SpanIterator si) {}
886N/A public void setOutputArea(java.awt.Rectangle r) {}
886N/A public void setOutputAreaXYWH(int x, int y, int w, int h) {}
886N/A public void setOutputArea(int[] box) {}
886N/A public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {}
886N/A }
886N/A
886N/A public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0);
886N/A public static final Region WHOLE_REGION = new ImmutableRegion(
886N/A Integer.MIN_VALUE,
886N/A Integer.MIN_VALUE,
886N/A Integer.MAX_VALUE,
886N/A Integer.MAX_VALUE);
0N/A
0N/A int lox;
0N/A int loy;
0N/A int hix;
0N/A int hiy;
0N/A
0N/A int endIndex;
0N/A int[] bands;
0N/A
0N/A private static native void initIDs();
0N/A
0N/A static {
0N/A initIDs();
0N/A }
0N/A
0N/A /**
0N/A * Adds the dimension <code>dim</code> to the coordinate
0N/A * <code>start</code> with appropriate clipping. If
0N/A * <code>dim</code> is non-positive then the method returns
0N/A * the start coordinate. If the sum overflows an integer
0N/A * data type then the method returns <code>Integer.MAX_VALUE</code>.
0N/A */
0N/A public static int dimAdd(int start, int dim) {
0N/A if (dim <= 0) return start;
0N/A if ((dim += start) < start) return Integer.MAX_VALUE;
0N/A return dim;
0N/A }
0N/A
0N/A /**
0N/A * Adds the delta {@code dv} to the value {@code v} with
0N/A * appropriate clipping to the bounds of Integer resolution.
0N/A * If the answer would be greater than {@code Integer.MAX_VALUE}
0N/A * then {@code Integer.MAX_VALUE} is returned.
0N/A * If the answer would be less than {@code Integer.MIN_VALUE}
0N/A * then {@code Integer.MIN_VALUE} is returned.
0N/A * Otherwise the sum is returned.
0N/A */
0N/A public static int clipAdd(int v, int dv) {
0N/A int newv = v + dv;
0N/A if ((newv > v) != (dv > 0)) {
0N/A newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
0N/A }
0N/A return newv;
0N/A }
0N/A
6084N/A /**
6084N/A * Multiply the scale factor {@code sv} and the value {@code v} with
6084N/A * appropriate clipping to the bounds of Integer resolution. If the answer
6084N/A * would be greater than {@code Integer.MAX_VALUE} then {@code
6084N/A * Integer.MAX_VALUE} is returned. If the answer would be less than {@code
6084N/A * Integer.MIN_VALUE} then {@code Integer.MIN_VALUE} is returned. Otherwise
6084N/A * the multiplication is returned.
6084N/A */
6084N/A public static int clipScale(final int v, final double sv) {
6084N/A if (sv == 1.0) {
6084N/A return v;
6084N/A }
6084N/A final double newv = v * sv;
6084N/A if (newv < Integer.MIN_VALUE) {
6084N/A return Integer.MIN_VALUE;
6084N/A }
6084N/A if (newv > Integer.MAX_VALUE) {
6084N/A return Integer.MAX_VALUE;
6084N/A }
6084N/A return (int) Math.round(newv);
6084N/A }
6084N/A
886N/A protected Region(int lox, int loy, int hix, int hiy) {
0N/A this.lox = lox;
0N/A this.loy = loy;
0N/A this.hix = hix;
0N/A this.hiy = hiy;
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object covering the pixels which would be
0N/A * touched by a fill or clip operation on a Graphics implementation
0N/A * on the specified Shape object under the optionally specified
0N/A * AffineTransform object.
0N/A *
0N/A * @param s a non-null Shape object specifying the geometry enclosing
0N/A * the pixels of interest
0N/A * @param at an optional <code>AffineTransform</code> to be applied to the
0N/A * coordinates as they are returned in the iteration, or
0N/A * <code>null</code> if untransformed coordinates are desired
0N/A */
0N/A public static Region getInstance(Shape s, AffineTransform at) {
0N/A return getInstance(WHOLE_REGION, false, s, at);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object covering the pixels which would be
0N/A * touched by a fill or clip operation on a Graphics implementation
0N/A * on the specified Shape object under the optionally specified
0N/A * AffineTransform object further restricted by the specified
0N/A * device bounds.
0N/A * <p>
0N/A * Note that only the bounds of the specified Region are used to
0N/A * restrict the resulting Region.
0N/A * If devBounds is non-rectangular and clipping to the specific
0N/A * bands of devBounds is needed, then an intersection of the
0N/A * resulting Region with devBounds must be performed in a
0N/A * subsequent step.
0N/A *
0N/A * @param devBounds a non-null Region specifying some bounds to
0N/A * clip the geometry to
0N/A * @param s a non-null Shape object specifying the geometry enclosing
0N/A * the pixels of interest
0N/A * @param at an optional <code>AffineTransform</code> to be applied to the
0N/A * coordinates as they are returned in the iteration, or
0N/A * <code>null</code> if untransformed coordinates are desired
0N/A */
0N/A public static Region getInstance(Region devBounds,
0N/A Shape s, AffineTransform at)
0N/A {
0N/A return getInstance(devBounds, false, s, at);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object covering the pixels which would be
0N/A * touched by a fill or clip operation on a Graphics implementation
0N/A * on the specified Shape object under the optionally specified
0N/A * AffineTransform object further restricted by the specified
0N/A * device bounds.
0N/A * If the normalize parameter is true then coordinate normalization
0N/A * is performed as per the 2D Graphics non-antialiasing implementation
0N/A * of the VALUE_STROKE_NORMALIZE hint.
0N/A * <p>
0N/A * Note that only the bounds of the specified Region are used to
0N/A * restrict the resulting Region.
0N/A * If devBounds is non-rectangular and clipping to the specific
0N/A * bands of devBounds is needed, then an intersection of the
0N/A * resulting Region with devBounds must be performed in a
0N/A * subsequent step.
0N/A *
0N/A * @param devBounds a non-null Region specifying some bounds to
0N/A * clip the geometry to
0N/A * @param normalize a boolean indicating whether or not to apply
0N/A * normalization
0N/A * @param s a non-null Shape object specifying the geometry enclosing
0N/A * the pixels of interest
0N/A * @param at an optional <code>AffineTransform</code> to be applied to the
0N/A * coordinates as they are returned in the iteration, or
0N/A * <code>null</code> if untransformed coordinates are desired
0N/A */
0N/A public static Region getInstance(Region devBounds, boolean normalize,
0N/A Shape s, AffineTransform at)
0N/A {
886N/A // Optimize for empty shapes to avoid involving the SpanIterator
886N/A if (s instanceof RectangularShape &&
886N/A ((RectangularShape)s).isEmpty())
886N/A {
886N/A return EMPTY_REGION;
886N/A }
886N/A
0N/A int box[] = new int[4];
0N/A ShapeSpanIterator sr = new ShapeSpanIterator(normalize);
0N/A try {
0N/A sr.setOutputArea(devBounds);
0N/A sr.appendPath(s.getPathIterator(at));
0N/A sr.getPathBox(box);
0N/A Region r = Region.getInstance(box);
0N/A r.appendSpans(sr);
0N/A return r;
0N/A } finally {
0N/A sr.dispose();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object with a rectangle of interest specified
0N/A * by the indicated Rectangle object.
0N/A * <p>
0N/A * This method can also be used to create a simple rectangular
0N/A * region.
0N/A */
0N/A public static Region getInstance(Rectangle r) {
0N/A return Region.getInstanceXYWH(r.x, r.y, r.width, r.height);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object with a rectangle of interest specified
0N/A * by the indicated rectangular area in x, y, width, height format.
0N/A * <p>
0N/A * This method can also be used to create a simple rectangular
0N/A * region.
0N/A */
0N/A public static Region getInstanceXYWH(int x, int y, int w, int h) {
0N/A return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object with a rectangle of interest specified
0N/A * by the indicated span array.
0N/A * <p>
0N/A * This method can also be used to create a simple rectangular
0N/A * region.
0N/A */
0N/A public static Region getInstance(int box[]) {
0N/A return new Region(box[0], box[1], box[2], box[3]);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object with a rectangle of interest specified
0N/A * by the indicated rectangular area in lox, loy, hix, hiy format.
0N/A * <p>
0N/A * This method can also be used to create a simple rectangular
0N/A * region.
0N/A */
0N/A public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) {
0N/A return new Region(lox, loy, hix, hiy);
0N/A }
0N/A
0N/A /**
0N/A * Sets the rectangle of interest for storing and returning
0N/A * region bands.
0N/A * <p>
0N/A * This method can also be used to initialize a simple rectangular
0N/A * region.
0N/A */
0N/A public void setOutputArea(Rectangle r) {
0N/A setOutputAreaXYWH(r.x, r.y, r.width, r.height);
0N/A }
0N/A
0N/A /**
0N/A * Sets the rectangle of interest for storing and returning
0N/A * region bands. The rectangle is specified in x, y, width, height
0N/A * format and appropriate clipping is performed as per the method
0N/A * <code>dimAdd</code>.
0N/A * <p>
0N/A * This method can also be used to initialize a simple rectangular
0N/A * region.
0N/A */
0N/A public void setOutputAreaXYWH(int x, int y, int w, int h) {
0N/A setOutputAreaXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
0N/A }
0N/A
0N/A /**
0N/A * Sets the rectangle of interest for storing and returning
0N/A * region bands. The rectangle is specified as a span array.
0N/A * <p>
0N/A * This method can also be used to initialize a simple rectangular
0N/A * region.
0N/A */
0N/A public void setOutputArea(int box[]) {
0N/A this.lox = box[0];
0N/A this.loy = box[1];
0N/A this.hix = box[2];
0N/A this.hiy = box[3];
0N/A }
0N/A
0N/A /**
0N/A * Sets the rectangle of interest for storing and returning
0N/A * region bands. The rectangle is specified in lox, loy,
0N/A * hix, hiy format.
0N/A * <p>
0N/A * This method can also be used to initialize a simple rectangular
0N/A * region.
0N/A */
0N/A public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {
0N/A this.lox = lox;
0N/A this.loy = loy;
0N/A this.hix = hix;
0N/A this.hiy = hiy;
0N/A }
0N/A
0N/A /**
0N/A * Appends the list of spans returned from the indicated
0N/A * SpanIterator. Each span must be at a higher starting
0N/A * Y coordinate than the previous data or it must have a
0N/A * Y range equal to the highest Y band in the region and a
0N/A * higher X coordinate than any of the spans in that band.
0N/A */
0N/A public void appendSpans(SpanIterator si) {
0N/A int[] box = new int[6];
0N/A
0N/A while (si.nextSpan(box)) {
0N/A appendSpan(box);
0N/A }
0N/A
0N/A endRow(box);
0N/A calcBBox();
0N/A }
0N/A
0N/A /**
6084N/A * Returns a Region object that represents the same list of rectangles as
6084N/A * the current Region object, scaled by the specified sx, sy factors.
6084N/A */
6084N/A public Region getScaledRegion(final double sx, final double sy) {
6084N/A if (sx == 0 || sy == 0 || this == EMPTY_REGION) {
6084N/A return EMPTY_REGION;
6084N/A }
6084N/A if ((sx == 1.0 && sy == 1.0) || (this == WHOLE_REGION)) {
6084N/A return this;
6084N/A }
6084N/A
6084N/A int tlox = clipScale(lox, sx);
6084N/A int tloy = clipScale(loy, sy);
6084N/A int thix = clipScale(hix, sx);
6084N/A int thiy = clipScale(hiy, sy);
6084N/A Region ret = new Region(tlox, tloy, thix, thiy);
6084N/A int bands[] = this.bands;
6084N/A if (bands != null) {
6084N/A int end = endIndex;
6084N/A int newbands[] = new int[end];
6084N/A int i = 0; // index for source bands
6084N/A int j = 0; // index for translated newbands
6084N/A int ncol;
6084N/A while (i < end) {
6084N/A int y1, y2;
6084N/A newbands[j++] = y1 = clipScale(bands[i++], sy);
6084N/A newbands[j++] = y2 = clipScale(bands[i++], sy);
6084N/A newbands[j++] = ncol = bands[i++];
6084N/A int savej = j;
6084N/A if (y1 < y2) {
6084N/A while (--ncol >= 0) {
6084N/A int x1 = clipScale(bands[i++], sx);
6084N/A int x2 = clipScale(bands[i++], sx);
6084N/A if (x1 < x2) {
6084N/A newbands[j++] = x1;
6084N/A newbands[j++] = x2;
6084N/A }
6084N/A }
6084N/A } else {
6084N/A i += ncol * 2;
6084N/A }
6084N/A // Did we get any non-empty bands in this row?
6084N/A if (j > savej) {
6084N/A newbands[savej-1] = (j - savej) / 2;
6084N/A } else {
6084N/A j = savej - 3;
6084N/A }
6084N/A }
6084N/A if (j <= 5) {
6084N/A if (j < 5) {
6084N/A // No rows or bands were generated...
6084N/A ret.lox = ret.loy = ret.hix = ret.hiy = 0;
6084N/A } else {
6084N/A // Only generated one single rect in the end...
6084N/A ret.loy = newbands[0];
6084N/A ret.hiy = newbands[1];
6084N/A ret.lox = newbands[3];
6084N/A ret.hix = newbands[4];
6084N/A }
6084N/A // ret.endIndex and ret.bands were never initialized...
6084N/A // ret.endIndex = 0;
6084N/A // ret.newbands = null;
6084N/A } else {
6084N/A // Generated multiple bands and/or multiple rows...
6084N/A ret.endIndex = j;
6084N/A ret.bands = newbands;
6084N/A }
6084N/A }
6084N/A return ret;
6084N/A }
6084N/A
6084N/A
6084N/A /**
0N/A * Returns a Region object that represents the same list of
0N/A * rectangles as the current Region object, translated by
0N/A * the specified dx, dy translation factors.
0N/A */
0N/A public Region getTranslatedRegion(int dx, int dy) {
0N/A if ((dx | dy) == 0) {
0N/A return this;
0N/A }
0N/A int tlox = lox + dx;
0N/A int tloy = loy + dy;
0N/A int thix = hix + dx;
0N/A int thiy = hiy + dy;
0N/A if ((tlox > lox) != (dx > 0) ||
0N/A (tloy > loy) != (dy > 0) ||
0N/A (thix > hix) != (dx > 0) ||
0N/A (thiy > hiy) != (dy > 0))
0N/A {
0N/A return getSafeTranslatedRegion(dx, dy);
0N/A }
0N/A Region ret = new Region(tlox, tloy, thix, thiy);
0N/A int bands[] = this.bands;
0N/A if (bands != null) {
0N/A int end = endIndex;
0N/A ret.endIndex = end;
0N/A int newbands[] = new int[end];
0N/A ret.bands = newbands;
0N/A int i = 0;
0N/A int ncol;
0N/A while (i < end) {
0N/A newbands[i] = bands[i] + dy; i++;
0N/A newbands[i] = bands[i] + dy; i++;
0N/A newbands[i] = ncol = bands[i]; i++;
0N/A while (--ncol >= 0) {
0N/A newbands[i] = bands[i] + dx; i++;
0N/A newbands[i] = bands[i] + dx; i++;
0N/A }
0N/A }
0N/A }
0N/A return ret;
0N/A }
0N/A
0N/A private Region getSafeTranslatedRegion(int dx, int dy) {
0N/A int tlox = clipAdd(lox, dx);
0N/A int tloy = clipAdd(loy, dy);
0N/A int thix = clipAdd(hix, dx);
0N/A int thiy = clipAdd(hiy, dy);
0N/A Region ret = new Region(tlox, tloy, thix, thiy);
0N/A int bands[] = this.bands;
0N/A if (bands != null) {
0N/A int end = endIndex;
0N/A int newbands[] = new int[end];
0N/A int i = 0; // index for source bands
0N/A int j = 0; // index for translated newbands
0N/A int ncol;
0N/A while (i < end) {
0N/A int y1, y2;
0N/A newbands[j++] = y1 = clipAdd(bands[i++], dy);
0N/A newbands[j++] = y2 = clipAdd(bands[i++], dy);
0N/A newbands[j++] = ncol = bands[i++];
0N/A int savej = j;
0N/A if (y1 < y2) {
0N/A while (--ncol >= 0) {
0N/A int x1 = clipAdd(bands[i++], dx);
0N/A int x2 = clipAdd(bands[i++], dx);
0N/A if (x1 < x2) {
0N/A newbands[j++] = x1;
0N/A newbands[j++] = x2;
0N/A }
0N/A }
0N/A } else {
0N/A i += ncol * 2;
0N/A }
0N/A // Did we get any non-empty bands in this row?
0N/A if (j > savej) {
0N/A newbands[savej-1] = (j - savej) / 2;
0N/A } else {
0N/A j = savej - 3;
0N/A }
0N/A }
0N/A if (j <= 5) {
0N/A if (j < 5) {
0N/A // No rows or bands were generated...
0N/A ret.lox = ret.loy = ret.hix = ret.hiy = 0;
0N/A } else {
0N/A // Only generated one single rect in the end...
0N/A ret.loy = newbands[0];
0N/A ret.hiy = newbands[1];
0N/A ret.lox = newbands[3];
0N/A ret.hix = newbands[4];
0N/A }
0N/A // ret.endIndex and ret.bands were never initialized...
0N/A // ret.endIndex = 0;
0N/A // ret.newbands = null;
0N/A } else {
0N/A // Generated multiple bands and/or multiple rows...
0N/A ret.endIndex = j;
0N/A ret.bands = newbands;
0N/A }
0N/A }
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the intersection of
0N/A * this object with the specified Rectangle. The return value
0N/A * may be this same object if no clipping occurs.
0N/A */
0N/A public Region getIntersection(Rectangle r) {
0N/A return getIntersectionXYWH(r.x, r.y, r.width, r.height);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the intersection of
0N/A * this object with the specified rectangular area. The return
0N/A * value may be this same object if no clipping occurs.
0N/A */
0N/A public Region getIntersectionXYWH(int x, int y, int w, int h) {
0N/A return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the intersection of
0N/A * this object with the specified rectangular area. The return
0N/A * value may be this same object if no clipping occurs.
0N/A */
0N/A public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) {
0N/A if (isInsideXYXY(lox, loy, hix, hiy)) {
0N/A return this;
0N/A }
0N/A Region ret = new Region((lox < this.lox) ? this.lox : lox,
0N/A (loy < this.loy) ? this.loy : loy,
0N/A (hix > this.hix) ? this.hix : hix,
0N/A (hiy > this.hiy) ? this.hiy : hiy);
0N/A if (bands != null) {
0N/A ret.appendSpans(this.getSpanIterator());
0N/A }
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the intersection of this
0N/A * object with the specified Region object.
0N/A * <p>
0N/A * If {@code A} and {@code B} are both Region Objects and
0N/A * <code>C = A.getIntersection(B);</code> then a point will
0N/A * be contained in {@code C} iff it is contained in both
0N/A * {@code A} and {@code B}.
0N/A * <p>
0N/A * The return value may be this same object or the argument
0N/A * Region object if no clipping occurs.
0N/A */
0N/A public Region getIntersection(Region r) {
0N/A if (this.isInsideQuickCheck(r)) {
0N/A return this;
0N/A }
0N/A if (r.isInsideQuickCheck(this)) {
0N/A return r;
0N/A }
0N/A Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox,
0N/A (r.loy < this.loy) ? this.loy : r.loy,
0N/A (r.hix > this.hix) ? this.hix : r.hix,
0N/A (r.hiy > this.hiy) ? this.hiy : r.hiy);
0N/A if (!ret.isEmpty()) {
0N/A ret.filterSpans(this, r, INCLUDE_COMMON);
0N/A }
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the union of this
0N/A * object with the specified Region object.
0N/A * <p>
0N/A * If {@code A} and {@code B} are both Region Objects and
0N/A * <code>C = A.getUnion(B);</code> then a point will
0N/A * be contained in {@code C} iff it is contained in either
0N/A * {@code A} or {@code B}.
0N/A * <p>
0N/A * The return value may be this same object or the argument
0N/A * Region object if no augmentation occurs.
0N/A */
0N/A public Region getUnion(Region r) {
0N/A if (r.isEmpty() || r.isInsideQuickCheck(this)) {
0N/A return this;
0N/A }
0N/A if (this.isEmpty() || this.isInsideQuickCheck(r)) {
0N/A return r;
0N/A }
0N/A Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,
0N/A (r.loy > this.loy) ? this.loy : r.loy,
0N/A (r.hix < this.hix) ? this.hix : r.hix,
0N/A (r.hiy < this.hiy) ? this.hiy : r.hiy);
0N/A ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON);
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the difference of the
0N/A * specified Region object subtracted from this object.
0N/A * <p>
0N/A * If {@code A} and {@code B} are both Region Objects and
0N/A * <code>C = A.getDifference(B);</code> then a point will
0N/A * be contained in {@code C} iff it is contained in
0N/A * {@code A} but not contained in {@code B}.
0N/A * <p>
0N/A * The return value may be this same object or the argument
0N/A * Region object if no clipping occurs.
0N/A */
0N/A public Region getDifference(Region r) {
0N/A if (!r.intersectsQuickCheck(this)) {
0N/A return this;
0N/A }
0N/A if (this.isInsideQuickCheck(r)) {
0N/A return EMPTY_REGION;
0N/A }
0N/A Region ret = new Region(this.lox, this.loy, this.hix, this.hiy);
0N/A ret.filterSpans(this, r, INCLUDE_A);
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the exclusive or of this
0N/A * object with the specified Region object.
0N/A * <p>
0N/A * If {@code A} and {@code B} are both Region Objects and
0N/A * <code>C = A.getExclusiveOr(B);</code> then a point will
0N/A * be contained in {@code C} iff it is contained in either
0N/A * {@code A} or {@code B}, but not if it is contained in both.
0N/A * <p>
0N/A * The return value may be this same object or the argument
0N/A * Region object if either is empty.
0N/A */
0N/A public Region getExclusiveOr(Region r) {
0N/A if (r.isEmpty()) {
0N/A return this;
0N/A }
0N/A if (this.isEmpty()) {
0N/A return r;
0N/A }
0N/A Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,
0N/A (r.loy > this.loy) ? this.loy : r.loy,
0N/A (r.hix < this.hix) ? this.hix : r.hix,
0N/A (r.hiy < this.hiy) ? this.hiy : r.hiy);
0N/A ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B);
0N/A return ret;
0N/A }
0N/A
0N/A static final int INCLUDE_A = 1;
0N/A static final int INCLUDE_B = 2;
0N/A static final int INCLUDE_COMMON = 4;
0N/A
0N/A private void filterSpans(Region ra, Region rb, int flags) {
0N/A int abands[] = ra.bands;
0N/A int bbands[] = rb.bands;
0N/A if (abands == null) {
0N/A abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix};
0N/A }
0N/A if (bbands == null) {
0N/A bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix};
0N/A }
0N/A int box[] = new int[6];
0N/A int acolstart = 0;
0N/A int ay1 = abands[acolstart++];
0N/A int ay2 = abands[acolstart++];
0N/A int acolend = abands[acolstart++];
0N/A acolend = acolstart + 2 * acolend;
0N/A int bcolstart = 0;
0N/A int by1 = bbands[bcolstart++];
0N/A int by2 = bbands[bcolstart++];
0N/A int bcolend = bbands[bcolstart++];
0N/A bcolend = bcolstart + 2 * bcolend;
0N/A int y = loy;
0N/A while (y < hiy) {
0N/A if (y >= ay2) {
0N/A if (acolend < ra.endIndex) {
0N/A acolstart = acolend;
0N/A ay1 = abands[acolstart++];
0N/A ay2 = abands[acolstart++];
0N/A acolend = abands[acolstart++];
0N/A acolend = acolstart + 2 * acolend;
0N/A } else {
0N/A if ((flags & INCLUDE_B) == 0) break;
0N/A ay1 = ay2 = hiy;
0N/A }
0N/A continue;
0N/A }
0N/A if (y >= by2) {
0N/A if (bcolend < rb.endIndex) {
0N/A bcolstart = bcolend;
0N/A by1 = bbands[bcolstart++];
0N/A by2 = bbands[bcolstart++];
0N/A bcolend = bbands[bcolstart++];
0N/A bcolend = bcolstart + 2 * bcolend;
0N/A } else {
0N/A if ((flags & INCLUDE_A) == 0) break;
0N/A by1 = by2 = hiy;
0N/A }
0N/A continue;
0N/A }
0N/A int yend;
0N/A if (y < by1) {
0N/A if (y < ay1) {
0N/A y = Math.min(ay1, by1);
0N/A continue;
0N/A }
0N/A // We are in a set of rows that belong only to A
0N/A yend = Math.min(ay2, by1);
0N/A if ((flags & INCLUDE_A) != 0) {
0N/A box[1] = y;
0N/A box[3] = yend;
0N/A int acol = acolstart;
0N/A while (acol < acolend) {
0N/A box[0] = abands[acol++];
0N/A box[2] = abands[acol++];
0N/A appendSpan(box);
0N/A }
0N/A }
0N/A } else if (y < ay1) {
0N/A // We are in a set of rows that belong only to B
0N/A yend = Math.min(by2, ay1);
0N/A if ((flags & INCLUDE_B) != 0) {
0N/A box[1] = y;
0N/A box[3] = yend;
0N/A int bcol = bcolstart;
0N/A while (bcol < bcolend) {
0N/A box[0] = bbands[bcol++];
0N/A box[2] = bbands[bcol++];
0N/A appendSpan(box);
0N/A }
0N/A }
0N/A } else {
0N/A // We are in a set of rows that belong to both A and B
0N/A yend = Math.min(ay2, by2);
0N/A box[1] = y;
0N/A box[3] = yend;
0N/A int acol = acolstart;
0N/A int bcol = bcolstart;
0N/A int ax1 = abands[acol++];
0N/A int ax2 = abands[acol++];
0N/A int bx1 = bbands[bcol++];
0N/A int bx2 = bbands[bcol++];
0N/A int x = Math.min(ax1, bx1);
0N/A if (x < lox) x = lox;
0N/A while (x < hix) {
0N/A if (x >= ax2) {
0N/A if (acol < acolend) {
0N/A ax1 = abands[acol++];
0N/A ax2 = abands[acol++];
0N/A } else {
0N/A if ((flags & INCLUDE_B) == 0) break;
0N/A ax1 = ax2 = hix;
0N/A }
0N/A continue;
0N/A }
0N/A if (x >= bx2) {
0N/A if (bcol < bcolend) {
0N/A bx1 = bbands[bcol++];
0N/A bx2 = bbands[bcol++];
0N/A } else {
0N/A if ((flags & INCLUDE_A) == 0) break;
0N/A bx1 = bx2 = hix;
0N/A }
0N/A continue;
0N/A }
0N/A int xend;
0N/A boolean appendit;
0N/A if (x < bx1) {
0N/A if (x < ax1) {
0N/A xend = Math.min(ax1, bx1);
0N/A appendit = false;
0N/A } else {
0N/A xend = Math.min(ax2, bx1);
0N/A appendit = ((flags & INCLUDE_A) != 0);
0N/A }
0N/A } else if (x < ax1) {
0N/A xend = Math.min(ax1, bx2);
0N/A appendit = ((flags & INCLUDE_B) != 0);
0N/A } else {
0N/A xend = Math.min(ax2, bx2);
0N/A appendit = ((flags & INCLUDE_COMMON) != 0);
0N/A }
0N/A if (appendit) {
0N/A box[0] = x;
0N/A box[2] = xend;
0N/A appendSpan(box);
0N/A }
0N/A x = xend;
0N/A }
0N/A }
0N/A y = yend;
0N/A }
0N/A endRow(box);
0N/A calcBBox();
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the bounds of the
0N/A * intersection of this object with the bounds of the specified
0N/A * Region object.
0N/A * <p>
0N/A * The return value may be this same object if no clipping occurs
0N/A * and this Region is rectangular.
0N/A */
0N/A public Region getBoundsIntersection(Rectangle r) {
0N/A return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the bounds of the
0N/A * intersection of this object with the bounds of the specified
0N/A * rectangular area in x, y, width, height format.
0N/A * <p>
0N/A * The return value may be this same object if no clipping occurs
0N/A * and this Region is rectangular.
0N/A */
0N/A public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) {
0N/A return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the bounds of the
0N/A * intersection of this object with the bounds of the specified
0N/A * rectangular area in lox, loy, hix, hiy format.
0N/A * <p>
0N/A * The return value may be this same object if no clipping occurs
0N/A * and this Region is rectangular.
0N/A */
0N/A public Region getBoundsIntersectionXYXY(int lox, int loy,
0N/A int hix, int hiy)
0N/A {
0N/A if (this.bands == null &&
0N/A this.lox >= lox && this.loy >= loy &&
0N/A this.hix <= hix && this.hiy <= hiy)
0N/A {
0N/A return this;
0N/A }
0N/A return new Region((lox < this.lox) ? this.lox : lox,
0N/A (loy < this.loy) ? this.loy : loy,
0N/A (hix > this.hix) ? this.hix : hix,
0N/A (hiy > this.hiy) ? this.hiy : hiy);
0N/A }
0N/A
0N/A /**
0N/A * Returns a Region object that represents the intersection of
0N/A * this object with the bounds of the specified Region object.
0N/A * <p>
0N/A * The return value may be this same object or the argument
0N/A * Region object if no clipping occurs and the Regions are
0N/A * rectangular.
0N/A */
0N/A public Region getBoundsIntersection(Region r) {
0N/A if (this.encompasses(r)) {
0N/A return r;
0N/A }
0N/A if (r.encompasses(this)) {
0N/A return this;
0N/A }
0N/A return new Region((r.lox < this.lox) ? this.lox : r.lox,
0N/A (r.loy < this.loy) ? this.loy : r.loy,
0N/A (r.hix > this.hix) ? this.hix : r.hix,
0N/A (r.hiy > this.hiy) ? this.hiy : r.hiy);
0N/A }
0N/A
0N/A /**
0N/A * Appends a single span defined by the 4 parameters
0N/A * spanlox, spanloy, spanhix, spanhiy.
0N/A * This span must be at a higher starting Y coordinate than
0N/A * the previous data or it must have a Y range equal to the
0N/A * highest Y band in the region and a higher X coordinate
0N/A * than any of the spans in that band.
0N/A */
0N/A private void appendSpan(int box[]) {
0N/A int spanlox, spanloy, spanhix, spanhiy;
0N/A if ((spanlox = box[0]) < lox) spanlox = lox;
0N/A if ((spanloy = box[1]) < loy) spanloy = loy;
0N/A if ((spanhix = box[2]) > hix) spanhix = hix;
0N/A if ((spanhiy = box[3]) > hiy) spanhiy = hiy;
0N/A if (spanhix <= spanlox || spanhiy <= spanloy) {
0N/A return;
0N/A }
0N/A
0N/A int curYrow = box[4];
0N/A if (endIndex == 0 || spanloy >= bands[curYrow + 1]) {
0N/A if (bands == null) {
0N/A bands = new int[INIT_SIZE];
0N/A } else {
0N/A needSpace(5);
0N/A endRow(box);
0N/A curYrow = box[4];
0N/A }
0N/A bands[endIndex++] = spanloy;
0N/A bands[endIndex++] = spanhiy;
0N/A bands[endIndex++] = 0;
0N/A } else if (spanloy == bands[curYrow] &&
0N/A spanhiy == bands[curYrow + 1] &&
0N/A spanlox >= bands[endIndex - 1]) {
0N/A if (spanlox == bands[endIndex - 1]) {
0N/A bands[endIndex - 1] = spanhix;
0N/A return;
0N/A }
0N/A needSpace(2);
0N/A } else {
0N/A throw new InternalError("bad span");
0N/A }
0N/A bands[endIndex++] = spanlox;
0N/A bands[endIndex++] = spanhix;
0N/A bands[curYrow + 2]++;
0N/A }
0N/A
0N/A private void needSpace(int num) {
0N/A if (endIndex + num >= bands.length) {
0N/A int[] newbands = new int[bands.length + GROW_SIZE];
0N/A System.arraycopy(bands, 0, newbands, 0, endIndex);
0N/A bands = newbands;
0N/A }
0N/A }
0N/A
0N/A private void endRow(int box[]) {
0N/A int cur = box[4];
0N/A int prev = box[5];
0N/A if (cur > prev) {
0N/A int[] bands = this.bands;
0N/A if (bands[prev + 1] == bands[cur] &&
0N/A bands[prev + 2] == bands[cur + 2])
0N/A {
0N/A int num = bands[cur + 2] * 2;
0N/A cur += 3;
0N/A prev += 3;
0N/A while (num > 0) {
0N/A if (bands[cur++] != bands[prev++]) {
0N/A break;
0N/A }
0N/A num--;
0N/A }
0N/A if (num == 0) {
0N/A // prev == box[4]
0N/A bands[box[5] + 1] = bands[prev + 1];
0N/A endIndex = prev;
0N/A return;
0N/A }
0N/A }
0N/A }
0N/A box[5] = box[4];
0N/A box[4] = endIndex;
0N/A }
0N/A
0N/A private void calcBBox() {
0N/A int[] bands = this.bands;
0N/A if (endIndex <= 5) {
0N/A if (endIndex == 0) {
0N/A lox = loy = hix = hiy = 0;
0N/A } else {
0N/A loy = bands[0];
0N/A hiy = bands[1];
0N/A lox = bands[3];
0N/A hix = bands[4];
0N/A endIndex = 0;
0N/A }
0N/A this.bands = null;
0N/A return;
0N/A }
0N/A int lox = this.hix;
0N/A int hix = this.lox;
0N/A int hiyindex = 0;
0N/A
0N/A int i = 0;
0N/A while (i < endIndex) {
0N/A hiyindex = i;
0N/A int numbands = bands[i + 2];
0N/A i += 3;
0N/A if (lox > bands[i]) {
0N/A lox = bands[i];
0N/A }
0N/A i += numbands * 2;
0N/A if (hix < bands[i - 1]) {
0N/A hix = bands[i - 1];
0N/A }
0N/A }
0N/A
0N/A this.lox = lox;
0N/A this.loy = bands[0];
0N/A this.hix = hix;
0N/A this.hiy = bands[hiyindex + 1];
0N/A }
0N/A
0N/A /**
0N/A * Returns the lowest X coordinate in the Region.
0N/A */
0N/A public final int getLoX() {
0N/A return lox;
0N/A }
0N/A
0N/A /**
0N/A * Returns the lowest Y coordinate in the Region.
0N/A */
0N/A public final int getLoY() {
0N/A return loy;
0N/A }
0N/A
0N/A /**
0N/A * Returns the highest X coordinate in the Region.
0N/A */
0N/A public final int getHiX() {
0N/A return hix;
0N/A }
0N/A
0N/A /**
0N/A * Returns the highest Y coordinate in the Region.
0N/A */
0N/A public final int getHiY() {
0N/A return hiy;
0N/A }
0N/A
0N/A /**
0N/A * Returns the width of this Region clipped to the range (0 - MAX_INT).
0N/A */
0N/A public final int getWidth() {
0N/A if (hix < lox) return 0;
0N/A int w;
0N/A if ((w = hix - lox) < 0) {
0N/A w = Integer.MAX_VALUE;
0N/A }
0N/A return w;
0N/A }
0N/A
0N/A /**
0N/A * Returns the height of this Region clipped to the range (0 - MAX_INT).
0N/A */
0N/A public final int getHeight() {
0N/A if (hiy < loy) return 0;
0N/A int h;
0N/A if ((h = hiy - loy) < 0) {
0N/A h = Integer.MAX_VALUE;
0N/A }
0N/A return h;
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this Region encloses no area.
0N/A */
0N/A public boolean isEmpty() {
0N/A return (hix <= lox || hiy <= loy);
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this Region represents a single simple
0N/A * rectangular area.
0N/A */
0N/A public boolean isRectangular() {
0N/A return (bands == null);
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this Region contains the specified coordinate.
0N/A */
0N/A public boolean contains(int x, int y) {
0N/A if (x < lox || x >= hix || y < loy || y >= hiy) return false;
0N/A if (bands == null) return true;
0N/A int i = 0;
0N/A while (i < endIndex) {
0N/A if (y < bands[i++]) {
0N/A return false;
0N/A }
0N/A if (y >= bands[i++]) {
0N/A int numspans = bands[i++];
0N/A i += numspans * 2;
0N/A } else {
0N/A int end = bands[i++];
0N/A end = i + end * 2;
0N/A while (i < end) {
0N/A if (x < bands[i++]) return false;
0N/A if (x < bands[i++]) return true;
0N/A }
0N/A return false;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this Region lies inside the indicated
0N/A * rectangular area specified in x, y, width, height format
0N/A * with appropriate clipping performed as per the dimAdd method.
0N/A */
0N/A public boolean isInsideXYWH(int x, int y, int w, int h) {
0N/A return isInsideXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this Region lies inside the indicated
0N/A * rectangular area specified in lox, loy, hix, hiy format.
0N/A */
0N/A public boolean isInsideXYXY(int lox, int loy, int hix, int hiy) {
0N/A return (this.lox >= lox && this.loy >= loy &&
0N/A this.hix <= hix && this.hiy <= hiy);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Quickly checks if this Region lies inside the specified
0N/A * Region object.
0N/A * <p>
0N/A * This method will return false if the specified Region
0N/A * object is not a simple rectangle.
0N/A */
0N/A public boolean isInsideQuickCheck(Region r) {
0N/A return (r.bands == null &&
0N/A r.lox <= this.lox && r.loy <= this.loy &&
0N/A r.hix >= this.hix && r.hiy >= this.hiy);
0N/A }
0N/A
0N/A /**
0N/A * Quickly checks if this Region intersects the specified
0N/A * rectangular area specified in lox, loy, hix, hiy format.
0N/A * <p>
0N/A * This method tests only against the bounds of this region
0N/A * and does not bother to test if the rectangular region
0N/A * actually intersects any bands.
0N/A */
0N/A public boolean intersectsQuickCheckXYXY(int lox, int loy,
0N/A int hix, int hiy)
0N/A {
0N/A return (hix > this.lox && lox < this.hix &&
0N/A hiy > this.loy && loy < this.hiy);
0N/A }
0N/A
0N/A /**
0N/A * Quickly checks if this Region intersects the specified
0N/A * Region object.
0N/A * <p>
0N/A * This method tests only against the bounds of this region
0N/A * and does not bother to test if the rectangular region
0N/A * actually intersects any bands.
0N/A */
0N/A public boolean intersectsQuickCheck(Region r) {
0N/A return (r.hix > this.lox && r.lox < this.hix &&
0N/A r.hiy > this.loy && r.loy < this.hiy);
0N/A }
0N/A
0N/A /**
0N/A * Quickly checks if this Region surrounds the specified
0N/A * Region object.
0N/A * <p>
0N/A * This method will return false if this Region object is
0N/A * not a simple rectangle.
0N/A */
0N/A public boolean encompasses(Region r) {
0N/A return (this.bands == null &&
0N/A this.lox <= r.lox && this.loy <= r.loy &&
0N/A this.hix >= r.hix && this.hiy >= r.hiy);
0N/A }
0N/A
0N/A /**
0N/A * Quickly checks if this Region surrounds the specified
0N/A * rectangular area specified in x, y, width, height format.
0N/A * <p>
0N/A * This method will return false if this Region object is
0N/A * not a simple rectangle.
0N/A */
0N/A public boolean encompassesXYWH(int x, int y, int w, int h) {
0N/A return encompassesXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
0N/A }
0N/A
0N/A /**
0N/A * Quickly checks if this Region surrounds the specified
0N/A * rectangular area specified in lox, loy, hix, hiy format.
0N/A * <p>
0N/A * This method will return false if this Region object is
0N/A * not a simple rectangle.
0N/A */
0N/A public boolean encompassesXYXY(int lox, int loy, int hix, int hiy) {
0N/A return (this.bands == null &&
0N/A this.lox <= lox && this.loy <= loy &&
0N/A this.hix >= hix && this.hiy >= hiy);
0N/A }
0N/A
0N/A /**
0N/A * Gets the bbox of the available spans, clipped to the OutputArea.
0N/A */
0N/A public void getBounds(int pathbox[]) {
0N/A pathbox[0] = lox;
0N/A pathbox[1] = loy;
0N/A pathbox[2] = hix;
0N/A pathbox[3] = hiy;
0N/A }
0N/A
0N/A /**
0N/A * Clips the indicated bbox array to the bounds of this Region.
0N/A */
0N/A public void clipBoxToBounds(int bbox[]) {
0N/A if (bbox[0] < lox) bbox[0] = lox;
0N/A if (bbox[1] < loy) bbox[1] = loy;
0N/A if (bbox[2] > hix) bbox[2] = hix;
0N/A if (bbox[3] > hiy) bbox[3] = hiy;
0N/A }
0N/A
0N/A /**
0N/A * Gets an iterator object to iterate over the spans in this region.
0N/A */
0N/A public RegionIterator getIterator() {
0N/A return new RegionIterator(this);
0N/A }
0N/A
0N/A /**
0N/A * Gets a span iterator object that iterates over the spans in this region
0N/A */
0N/A public SpanIterator getSpanIterator() {
0N/A return new RegionSpanIterator(this);
0N/A }
0N/A
0N/A /**
0N/A * Gets a span iterator object that iterates over the spans in this region
0N/A * but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi).
0N/A */
0N/A public SpanIterator getSpanIterator(int bbox[]) {
0N/A SpanIterator result = getSpanIterator();
0N/A result.intersectClipBox(bbox[0], bbox[1], bbox[2], bbox[3]);
0N/A return result;
0N/A }
0N/A
0N/A /**
0N/A * Returns a SpanIterator that is the argument iterator filtered by
0N/A * this region.
0N/A */
0N/A public SpanIterator filter(SpanIterator si) {
0N/A if (bands == null) {
0N/A si.intersectClipBox(lox, loy, hix, hiy);
0N/A } else {
0N/A si = new RegionClipSpanIterator(this, si);
0N/A }
0N/A return si;
0N/A }
0N/A
0N/A public String toString() {
0N/A StringBuffer sb = new StringBuffer();
0N/A sb.append("Region[[");
0N/A sb.append(lox);
0N/A sb.append(", ");
0N/A sb.append(loy);
0N/A sb.append(" => ");
0N/A sb.append(hix);
0N/A sb.append(", ");
0N/A sb.append(hiy);
0N/A sb.append("]");
0N/A if (bands != null) {
0N/A int col = 0;
0N/A while (col < endIndex) {
0N/A sb.append("y{");
0N/A sb.append(bands[col++]);
0N/A sb.append(",");
0N/A sb.append(bands[col++]);
0N/A sb.append("}[");
0N/A int end = bands[col++];
0N/A end = col + end * 2;
0N/A while (col < end) {
0N/A sb.append("x(");
0N/A sb.append(bands[col++]);
0N/A sb.append(", ");
0N/A sb.append(bands[col++]);
0N/A sb.append(")");
0N/A }
0N/A sb.append("]");
0N/A }
0N/A }
0N/A sb.append("]");
0N/A return sb.toString();
0N/A }
0N/A
0N/A public int hashCode() {
0N/A return (isEmpty() ? 0 : (lox * 3 + loy * 5 + hix * 7 + hiy * 9));
0N/A }
0N/A
0N/A public boolean equals(Object o) {
0N/A if (!(o instanceof Region)) {
0N/A return false;
0N/A }
0N/A Region r = (Region) o;
0N/A if (this.isEmpty()) {
0N/A return r.isEmpty();
0N/A } else if (r.isEmpty()) {
0N/A return false;
0N/A }
0N/A if (r.lox != this.lox || r.loy != this.loy ||
886N/A r.hix != this.hix || r.hiy != this.hiy)
0N/A {
0N/A return false;
0N/A }
0N/A if (this.bands == null) {
0N/A return (r.bands == null);
0N/A } else if (r.bands == null) {
0N/A return false;
0N/A }
0N/A if (this.endIndex != r.endIndex) {
0N/A return false;
0N/A }
0N/A int abands[] = this.bands;
0N/A int bbands[] = r.bands;
0N/A for (int i = 0; i < endIndex; i++) {
0N/A if (abands[i] != bbands[i]) {
0N/A return false;
0N/A }
0N/A }
0N/A return true;
0N/A }
0N/A}