/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.java2d.pipe;

import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.RectangularShape;

import sun.java2d.loops.TransformHelper;

This class encapsulates a definition of a two dimensional region which consists of a number of Y ranges each containing multiple X bands.

A rectangular Region is allowed to have a null band list in which case the rectangular shape is defined by the bounding box parameters (lox, loy, hix, hiy).

The band list, if present, consists of a list of rows in ascending Y order, ending at endIndex which is the index beyond the end of the last row. Each row consists of at least 3 + 2n entries (n >= 1) where the first 3 entries specify the Y range as start, end, and the number of X ranges in that Y range. These 3 entries are followed by pairs of X coordinates in ascending order:

bands[rowstart+0] = Y0;        // starting Y coordinate
bands[rowstart+1] = Y1;        // ending Y coordinate - endY > startY
bands[rowstart+2] = N;         // number of X bands - N >= 1
bands[rowstart+3] = X10;       // starting X coordinate of first band
bands[rowstart+4] = X11;       // ending X coordinate of first band
bands[rowstart+5] = X20;       // starting X coordinate of second band
bands[rowstart+6] = X21;       // ending X coordinate of second band
...
bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band
bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band
bands[rowstart+3+N*2] = ...    // start of next Y row
/** * This class encapsulates a definition of a two dimensional region which * consists of a number of Y ranges each containing multiple X bands. * <p> * A rectangular Region is allowed to have a null band list in which * case the rectangular shape is defined by the bounding box parameters * (lox, loy, hix, hiy). * <p> * The band list, if present, consists of a list of rows in ascending Y * order, ending at endIndex which is the index beyond the end of the * last row. Each row consists of at least 3 + 2n entries (n >= 1) * where the first 3 entries specify the Y range as start, end, and * the number of X ranges in that Y range. These 3 entries are * followed by pairs of X coordinates in ascending order: * <pre> * bands[rowstart+0] = Y0; // starting Y coordinate * bands[rowstart+1] = Y1; // ending Y coordinate - endY > startY * bands[rowstart+2] = N; // number of X bands - N >= 1 * * bands[rowstart+3] = X10; // starting X coordinate of first band * bands[rowstart+4] = X11; // ending X coordinate of first band * bands[rowstart+5] = X20; // starting X coordinate of second band * bands[rowstart+6] = X21; // ending X coordinate of second band * ... * bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band * bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band * * bands[rowstart+3+N*2] = ... // start of next Y row * </pre> */
public class Region { static final int INIT_SIZE = 50; static final int GROW_SIZE = 50;
Immutable Region.
/** * Immutable Region. */
private static final class ImmutableRegion extends Region { protected ImmutableRegion(int lox, int loy, int hix, int hiy) { super(lox, loy, hix, hiy); } // Override all the methods that mutate the object public void appendSpans(sun.java2d.pipe.SpanIterator si) {} public void setOutputArea(java.awt.Rectangle r) {} public void setOutputAreaXYWH(int x, int y, int w, int h) {} public void setOutputArea(int[] box) {} public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {} } public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0); public static final Region WHOLE_REGION = new ImmutableRegion( Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); int lox; int loy; int hix; int hiy; int endIndex; int[] bands; private static native void initIDs(); static { initIDs(); }
Adds the dimension dim to the coordinate start with appropriate clipping. If dim is non-positive then the method returns the start coordinate. If the sum overflows an integer data type then the method returns Integer.MAX_VALUE.
/** * Adds the dimension <code>dim</code> to the coordinate * <code>start</code> with appropriate clipping. If * <code>dim</code> is non-positive then the method returns * the start coordinate. If the sum overflows an integer * data type then the method returns <code>Integer.MAX_VALUE</code>. */
public static int dimAdd(int start, int dim) { if (dim <= 0) return start; if ((dim += start) < start) return Integer.MAX_VALUE; return dim; }
Adds the delta dv to the value v with appropriate clipping to the bounds of Integer resolution. If the answer would be greater than Integer.MAX_VALUE then Integer.MAX_VALUE is returned. If the answer would be less than Integer.MIN_VALUE then Integer.MIN_VALUE is returned. Otherwise the sum is returned.
/** * Adds the delta {@code dv} to the value {@code v} with * appropriate clipping to the bounds of Integer resolution. * If the answer would be greater than {@code Integer.MAX_VALUE} * then {@code Integer.MAX_VALUE} is returned. * If the answer would be less than {@code Integer.MIN_VALUE} * then {@code Integer.MIN_VALUE} is returned. * Otherwise the sum is returned. */
public static int clipAdd(int v, int dv) { int newv = v + dv; if ((newv > v) != (dv > 0)) { newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; } return newv; }
Multiply the scale factor sv and the value v with appropriate clipping to the bounds of Integer resolution. If the answer would be greater than Integer.MAX_VALUE then Integer.MAX_VALUE is returned. If the answer would be less than Integer.MIN_VALUE then Integer.MIN_VALUE is returned. Otherwise the multiplication is returned.
/** * Multiply the scale factor {@code sv} and the value {@code v} with * appropriate clipping to the bounds of Integer resolution. If the answer * would be greater than {@code Integer.MAX_VALUE} then {@code * Integer.MAX_VALUE} is returned. If the answer would be less than {@code * Integer.MIN_VALUE} then {@code Integer.MIN_VALUE} is returned. Otherwise * the multiplication is returned. */
public static int clipScale(final int v, final double sv) { if (sv == 1.0) { return v; } final double newv = v * sv; if (newv < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } if (newv > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } return (int) Math.round(newv); } protected Region(int lox, int loy, int hix, int hiy) { this.lox = lox; this.loy = loy; this.hix = hix; this.hiy = hiy; } private Region(int lox, int loy, int hix, int hiy, int[] bands, int end) { this.lox = lox; this.loy = loy; this.hix = hix; this.hiy = hiy; this.bands = bands; this.endIndex = end; }
Returns a Region object covering the pixels which would be touched by a fill or clip operation on a Graphics implementation on the specified Shape object under the optionally specified AffineTransform object.
Params:
  • s – a non-null Shape object specifying the geometry enclosing the pixels of interest
  • at – an optional AffineTransform to be applied to the coordinates as they are returned in the iteration, or null if untransformed coordinates are desired
/** * Returns a Region object covering the pixels which would be * touched by a fill or clip operation on a Graphics implementation * on the specified Shape object under the optionally specified * AffineTransform object. * * @param s a non-null Shape object specifying the geometry enclosing * the pixels of interest * @param at an optional <code>AffineTransform</code> to be applied to the * coordinates as they are returned in the iteration, or * <code>null</code> if untransformed coordinates are desired */
public static Region getInstance(Shape s, AffineTransform at) { return getInstance(WHOLE_REGION, false, s, at); }
Returns a Region object covering the pixels which would be touched by a fill or clip operation on a Graphics implementation on the specified Shape object under the optionally specified AffineTransform object further restricted by the specified device bounds.

Note that only the bounds of the specified Region are used to restrict the resulting Region. If devBounds is non-rectangular and clipping to the specific bands of devBounds is needed, then an intersection of the resulting Region with devBounds must be performed in a subsequent step.

Params:
  • devBounds – a non-null Region specifying some bounds to clip the geometry to
  • s – a non-null Shape object specifying the geometry enclosing the pixels of interest
  • at – an optional AffineTransform to be applied to the coordinates as they are returned in the iteration, or null if untransformed coordinates are desired
/** * Returns a Region object covering the pixels which would be * touched by a fill or clip operation on a Graphics implementation * on the specified Shape object under the optionally specified * AffineTransform object further restricted by the specified * device bounds. * <p> * Note that only the bounds of the specified Region are used to * restrict the resulting Region. * If devBounds is non-rectangular and clipping to the specific * bands of devBounds is needed, then an intersection of the * resulting Region with devBounds must be performed in a * subsequent step. * * @param devBounds a non-null Region specifying some bounds to * clip the geometry to * @param s a non-null Shape object specifying the geometry enclosing * the pixels of interest * @param at an optional <code>AffineTransform</code> to be applied to the * coordinates as they are returned in the iteration, or * <code>null</code> if untransformed coordinates are desired */
public static Region getInstance(Region devBounds, Shape s, AffineTransform at) { return getInstance(devBounds, false, s, at); }
Returns a Region object covering the pixels which would be touched by a fill or clip operation on a Graphics implementation on the specified Shape object under the optionally specified AffineTransform object further restricted by the specified device bounds. If the normalize parameter is true then coordinate normalization is performed as per the 2D Graphics non-antialiasing implementation of the VALUE_STROKE_NORMALIZE hint.

Note that only the bounds of the specified Region are used to restrict the resulting Region. If devBounds is non-rectangular and clipping to the specific bands of devBounds is needed, then an intersection of the resulting Region with devBounds must be performed in a subsequent step.

Params:
  • devBounds – a non-null Region specifying some bounds to clip the geometry to
  • normalize – a boolean indicating whether or not to apply normalization
  • s – a non-null Shape object specifying the geometry enclosing the pixels of interest
  • at – an optional AffineTransform to be applied to the coordinates as they are returned in the iteration, or null if untransformed coordinates are desired
/** * Returns a Region object covering the pixels which would be * touched by a fill or clip operation on a Graphics implementation * on the specified Shape object under the optionally specified * AffineTransform object further restricted by the specified * device bounds. * If the normalize parameter is true then coordinate normalization * is performed as per the 2D Graphics non-antialiasing implementation * of the VALUE_STROKE_NORMALIZE hint. * <p> * Note that only the bounds of the specified Region are used to * restrict the resulting Region. * If devBounds is non-rectangular and clipping to the specific * bands of devBounds is needed, then an intersection of the * resulting Region with devBounds must be performed in a * subsequent step. * * @param devBounds a non-null Region specifying some bounds to * clip the geometry to * @param normalize a boolean indicating whether or not to apply * normalization * @param s a non-null Shape object specifying the geometry enclosing * the pixels of interest * @param at an optional <code>AffineTransform</code> to be applied to the * coordinates as they are returned in the iteration, or * <code>null</code> if untransformed coordinates are desired */
public static Region getInstance(Region devBounds, boolean normalize, Shape s, AffineTransform at) { // Optimize for empty shapes to avoid involving the SpanIterator if (s instanceof RectangularShape && ((RectangularShape)s).isEmpty()) { return EMPTY_REGION; } int box[] = new int[4]; ShapeSpanIterator sr = new ShapeSpanIterator(normalize); try { sr.setOutputArea(devBounds); sr.appendPath(s.getPathIterator(at)); sr.getPathBox(box); Region r = Region.getInstance(box); r.appendSpans(sr); return r; } finally { sr.dispose(); } }
Returns a Region object with a rectangle of interest specified by the indicated rectangular area in lox, loy, hix, hiy and edges array, which is located relative to the rectangular area. Edges array - 0,1 are y range, 2N,2N+1 are x ranges, 1 per y range.
See Also:
  • TransformHelper
/** * Returns a Region object with a rectangle of interest specified by the * indicated rectangular area in lox, loy, hix, hiy and edges array, which * is located relative to the rectangular area. Edges array - 0,1 are y * range, 2N,2N+1 are x ranges, 1 per y range. * * @see TransformHelper */
static Region getInstance(final int lox, final int loy, final int hix, final int hiy, final int[] edges) { final int y1 = edges[0]; final int y2 = edges[1]; if (hiy <= loy || hix <= lox || y2 <= y1) { return EMPTY_REGION; } // rowsNum * (3 + 1 * 2) final int[] bands = new int[(y2 - y1) * 5]; int end = 0; int index = 2; for (int y = y1; y < y2; ++y) { final int spanlox = Math.max(clipAdd(lox, edges[index++]), lox); final int spanhix = Math.min(clipAdd(lox, edges[index++]), hix); if (spanlox < spanhix) { final int spanloy = Math.max(clipAdd(loy, y), loy); final int spanhiy = Math.min(clipAdd(spanloy, 1), hiy); if (spanloy < spanhiy) { bands[end++] = spanloy; bands[end++] = spanhiy; bands[end++] = 1; // 1 span per row bands[end++] = spanlox; bands[end++] = spanhix; } } } return end != 0 ? new Region(lox, loy, hix, hiy, bands, end) : EMPTY_REGION; }
Returns a Region object with a rectangle of interest specified by the indicated Rectangle object.

This method can also be used to create a simple rectangular region.

/** * Returns a Region object with a rectangle of interest specified * by the indicated Rectangle object. * <p> * This method can also be used to create a simple rectangular * region. */
public static Region getInstance(Rectangle r) { return Region.getInstanceXYWH(r.x, r.y, r.width, r.height); }
Returns a Region object with a rectangle of interest specified by the indicated rectangular area in x, y, width, height format.

This method can also be used to create a simple rectangular region.

/** * Returns a Region object with a rectangle of interest specified * by the indicated rectangular area in x, y, width, height format. * <p> * This method can also be used to create a simple rectangular * region. */
public static Region getInstanceXYWH(int x, int y, int w, int h) { return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); }
Returns a Region object with a rectangle of interest specified by the indicated span array.

This method can also be used to create a simple rectangular region.

/** * Returns a Region object with a rectangle of interest specified * by the indicated span array. * <p> * This method can also be used to create a simple rectangular * region. */
public static Region getInstance(int box[]) { return new Region(box[0], box[1], box[2], box[3]); }
Returns a Region object with a rectangle of interest specified by the indicated rectangular area in lox, loy, hix, hiy format.

This method can also be used to create a simple rectangular region.

/** * Returns a Region object with a rectangle of interest specified * by the indicated rectangular area in lox, loy, hix, hiy format. * <p> * This method can also be used to create a simple rectangular * region. */
public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) { return new Region(lox, loy, hix, hiy); }
Sets the rectangle of interest for storing and returning region bands.

This method can also be used to initialize a simple rectangular region.

/** * Sets the rectangle of interest for storing and returning * region bands. * <p> * This method can also be used to initialize a simple rectangular * region. */
public void setOutputArea(Rectangle r) { setOutputAreaXYWH(r.x, r.y, r.width, r.height); }
Sets the rectangle of interest for storing and returning region bands. The rectangle is specified in x, y, width, height format and appropriate clipping is performed as per the method dimAdd.

This method can also be used to initialize a simple rectangular region.

/** * Sets the rectangle of interest for storing and returning * region bands. The rectangle is specified in x, y, width, height * format and appropriate clipping is performed as per the method * <code>dimAdd</code>. * <p> * This method can also be used to initialize a simple rectangular * region. */
public void setOutputAreaXYWH(int x, int y, int w, int h) { setOutputAreaXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); }
Sets the rectangle of interest for storing and returning region bands. The rectangle is specified as a span array.

This method can also be used to initialize a simple rectangular region.

/** * Sets the rectangle of interest for storing and returning * region bands. The rectangle is specified as a span array. * <p> * This method can also be used to initialize a simple rectangular * region. */
public void setOutputArea(int box[]) { this.lox = box[0]; this.loy = box[1]; this.hix = box[2]; this.hiy = box[3]; }
Sets the rectangle of interest for storing and returning region bands. The rectangle is specified in lox, loy, hix, hiy format.

This method can also be used to initialize a simple rectangular region.

/** * Sets the rectangle of interest for storing and returning * region bands. The rectangle is specified in lox, loy, * hix, hiy format. * <p> * This method can also be used to initialize a simple rectangular * region. */
public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) { this.lox = lox; this.loy = loy; this.hix = hix; this.hiy = hiy; }
Appends the list of spans returned from the indicated SpanIterator. Each span must be at a higher starting Y coordinate than the previous data or it must have a Y range equal to the highest Y band in the region and a higher X coordinate than any of the spans in that band.
/** * Appends the list of spans returned from the indicated * SpanIterator. Each span must be at a higher starting * Y coordinate than the previous data or it must have a * Y range equal to the highest Y band in the region and a * higher X coordinate than any of the spans in that band. */
public void appendSpans(SpanIterator si) { int[] box = new int[6]; while (si.nextSpan(box)) { appendSpan(box); } endRow(box); calcBBox(); }
Returns a Region object that represents the same list of rectangles as the current Region object, scaled by the specified sx, sy factors.
/** * Returns a Region object that represents the same list of rectangles as * the current Region object, scaled by the specified sx, sy factors. */
public Region getScaledRegion(final double sx, final double sy) { if (sx == 0 || sy == 0 || this == EMPTY_REGION) { return EMPTY_REGION; } if ((sx == 1.0 && sy == 1.0) || (this == WHOLE_REGION)) { return this; } int tlox = clipScale(lox, sx); int tloy = clipScale(loy, sy); int thix = clipScale(hix, sx); int thiy = clipScale(hiy, sy); Region ret = new Region(tlox, tloy, thix, thiy); int bands[] = this.bands; if (bands != null) { int end = endIndex; int newbands[] = new int[end]; int i = 0; // index for source bands int j = 0; // index for translated newbands int ncol; while (i < end) { int y1, y2; newbands[j++] = y1 = clipScale(bands[i++], sy); newbands[j++] = y2 = clipScale(bands[i++], sy); newbands[j++] = ncol = bands[i++]; int savej = j; if (y1 < y2) { while (--ncol >= 0) { int x1 = clipScale(bands[i++], sx); int x2 = clipScale(bands[i++], sx); if (x1 < x2) { newbands[j++] = x1; newbands[j++] = x2; } } } else { i += ncol * 2; } // Did we get any non-empty bands in this row? if (j > savej) { newbands[savej-1] = (j - savej) / 2; } else { j = savej - 3; } } if (j <= 5) { if (j < 5) { // No rows or bands were generated... ret.lox = ret.loy = ret.hix = ret.hiy = 0; } else { // Only generated one single rect in the end... ret.loy = newbands[0]; ret.hiy = newbands[1]; ret.lox = newbands[3]; ret.hix = newbands[4]; } // ret.endIndex and ret.bands were never initialized... // ret.endIndex = 0; // ret.newbands = null; } else { // Generated multiple bands and/or multiple rows... ret.endIndex = j; ret.bands = newbands; } } return ret; }
Returns a Region object that represents the same list of rectangles as the current Region object, translated by the specified dx, dy translation factors.
/** * Returns a Region object that represents the same list of * rectangles as the current Region object, translated by * the specified dx, dy translation factors. */
public Region getTranslatedRegion(int dx, int dy) { if ((dx | dy) == 0) { return this; } int tlox = lox + dx; int tloy = loy + dy; int thix = hix + dx; int thiy = hiy + dy; if ((tlox > lox) != (dx > 0) || (tloy > loy) != (dy > 0) || (thix > hix) != (dx > 0) || (thiy > hiy) != (dy > 0)) { return getSafeTranslatedRegion(dx, dy); } Region ret = new Region(tlox, tloy, thix, thiy); int bands[] = this.bands; if (bands != null) { int end = endIndex; ret.endIndex = end; int newbands[] = new int[end]; ret.bands = newbands; int i = 0; int ncol; while (i < end) { newbands[i] = bands[i] + dy; i++; newbands[i] = bands[i] + dy; i++; newbands[i] = ncol = bands[i]; i++; while (--ncol >= 0) { newbands[i] = bands[i] + dx; i++; newbands[i] = bands[i] + dx; i++; } } } return ret; } private Region getSafeTranslatedRegion(int dx, int dy) { int tlox = clipAdd(lox, dx); int tloy = clipAdd(loy, dy); int thix = clipAdd(hix, dx); int thiy = clipAdd(hiy, dy); Region ret = new Region(tlox, tloy, thix, thiy); int bands[] = this.bands; if (bands != null) { int end = endIndex; int newbands[] = new int[end]; int i = 0; // index for source bands int j = 0; // index for translated newbands int ncol; while (i < end) { int y1, y2; newbands[j++] = y1 = clipAdd(bands[i++], dy); newbands[j++] = y2 = clipAdd(bands[i++], dy); newbands[j++] = ncol = bands[i++]; int savej = j; if (y1 < y2) { while (--ncol >= 0) { int x1 = clipAdd(bands[i++], dx); int x2 = clipAdd(bands[i++], dx); if (x1 < x2) { newbands[j++] = x1; newbands[j++] = x2; } } } else { i += ncol * 2; } // Did we get any non-empty bands in this row? if (j > savej) { newbands[savej-1] = (j - savej) / 2; } else { j = savej - 3; } } if (j <= 5) { if (j < 5) { // No rows or bands were generated... ret.lox = ret.loy = ret.hix = ret.hiy = 0; } else { // Only generated one single rect in the end... ret.loy = newbands[0]; ret.hiy = newbands[1]; ret.lox = newbands[3]; ret.hix = newbands[4]; } // ret.endIndex and ret.bands were never initialized... // ret.endIndex = 0; // ret.newbands = null; } else { // Generated multiple bands and/or multiple rows... ret.endIndex = j; ret.bands = newbands; } } return ret; }
Returns a Region object that represents the intersection of this object with the specified Rectangle. The return value may be this same object if no clipping occurs.
/** * Returns a Region object that represents the intersection of * this object with the specified Rectangle. The return value * may be this same object if no clipping occurs. */
public Region getIntersection(Rectangle r) { return getIntersectionXYWH(r.x, r.y, r.width, r.height); }
Returns a Region object that represents the intersection of this object with the specified rectangular area. The return value may be this same object if no clipping occurs.
/** * Returns a Region object that represents the intersection of * this object with the specified rectangular area. The return * value may be this same object if no clipping occurs. */
public Region getIntersectionXYWH(int x, int y, int w, int h) { return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); }
Returns a Region object that represents the intersection of this object with the specified rectangular area. The return value may be this same object if no clipping occurs.
/** * Returns a Region object that represents the intersection of * this object with the specified rectangular area. The return * value may be this same object if no clipping occurs. */
public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) { if (isInsideXYXY(lox, loy, hix, hiy)) { return this; } Region ret = new Region((lox < this.lox) ? this.lox : lox, (loy < this.loy) ? this.loy : loy, (hix > this.hix) ? this.hix : hix, (hiy > this.hiy) ? this.hiy : hiy); if (bands != null) { ret.appendSpans(this.getSpanIterator()); } return ret; }
Returns a Region object that represents the intersection of this object with the specified Region object.

If A and B are both Region Objects and C = A.getIntersection(B); then a point will be contained in C iff it is contained in both A and B.

The return value may be this same object or the argument Region object if no clipping occurs.

/** * Returns a Region object that represents the intersection of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getIntersection(B);</code> then a point will * be contained in {@code C} iff it is contained in both * {@code A} and {@code B}. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs. */
public Region getIntersection(Region r) { if (this.isInsideQuickCheck(r)) { return this; } if (r.isInsideQuickCheck(this)) { return r; } Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox, (r.loy < this.loy) ? this.loy : r.loy, (r.hix > this.hix) ? this.hix : r.hix, (r.hiy > this.hiy) ? this.hiy : r.hiy); if (!ret.isEmpty()) { ret.filterSpans(this, r, INCLUDE_COMMON); } return ret; }
Returns a Region object that represents the union of this object with the specified Region object.

If A and B are both Region Objects and C = A.getUnion(B); then a point will be contained in C iff it is contained in either A or B.

The return value may be this same object or the argument Region object if no augmentation occurs.

/** * Returns a Region object that represents the union of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getUnion(B);</code> then a point will * be contained in {@code C} iff it is contained in either * {@code A} or {@code B}. * <p> * The return value may be this same object or the argument * Region object if no augmentation occurs. */
public Region getUnion(Region r) { if (r.isEmpty() || r.isInsideQuickCheck(this)) { return this; } if (this.isEmpty() || this.isInsideQuickCheck(r)) { return r; } Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, (r.loy > this.loy) ? this.loy : r.loy, (r.hix < this.hix) ? this.hix : r.hix, (r.hiy < this.hiy) ? this.hiy : r.hiy); ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON); return ret; }
Returns a Region object that represents the difference of the specified Region object subtracted from this object.

If A and B are both Region Objects and C = A.getDifference(B); then a point will be contained in C iff it is contained in A but not contained in B.

The return value may be this same object or the argument Region object if no clipping occurs.

/** * Returns a Region object that represents the difference of the * specified Region object subtracted from this object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getDifference(B);</code> then a point will * be contained in {@code C} iff it is contained in * {@code A} but not contained in {@code B}. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs. */
public Region getDifference(Region r) { if (!r.intersectsQuickCheck(this)) { return this; } if (this.isInsideQuickCheck(r)) { return EMPTY_REGION; } Region ret = new Region(this.lox, this.loy, this.hix, this.hiy); ret.filterSpans(this, r, INCLUDE_A); return ret; }
Returns a Region object that represents the exclusive or of this object with the specified Region object.

If A and B are both Region Objects and C = A.getExclusiveOr(B); then a point will be contained in C iff it is contained in either A or B, but not if it is contained in both.

The return value may be this same object or the argument Region object if either is empty.

/** * Returns a Region object that represents the exclusive or of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getExclusiveOr(B);</code> then a point will * be contained in {@code C} iff it is contained in either * {@code A} or {@code B}, but not if it is contained in both. * <p> * The return value may be this same object or the argument * Region object if either is empty. */
public Region getExclusiveOr(Region r) { if (r.isEmpty()) { return this; } if (this.isEmpty()) { return r; } Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, (r.loy > this.loy) ? this.loy : r.loy, (r.hix < this.hix) ? this.hix : r.hix, (r.hiy < this.hiy) ? this.hiy : r.hiy); ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B); return ret; } static final int INCLUDE_A = 1; static final int INCLUDE_B = 2; static final int INCLUDE_COMMON = 4; private void filterSpans(Region ra, Region rb, int flags) { int abands[] = ra.bands; int bbands[] = rb.bands; if (abands == null) { abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix}; } if (bbands == null) { bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix}; } int box[] = new int[6]; int acolstart = 0; int ay1 = abands[acolstart++]; int ay2 = abands[acolstart++]; int acolend = abands[acolstart++]; acolend = acolstart + 2 * acolend; int bcolstart = 0; int by1 = bbands[bcolstart++]; int by2 = bbands[bcolstart++]; int bcolend = bbands[bcolstart++]; bcolend = bcolstart + 2 * bcolend; int y = loy; while (y < hiy) { if (y >= ay2) { if (acolend < ra.endIndex) { acolstart = acolend; ay1 = abands[acolstart++]; ay2 = abands[acolstart++]; acolend = abands[acolstart++]; acolend = acolstart + 2 * acolend; } else { if ((flags & INCLUDE_B) == 0) break; ay1 = ay2 = hiy; } continue; } if (y >= by2) { if (bcolend < rb.endIndex) { bcolstart = bcolend; by1 = bbands[bcolstart++]; by2 = bbands[bcolstart++]; bcolend = bbands[bcolstart++]; bcolend = bcolstart + 2 * bcolend; } else { if ((flags & INCLUDE_A) == 0) break; by1 = by2 = hiy; } continue; } int yend; if (y < by1) { if (y < ay1) { y = Math.min(ay1, by1); continue; } // We are in a set of rows that belong only to A yend = Math.min(ay2, by1); if ((flags & INCLUDE_A) != 0) { box[1] = y; box[3] = yend; int acol = acolstart; while (acol < acolend) { box[0] = abands[acol++]; box[2] = abands[acol++]; appendSpan(box); } } } else if (y < ay1) { // We are in a set of rows that belong only to B yend = Math.min(by2, ay1); if ((flags & INCLUDE_B) != 0) { box[1] = y; box[3] = yend; int bcol = bcolstart; while (bcol < bcolend) { box[0] = bbands[bcol++]; box[2] = bbands[bcol++]; appendSpan(box); } } } else { // We are in a set of rows that belong to both A and B yend = Math.min(ay2, by2); box[1] = y; box[3] = yend; int acol = acolstart; int bcol = bcolstart; int ax1 = abands[acol++]; int ax2 = abands[acol++]; int bx1 = bbands[bcol++]; int bx2 = bbands[bcol++]; int x = Math.min(ax1, bx1); if (x < lox) x = lox; while (x < hix) { if (x >= ax2) { if (acol < acolend) { ax1 = abands[acol++]; ax2 = abands[acol++]; } else { if ((flags & INCLUDE_B) == 0) break; ax1 = ax2 = hix; } continue; } if (x >= bx2) { if (bcol < bcolend) { bx1 = bbands[bcol++]; bx2 = bbands[bcol++]; } else { if ((flags & INCLUDE_A) == 0) break; bx1 = bx2 = hix; } continue; } int xend; boolean appendit; if (x < bx1) { if (x < ax1) { xend = Math.min(ax1, bx1); appendit = false; } else { xend = Math.min(ax2, bx1); appendit = ((flags & INCLUDE_A) != 0); } } else if (x < ax1) { xend = Math.min(ax1, bx2); appendit = ((flags & INCLUDE_B) != 0); } else { xend = Math.min(ax2, bx2); appendit = ((flags & INCLUDE_COMMON) != 0); } if (appendit) { box[0] = x; box[2] = xend; appendSpan(box); } x = xend; } } y = yend; } endRow(box); calcBBox(); }
Returns a Region object that represents the bounds of the intersection of this object with the bounds of the specified Region object.

The return value may be this same object if no clipping occurs and this Region is rectangular.

/** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * Region object. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */
public Region getBoundsIntersection(Rectangle r) { return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height); }
Returns a Region object that represents the bounds of the intersection of this object with the bounds of the specified rectangular area in x, y, width, height format.

The return value may be this same object if no clipping occurs and this Region is rectangular.

/** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * rectangular area in x, y, width, height format. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */
public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) { return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); }
Returns a Region object that represents the bounds of the intersection of this object with the bounds of the specified rectangular area in lox, loy, hix, hiy format.

The return value may be this same object if no clipping occurs and this Region is rectangular.

/** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * rectangular area in lox, loy, hix, hiy format. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */
public Region getBoundsIntersectionXYXY(int lox, int loy, int hix, int hiy) { if (this.bands == null && this.lox >= lox && this.loy >= loy && this.hix <= hix && this.hiy <= hiy) { return this; } return new Region((lox < this.lox) ? this.lox : lox, (loy < this.loy) ? this.loy : loy, (hix > this.hix) ? this.hix : hix, (hiy > this.hiy) ? this.hiy : hiy); }
Returns a Region object that represents the intersection of this object with the bounds of the specified Region object.

The return value may be this same object or the argument Region object if no clipping occurs and the Regions are rectangular.

/** * Returns a Region object that represents the intersection of * this object with the bounds of the specified Region object. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs and the Regions are * rectangular. */
public Region getBoundsIntersection(Region r) { if (this.encompasses(r)) { return r; } if (r.encompasses(this)) { return this; } return new Region((r.lox < this.lox) ? this.lox : r.lox, (r.loy < this.loy) ? this.loy : r.loy, (r.hix > this.hix) ? this.hix : r.hix, (r.hiy > this.hiy) ? this.hiy : r.hiy); }
Appends a single span defined by the 4 parameters spanlox, spanloy, spanhix, spanhiy. This span must be at a higher starting Y coordinate than the previous data or it must have a Y range equal to the highest Y band in the region and a higher X coordinate than any of the spans in that band.
/** * Appends a single span defined by the 4 parameters * spanlox, spanloy, spanhix, spanhiy. * This span must be at a higher starting Y coordinate than * the previous data or it must have a Y range equal to the * highest Y band in the region and a higher X coordinate * than any of the spans in that band. */
private void appendSpan(int box[]) { int spanlox, spanloy, spanhix, spanhiy; if ((spanlox = box[0]) < lox) spanlox = lox; if ((spanloy = box[1]) < loy) spanloy = loy; if ((spanhix = box[2]) > hix) spanhix = hix; if ((spanhiy = box[3]) > hiy) spanhiy = hiy; if (spanhix <= spanlox || spanhiy <= spanloy) { return; } int curYrow = box[4]; if (endIndex == 0 || spanloy >= bands[curYrow + 1]) { if (bands == null) { bands = new int[INIT_SIZE]; } else { needSpace(5); endRow(box); curYrow = box[4]; } bands[endIndex++] = spanloy; bands[endIndex++] = spanhiy; bands[endIndex++] = 0; } else if (spanloy == bands[curYrow] && spanhiy == bands[curYrow + 1] && spanlox >= bands[endIndex - 1]) { if (spanlox == bands[endIndex - 1]) { bands[endIndex - 1] = spanhix; return; } needSpace(2); } else { throw new InternalError("bad span"); } bands[endIndex++] = spanlox; bands[endIndex++] = spanhix; bands[curYrow + 2]++; } private void needSpace(int num) { if (endIndex + num >= bands.length) { int[] newbands = new int[bands.length + GROW_SIZE]; System.arraycopy(bands, 0, newbands, 0, endIndex); bands = newbands; } } private void endRow(int box[]) { int cur = box[4]; int prev = box[5]; if (cur > prev) { int[] bands = this.bands; if (bands[prev + 1] == bands[cur] && bands[prev + 2] == bands[cur + 2]) { int num = bands[cur + 2] * 2; cur += 3; prev += 3; while (num > 0) { if (bands[cur++] != bands[prev++]) { break; } num--; } if (num == 0) { // prev == box[4] bands[box[5] + 1] = bands[prev + 1]; endIndex = prev; return; } } } box[5] = box[4]; box[4] = endIndex; } private void calcBBox() { int[] bands = this.bands; if (endIndex <= 5) { if (endIndex == 0) { lox = loy = hix = hiy = 0; } else { loy = bands[0]; hiy = bands[1]; lox = bands[3]; hix = bands[4]; endIndex = 0; } this.bands = null; return; } int lox = this.hix; int hix = this.lox; int hiyindex = 0; int i = 0; while (i < endIndex) { hiyindex = i; int numbands = bands[i + 2]; i += 3; if (lox > bands[i]) { lox = bands[i]; } i += numbands * 2; if (hix < bands[i - 1]) { hix = bands[i - 1]; } } this.lox = lox; this.loy = bands[0]; this.hix = hix; this.hiy = bands[hiyindex + 1]; }
Returns the lowest X coordinate in the Region.
/** * Returns the lowest X coordinate in the Region. */
public final int getLoX() { return lox; }
Returns the lowest Y coordinate in the Region.
/** * Returns the lowest Y coordinate in the Region. */
public final int getLoY() { return loy; }
Returns the highest X coordinate in the Region.
/** * Returns the highest X coordinate in the Region. */
public final int getHiX() { return hix; }
Returns the highest Y coordinate in the Region.
/** * Returns the highest Y coordinate in the Region. */
public final int getHiY() { return hiy; }
Returns the width of this Region clipped to the range (0 - MAX_INT).
/** * Returns the width of this Region clipped to the range (0 - MAX_INT). */
public final int getWidth() { if (hix < lox) return 0; int w; if ((w = hix - lox) < 0) { w = Integer.MAX_VALUE; } return w; }
Returns the height of this Region clipped to the range (0 - MAX_INT).
/** * Returns the height of this Region clipped to the range (0 - MAX_INT). */
public final int getHeight() { if (hiy < loy) return 0; int h; if ((h = hiy - loy) < 0) { h = Integer.MAX_VALUE; } return h; }
Returns true iff this Region encloses no area.
/** * Returns true iff this Region encloses no area. */
public boolean isEmpty() { return (hix <= lox || hiy <= loy); }
Returns true iff this Region represents a single simple rectangular area.
/** * Returns true iff this Region represents a single simple * rectangular area. */
public boolean isRectangular() { return (bands == null); }
Returns true iff this Region contains the specified coordinate.
/** * Returns true iff this Region contains the specified coordinate. */
public boolean contains(int x, int y) { if (x < lox || x >= hix || y < loy || y >= hiy) return false; if (bands == null) return true; int i = 0; while (i < endIndex) { if (y < bands[i++]) { return false; } if (y >= bands[i++]) { int numspans = bands[i++]; i += numspans * 2; } else { int end = bands[i++]; end = i + end * 2; while (i < end) { if (x < bands[i++]) return false; if (x < bands[i++]) return true; } return false; } } return false; }
Returns true iff this Region lies inside the indicated rectangular area specified in x, y, width, height format with appropriate clipping performed as per the dimAdd method.
/** * Returns true iff this Region lies inside the indicated * rectangular area specified in x, y, width, height format * with appropriate clipping performed as per the dimAdd method. */
public boolean isInsideXYWH(int x, int y, int w, int h) { return isInsideXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); }
Returns true iff this Region lies inside the indicated rectangular area specified in lox, loy, hix, hiy format.
/** * Returns true iff this Region lies inside the indicated * rectangular area specified in lox, loy, hix, hiy format. */
public boolean isInsideXYXY(int lox, int loy, int hix, int hiy) { return (this.lox >= lox && this.loy >= loy && this.hix <= hix && this.hiy <= hiy); }
Quickly checks if this Region lies inside the specified Region object.

This method will return false if the specified Region object is not a simple rectangle.

/** * Quickly checks if this Region lies inside the specified * Region object. * <p> * This method will return false if the specified Region * object is not a simple rectangle. */
public boolean isInsideQuickCheck(Region r) { return (r.bands == null && r.lox <= this.lox && r.loy <= this.loy && r.hix >= this.hix && r.hiy >= this.hiy); }
Quickly checks if this Region intersects the specified rectangular area specified in lox, loy, hix, hiy format.

This method tests only against the bounds of this region and does not bother to test if the rectangular region actually intersects any bands.

/** * Quickly checks if this Region intersects the specified * rectangular area specified in lox, loy, hix, hiy format. * <p> * This method tests only against the bounds of this region * and does not bother to test if the rectangular region * actually intersects any bands. */
public boolean intersectsQuickCheckXYXY(int lox, int loy, int hix, int hiy) { return (hix > this.lox && lox < this.hix && hiy > this.loy && loy < this.hiy); }
Quickly checks if this Region intersects the specified Region object.

This method tests only against the bounds of this region and does not bother to test if the rectangular region actually intersects any bands.

/** * Quickly checks if this Region intersects the specified * Region object. * <p> * This method tests only against the bounds of this region * and does not bother to test if the rectangular region * actually intersects any bands. */
public boolean intersectsQuickCheck(Region r) { return (r.hix > this.lox && r.lox < this.hix && r.hiy > this.loy && r.loy < this.hiy); }
Quickly checks if this Region surrounds the specified Region object.

This method will return false if this Region object is not a simple rectangle.

/** * Quickly checks if this Region surrounds the specified * Region object. * <p> * This method will return false if this Region object is * not a simple rectangle. */
public boolean encompasses(Region r) { return (this.bands == null && this.lox <= r.lox && this.loy <= r.loy && this.hix >= r.hix && this.hiy >= r.hiy); }
Quickly checks if this Region surrounds the specified rectangular area specified in x, y, width, height format.

This method will return false if this Region object is not a simple rectangle.

/** * Quickly checks if this Region surrounds the specified * rectangular area specified in x, y, width, height format. * <p> * This method will return false if this Region object is * not a simple rectangle. */
public boolean encompassesXYWH(int x, int y, int w, int h) { return encompassesXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); }
Quickly checks if this Region surrounds the specified rectangular area specified in lox, loy, hix, hiy format.

This method will return false if this Region object is not a simple rectangle.

/** * Quickly checks if this Region surrounds the specified * rectangular area specified in lox, loy, hix, hiy format. * <p> * This method will return false if this Region object is * not a simple rectangle. */
public boolean encompassesXYXY(int lox, int loy, int hix, int hiy) { return (this.bands == null && this.lox <= lox && this.loy <= loy && this.hix >= hix && this.hiy >= hiy); }
Gets the bbox of the available spans, clipped to the OutputArea.
/** * Gets the bbox of the available spans, clipped to the OutputArea. */
public void getBounds(int pathbox[]) { pathbox[0] = lox; pathbox[1] = loy; pathbox[2] = hix; pathbox[3] = hiy; }
Clips the indicated bbox array to the bounds of this Region.
/** * Clips the indicated bbox array to the bounds of this Region. */
public void clipBoxToBounds(int bbox[]) { if (bbox[0] < lox) bbox[0] = lox; if (bbox[1] < loy) bbox[1] = loy; if (bbox[2] > hix) bbox[2] = hix; if (bbox[3] > hiy) bbox[3] = hiy; }
Gets an iterator object to iterate over the spans in this region.
/** * Gets an iterator object to iterate over the spans in this region. */
public RegionIterator getIterator() { return new RegionIterator(this); }
Gets a span iterator object that iterates over the spans in this region
/** * Gets a span iterator object that iterates over the spans in this region */
public SpanIterator getSpanIterator() { return new RegionSpanIterator(this); }
Gets a span iterator object that iterates over the spans in this region but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi).
/** * Gets a span iterator object that iterates over the spans in this region * but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi). */
public SpanIterator getSpanIterator(int bbox[]) { SpanIterator result = getSpanIterator(); result.intersectClipBox(bbox[0], bbox[1], bbox[2], bbox[3]); return result; }
Returns a SpanIterator that is the argument iterator filtered by this region.
/** * Returns a SpanIterator that is the argument iterator filtered by * this region. */
public SpanIterator filter(SpanIterator si) { if (bands == null) { si.intersectClipBox(lox, loy, hix, hiy); } else { si = new RegionClipSpanIterator(this, si); } return si; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Region[["); sb.append(lox); sb.append(", "); sb.append(loy); sb.append(" => "); sb.append(hix); sb.append(", "); sb.append(hiy); sb.append("]"); if (bands != null) { int col = 0; while (col < endIndex) { sb.append("y{"); sb.append(bands[col++]); sb.append(","); sb.append(bands[col++]); sb.append("}["); int end = bands[col++]; end = col + end * 2; while (col < end) { sb.append("x("); sb.append(bands[col++]); sb.append(", "); sb.append(bands[col++]); sb.append(")"); } sb.append("]"); } } sb.append("]"); return sb.toString(); } public int hashCode() { return (isEmpty() ? 0 : (lox * 3 + loy * 5 + hix * 7 + hiy * 9)); } public boolean equals(Object o) { if (!(o instanceof Region)) { return false; } Region r = (Region) o; if (this.isEmpty()) { return r.isEmpty(); } else if (r.isEmpty()) { return false; } if (r.lox != this.lox || r.loy != this.loy || r.hix != this.hix || r.hiy != this.hiy) { return false; } if (this.bands == null) { return (r.bands == null); } else if (r.bands == null) { return false; } if (this.endIndex != r.endIndex) { return false; } int abands[] = this.bands; int bbands[] = r.bands; for (int i = 0; i < endIndex; i++) { if (abands[i] != bbands[i]) { return false; } } return true; } }