Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/pipe/Region.java
38918 views
/*1* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.pipe;2627import java.awt.Rectangle;28import java.awt.Shape;29import java.awt.geom.AffineTransform;30import java.awt.geom.RectangularShape;3132import sun.java2d.loops.TransformHelper;3334/**35* This class encapsulates a definition of a two dimensional region which36* consists of a number of Y ranges each containing multiple X bands.37* <p>38* A rectangular Region is allowed to have a null band list in which39* case the rectangular shape is defined by the bounding box parameters40* (lox, loy, hix, hiy).41* <p>42* The band list, if present, consists of a list of rows in ascending Y43* order, ending at endIndex which is the index beyond the end of the44* last row. Each row consists of at least 3 + 2n entries (n >= 1)45* where the first 3 entries specify the Y range as start, end, and46* the number of X ranges in that Y range. These 3 entries are47* followed by pairs of X coordinates in ascending order:48* <pre>49* bands[rowstart+0] = Y0; // starting Y coordinate50* bands[rowstart+1] = Y1; // ending Y coordinate - endY > startY51* bands[rowstart+2] = N; // number of X bands - N >= 152*53* bands[rowstart+3] = X10; // starting X coordinate of first band54* bands[rowstart+4] = X11; // ending X coordinate of first band55* bands[rowstart+5] = X20; // starting X coordinate of second band56* bands[rowstart+6] = X21; // ending X coordinate of second band57* ...58* bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band59* bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band60*61* bands[rowstart+3+N*2] = ... // start of next Y row62* </pre>63*/64public class Region {65static final int INIT_SIZE = 50;66static final int GROW_SIZE = 50;6768/**69* Immutable Region.70*/71private static final class ImmutableRegion extends Region {72protected ImmutableRegion(int lox, int loy, int hix, int hiy) {73super(lox, loy, hix, hiy);74}7576// Override all the methods that mutate the object77public void appendSpans(sun.java2d.pipe.SpanIterator si) {}78public void setOutputArea(java.awt.Rectangle r) {}79public void setOutputAreaXYWH(int x, int y, int w, int h) {}80public void setOutputArea(int[] box) {}81public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {}82}8384public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0);85public static final Region WHOLE_REGION = new ImmutableRegion(86Integer.MIN_VALUE,87Integer.MIN_VALUE,88Integer.MAX_VALUE,89Integer.MAX_VALUE);9091int lox;92int loy;93int hix;94int hiy;9596int endIndex;97int[] bands;9899private static native void initIDs();100101static {102initIDs();103}104105/**106* Adds the dimension <code>dim</code> to the coordinate107* <code>start</code> with appropriate clipping. If108* <code>dim</code> is non-positive then the method returns109* the start coordinate. If the sum overflows an integer110* data type then the method returns <code>Integer.MAX_VALUE</code>.111*/112public static int dimAdd(int start, int dim) {113if (dim <= 0) return start;114if ((dim += start) < start) return Integer.MAX_VALUE;115return dim;116}117118/**119* Adds the delta {@code dv} to the value {@code v} with120* appropriate clipping to the bounds of Integer resolution.121* If the answer would be greater than {@code Integer.MAX_VALUE}122* then {@code Integer.MAX_VALUE} is returned.123* If the answer would be less than {@code Integer.MIN_VALUE}124* then {@code Integer.MIN_VALUE} is returned.125* Otherwise the sum is returned.126*/127public static int clipAdd(int v, int dv) {128int newv = v + dv;129if ((newv > v) != (dv > 0)) {130newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;131}132return newv;133}134135/**136* Multiply the scale factor {@code sv} and the value {@code v} with137* appropriate clipping to the bounds of Integer resolution. If the answer138* would be greater than {@code Integer.MAX_VALUE} then {@code139* Integer.MAX_VALUE} is returned. If the answer would be less than {@code140* Integer.MIN_VALUE} then {@code Integer.MIN_VALUE} is returned. Otherwise141* the multiplication is returned.142*/143public static int clipScale(final int v, final double sv) {144if (sv == 1.0) {145return v;146}147final double newv = v * sv;148if (newv < Integer.MIN_VALUE) {149return Integer.MIN_VALUE;150}151if (newv > Integer.MAX_VALUE) {152return Integer.MAX_VALUE;153}154return (int) Math.round(newv);155}156157protected Region(int lox, int loy, int hix, int hiy) {158this.lox = lox;159this.loy = loy;160this.hix = hix;161this.hiy = hiy;162}163164private Region(int lox, int loy, int hix, int hiy, int[] bands, int end) {165this.lox = lox;166this.loy = loy;167this.hix = hix;168this.hiy = hiy;169this.bands = bands;170this.endIndex = end;171}172173/**174* Returns a Region object covering the pixels which would be175* touched by a fill or clip operation on a Graphics implementation176* on the specified Shape object under the optionally specified177* AffineTransform object.178*179* @param s a non-null Shape object specifying the geometry enclosing180* the pixels of interest181* @param at an optional <code>AffineTransform</code> to be applied to the182* coordinates as they are returned in the iteration, or183* <code>null</code> if untransformed coordinates are desired184*/185public static Region getInstance(Shape s, AffineTransform at) {186return getInstance(WHOLE_REGION, false, s, at);187}188189/**190* Returns a Region object covering the pixels which would be191* touched by a fill or clip operation on a Graphics implementation192* on the specified Shape object under the optionally specified193* AffineTransform object further restricted by the specified194* device bounds.195* <p>196* Note that only the bounds of the specified Region are used to197* restrict the resulting Region.198* If devBounds is non-rectangular and clipping to the specific199* bands of devBounds is needed, then an intersection of the200* resulting Region with devBounds must be performed in a201* subsequent step.202*203* @param devBounds a non-null Region specifying some bounds to204* clip the geometry to205* @param s a non-null Shape object specifying the geometry enclosing206* the pixels of interest207* @param at an optional <code>AffineTransform</code> to be applied to the208* coordinates as they are returned in the iteration, or209* <code>null</code> if untransformed coordinates are desired210*/211public static Region getInstance(Region devBounds,212Shape s, AffineTransform at)213{214return getInstance(devBounds, false, s, at);215}216217/**218* Returns a Region object covering the pixels which would be219* touched by a fill or clip operation on a Graphics implementation220* on the specified Shape object under the optionally specified221* AffineTransform object further restricted by the specified222* device bounds.223* If the normalize parameter is true then coordinate normalization224* is performed as per the 2D Graphics non-antialiasing implementation225* of the VALUE_STROKE_NORMALIZE hint.226* <p>227* Note that only the bounds of the specified Region are used to228* restrict the resulting Region.229* If devBounds is non-rectangular and clipping to the specific230* bands of devBounds is needed, then an intersection of the231* resulting Region with devBounds must be performed in a232* subsequent step.233*234* @param devBounds a non-null Region specifying some bounds to235* clip the geometry to236* @param normalize a boolean indicating whether or not to apply237* normalization238* @param s a non-null Shape object specifying the geometry enclosing239* the pixels of interest240* @param at an optional <code>AffineTransform</code> to be applied to the241* coordinates as they are returned in the iteration, or242* <code>null</code> if untransformed coordinates are desired243*/244public static Region getInstance(Region devBounds, boolean normalize,245Shape s, AffineTransform at)246{247// Optimize for empty shapes to avoid involving the SpanIterator248if (s instanceof RectangularShape &&249((RectangularShape)s).isEmpty())250{251return EMPTY_REGION;252}253254int box[] = new int[4];255ShapeSpanIterator sr = new ShapeSpanIterator(normalize);256try {257sr.setOutputArea(devBounds);258sr.appendPath(s.getPathIterator(at));259sr.getPathBox(box);260Region r = Region.getInstance(box);261r.appendSpans(sr);262return r;263} finally {264sr.dispose();265}266}267268/**269* Returns a Region object with a rectangle of interest specified by the270* indicated rectangular area in lox, loy, hix, hiy and edges array, which271* is located relative to the rectangular area. Edges array - 0,1 are y272* range, 2N,2N+1 are x ranges, 1 per y range.273*274* @see TransformHelper275*/276static Region getInstance(final int lox, final int loy, final int hix,277final int hiy, final int[] edges) {278final int y1 = edges[0];279final int y2 = edges[1];280if (hiy <= loy || hix <= lox || y2 <= y1) {281return EMPTY_REGION;282}283// rowsNum * (3 + 1 * 2)284final int[] bands = new int[(y2 - y1) * 5];285int end = 0;286int index = 2;287for (int y = y1; y < y2; ++y) {288final int spanlox = Math.max(clipAdd(lox, edges[index++]), lox);289final int spanhix = Math.min(clipAdd(lox, edges[index++]), hix);290if (spanlox < spanhix) {291final int spanloy = Math.max(clipAdd(loy, y), loy);292final int spanhiy = Math.min(clipAdd(spanloy, 1), hiy);293if (spanloy < spanhiy) {294bands[end++] = spanloy;295bands[end++] = spanhiy;296bands[end++] = 1; // 1 span per row297bands[end++] = spanlox;298bands[end++] = spanhix;299}300}301}302return end != 0 ? new Region(lox, loy, hix, hiy, bands, end)303: EMPTY_REGION;304}305306/**307* Returns a Region object with a rectangle of interest specified308* by the indicated Rectangle object.309* <p>310* This method can also be used to create a simple rectangular311* region.312*/313public static Region getInstance(Rectangle r) {314return Region.getInstanceXYWH(r.x, r.y, r.width, r.height);315}316317/**318* Returns a Region object with a rectangle of interest specified319* by the indicated rectangular area in x, y, width, height format.320* <p>321* This method can also be used to create a simple rectangular322* region.323*/324public static Region getInstanceXYWH(int x, int y, int w, int h) {325return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h));326}327328/**329* Returns a Region object with a rectangle of interest specified330* by the indicated span array.331* <p>332* This method can also be used to create a simple rectangular333* region.334*/335public static Region getInstance(int box[]) {336return new Region(box[0], box[1], box[2], box[3]);337}338339/**340* Returns a Region object with a rectangle of interest specified341* by the indicated rectangular area in lox, loy, hix, hiy format.342* <p>343* This method can also be used to create a simple rectangular344* region.345*/346public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) {347return new Region(lox, loy, hix, hiy);348}349350/**351* Sets the rectangle of interest for storing and returning352* region bands.353* <p>354* This method can also be used to initialize a simple rectangular355* region.356*/357public void setOutputArea(Rectangle r) {358setOutputAreaXYWH(r.x, r.y, r.width, r.height);359}360361/**362* Sets the rectangle of interest for storing and returning363* region bands. The rectangle is specified in x, y, width, height364* format and appropriate clipping is performed as per the method365* <code>dimAdd</code>.366* <p>367* This method can also be used to initialize a simple rectangular368* region.369*/370public void setOutputAreaXYWH(int x, int y, int w, int h) {371setOutputAreaXYXY(x, y, dimAdd(x, w), dimAdd(y, h));372}373374/**375* Sets the rectangle of interest for storing and returning376* region bands. The rectangle is specified as a span array.377* <p>378* This method can also be used to initialize a simple rectangular379* region.380*/381public void setOutputArea(int box[]) {382this.lox = box[0];383this.loy = box[1];384this.hix = box[2];385this.hiy = box[3];386}387388/**389* Sets the rectangle of interest for storing and returning390* region bands. The rectangle is specified in lox, loy,391* hix, hiy format.392* <p>393* This method can also be used to initialize a simple rectangular394* region.395*/396public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {397this.lox = lox;398this.loy = loy;399this.hix = hix;400this.hiy = hiy;401}402403/**404* Appends the list of spans returned from the indicated405* SpanIterator. Each span must be at a higher starting406* Y coordinate than the previous data or it must have a407* Y range equal to the highest Y band in the region and a408* higher X coordinate than any of the spans in that band.409*/410public void appendSpans(SpanIterator si) {411int[] box = new int[6];412413while (si.nextSpan(box)) {414appendSpan(box);415}416417endRow(box);418calcBBox();419}420421/**422* Returns a Region object that represents the same list of rectangles as423* the current Region object, scaled by the specified sx, sy factors.424*/425public Region getScaledRegion(final double sx, final double sy) {426if (sx == 0 || sy == 0 || this == EMPTY_REGION) {427return EMPTY_REGION;428}429if ((sx == 1.0 && sy == 1.0) || (this == WHOLE_REGION)) {430return this;431}432433int tlox = clipScale(lox, sx);434int tloy = clipScale(loy, sy);435int thix = clipScale(hix, sx);436int thiy = clipScale(hiy, sy);437Region ret = new Region(tlox, tloy, thix, thiy);438int bands[] = this.bands;439if (bands != null) {440int end = endIndex;441int newbands[] = new int[end];442int i = 0; // index for source bands443int j = 0; // index for translated newbands444int ncol;445while (i < end) {446int y1, y2;447newbands[j++] = y1 = clipScale(bands[i++], sy);448newbands[j++] = y2 = clipScale(bands[i++], sy);449newbands[j++] = ncol = bands[i++];450int savej = j;451if (y1 < y2) {452while (--ncol >= 0) {453int x1 = clipScale(bands[i++], sx);454int x2 = clipScale(bands[i++], sx);455if (x1 < x2) {456newbands[j++] = x1;457newbands[j++] = x2;458}459}460} else {461i += ncol * 2;462}463// Did we get any non-empty bands in this row?464if (j > savej) {465newbands[savej-1] = (j - savej) / 2;466} else {467j = savej - 3;468}469}470if (j <= 5) {471if (j < 5) {472// No rows or bands were generated...473ret.lox = ret.loy = ret.hix = ret.hiy = 0;474} else {475// Only generated one single rect in the end...476ret.loy = newbands[0];477ret.hiy = newbands[1];478ret.lox = newbands[3];479ret.hix = newbands[4];480}481// ret.endIndex and ret.bands were never initialized...482// ret.endIndex = 0;483// ret.newbands = null;484} else {485// Generated multiple bands and/or multiple rows...486ret.endIndex = j;487ret.bands = newbands;488}489}490return ret;491}492493494/**495* Returns a Region object that represents the same list of496* rectangles as the current Region object, translated by497* the specified dx, dy translation factors.498*/499public Region getTranslatedRegion(int dx, int dy) {500if ((dx | dy) == 0) {501return this;502}503int tlox = lox + dx;504int tloy = loy + dy;505int thix = hix + dx;506int thiy = hiy + dy;507if ((tlox > lox) != (dx > 0) ||508(tloy > loy) != (dy > 0) ||509(thix > hix) != (dx > 0) ||510(thiy > hiy) != (dy > 0))511{512return getSafeTranslatedRegion(dx, dy);513}514Region ret = new Region(tlox, tloy, thix, thiy);515int bands[] = this.bands;516if (bands != null) {517int end = endIndex;518ret.endIndex = end;519int newbands[] = new int[end];520ret.bands = newbands;521int i = 0;522int ncol;523while (i < end) {524newbands[i] = bands[i] + dy; i++;525newbands[i] = bands[i] + dy; i++;526newbands[i] = ncol = bands[i]; i++;527while (--ncol >= 0) {528newbands[i] = bands[i] + dx; i++;529newbands[i] = bands[i] + dx; i++;530}531}532}533return ret;534}535536private Region getSafeTranslatedRegion(int dx, int dy) {537int tlox = clipAdd(lox, dx);538int tloy = clipAdd(loy, dy);539int thix = clipAdd(hix, dx);540int thiy = clipAdd(hiy, dy);541Region ret = new Region(tlox, tloy, thix, thiy);542int bands[] = this.bands;543if (bands != null) {544int end = endIndex;545int newbands[] = new int[end];546int i = 0; // index for source bands547int j = 0; // index for translated newbands548int ncol;549while (i < end) {550int y1, y2;551newbands[j++] = y1 = clipAdd(bands[i++], dy);552newbands[j++] = y2 = clipAdd(bands[i++], dy);553newbands[j++] = ncol = bands[i++];554int savej = j;555if (y1 < y2) {556while (--ncol >= 0) {557int x1 = clipAdd(bands[i++], dx);558int x2 = clipAdd(bands[i++], dx);559if (x1 < x2) {560newbands[j++] = x1;561newbands[j++] = x2;562}563}564} else {565i += ncol * 2;566}567// Did we get any non-empty bands in this row?568if (j > savej) {569newbands[savej-1] = (j - savej) / 2;570} else {571j = savej - 3;572}573}574if (j <= 5) {575if (j < 5) {576// No rows or bands were generated...577ret.lox = ret.loy = ret.hix = ret.hiy = 0;578} else {579// Only generated one single rect in the end...580ret.loy = newbands[0];581ret.hiy = newbands[1];582ret.lox = newbands[3];583ret.hix = newbands[4];584}585// ret.endIndex and ret.bands were never initialized...586// ret.endIndex = 0;587// ret.newbands = null;588} else {589// Generated multiple bands and/or multiple rows...590ret.endIndex = j;591ret.bands = newbands;592}593}594return ret;595}596597/**598* Returns a Region object that represents the intersection of599* this object with the specified Rectangle. The return value600* may be this same object if no clipping occurs.601*/602public Region getIntersection(Rectangle r) {603return getIntersectionXYWH(r.x, r.y, r.width, r.height);604}605606/**607* Returns a Region object that represents the intersection of608* this object with the specified rectangular area. The return609* value may be this same object if no clipping occurs.610*/611public Region getIntersectionXYWH(int x, int y, int w, int h) {612return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));613}614615/**616* Returns a Region object that represents the intersection of617* this object with the specified rectangular area. The return618* value may be this same object if no clipping occurs.619*/620public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) {621if (isInsideXYXY(lox, loy, hix, hiy)) {622return this;623}624Region ret = new Region((lox < this.lox) ? this.lox : lox,625(loy < this.loy) ? this.loy : loy,626(hix > this.hix) ? this.hix : hix,627(hiy > this.hiy) ? this.hiy : hiy);628if (bands != null) {629ret.appendSpans(this.getSpanIterator());630}631return ret;632}633634/**635* Returns a Region object that represents the intersection of this636* object with the specified Region object.637* <p>638* If {@code A} and {@code B} are both Region Objects and639* <code>C = A.getIntersection(B);</code> then a point will640* be contained in {@code C} iff it is contained in both641* {@code A} and {@code B}.642* <p>643* The return value may be this same object or the argument644* Region object if no clipping occurs.645*/646public Region getIntersection(Region r) {647if (this.isInsideQuickCheck(r)) {648return this;649}650if (r.isInsideQuickCheck(this)) {651return r;652}653Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox,654(r.loy < this.loy) ? this.loy : r.loy,655(r.hix > this.hix) ? this.hix : r.hix,656(r.hiy > this.hiy) ? this.hiy : r.hiy);657if (!ret.isEmpty()) {658ret.filterSpans(this, r, INCLUDE_COMMON);659}660return ret;661}662663/**664* Returns a Region object that represents the union of this665* object with the specified Region object.666* <p>667* If {@code A} and {@code B} are both Region Objects and668* <code>C = A.getUnion(B);</code> then a point will669* be contained in {@code C} iff it is contained in either670* {@code A} or {@code B}.671* <p>672* The return value may be this same object or the argument673* Region object if no augmentation occurs.674*/675public Region getUnion(Region r) {676if (r.isEmpty() || r.isInsideQuickCheck(this)) {677return this;678}679if (this.isEmpty() || this.isInsideQuickCheck(r)) {680return r;681}682Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,683(r.loy > this.loy) ? this.loy : r.loy,684(r.hix < this.hix) ? this.hix : r.hix,685(r.hiy < this.hiy) ? this.hiy : r.hiy);686ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON);687return ret;688}689690/**691* Returns a Region object that represents the difference of the692* specified Region object subtracted from this object.693* <p>694* If {@code A} and {@code B} are both Region Objects and695* <code>C = A.getDifference(B);</code> then a point will696* be contained in {@code C} iff it is contained in697* {@code A} but not contained in {@code B}.698* <p>699* The return value may be this same object or the argument700* Region object if no clipping occurs.701*/702public Region getDifference(Region r) {703if (!r.intersectsQuickCheck(this)) {704return this;705}706if (this.isInsideQuickCheck(r)) {707return EMPTY_REGION;708}709Region ret = new Region(this.lox, this.loy, this.hix, this.hiy);710ret.filterSpans(this, r, INCLUDE_A);711return ret;712}713714/**715* Returns a Region object that represents the exclusive or of this716* object with the specified Region object.717* <p>718* If {@code A} and {@code B} are both Region Objects and719* <code>C = A.getExclusiveOr(B);</code> then a point will720* be contained in {@code C} iff it is contained in either721* {@code A} or {@code B}, but not if it is contained in both.722* <p>723* The return value may be this same object or the argument724* Region object if either is empty.725*/726public Region getExclusiveOr(Region r) {727if (r.isEmpty()) {728return this;729}730if (this.isEmpty()) {731return r;732}733Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,734(r.loy > this.loy) ? this.loy : r.loy,735(r.hix < this.hix) ? this.hix : r.hix,736(r.hiy < this.hiy) ? this.hiy : r.hiy);737ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B);738return ret;739}740741static final int INCLUDE_A = 1;742static final int INCLUDE_B = 2;743static final int INCLUDE_COMMON = 4;744745private void filterSpans(Region ra, Region rb, int flags) {746int abands[] = ra.bands;747int bbands[] = rb.bands;748if (abands == null) {749abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix};750}751if (bbands == null) {752bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix};753}754int box[] = new int[6];755int acolstart = 0;756int ay1 = abands[acolstart++];757int ay2 = abands[acolstart++];758int acolend = abands[acolstart++];759acolend = acolstart + 2 * acolend;760int bcolstart = 0;761int by1 = bbands[bcolstart++];762int by2 = bbands[bcolstart++];763int bcolend = bbands[bcolstart++];764bcolend = bcolstart + 2 * bcolend;765int y = loy;766while (y < hiy) {767if (y >= ay2) {768if (acolend < ra.endIndex) {769acolstart = acolend;770ay1 = abands[acolstart++];771ay2 = abands[acolstart++];772acolend = abands[acolstart++];773acolend = acolstart + 2 * acolend;774} else {775if ((flags & INCLUDE_B) == 0) break;776ay1 = ay2 = hiy;777}778continue;779}780if (y >= by2) {781if (bcolend < rb.endIndex) {782bcolstart = bcolend;783by1 = bbands[bcolstart++];784by2 = bbands[bcolstart++];785bcolend = bbands[bcolstart++];786bcolend = bcolstart + 2 * bcolend;787} else {788if ((flags & INCLUDE_A) == 0) break;789by1 = by2 = hiy;790}791continue;792}793int yend;794if (y < by1) {795if (y < ay1) {796y = Math.min(ay1, by1);797continue;798}799// We are in a set of rows that belong only to A800yend = Math.min(ay2, by1);801if ((flags & INCLUDE_A) != 0) {802box[1] = y;803box[3] = yend;804int acol = acolstart;805while (acol < acolend) {806box[0] = abands[acol++];807box[2] = abands[acol++];808appendSpan(box);809}810}811} else if (y < ay1) {812// We are in a set of rows that belong only to B813yend = Math.min(by2, ay1);814if ((flags & INCLUDE_B) != 0) {815box[1] = y;816box[3] = yend;817int bcol = bcolstart;818while (bcol < bcolend) {819box[0] = bbands[bcol++];820box[2] = bbands[bcol++];821appendSpan(box);822}823}824} else {825// We are in a set of rows that belong to both A and B826yend = Math.min(ay2, by2);827box[1] = y;828box[3] = yend;829int acol = acolstart;830int bcol = bcolstart;831int ax1 = abands[acol++];832int ax2 = abands[acol++];833int bx1 = bbands[bcol++];834int bx2 = bbands[bcol++];835int x = Math.min(ax1, bx1);836if (x < lox) x = lox;837while (x < hix) {838if (x >= ax2) {839if (acol < acolend) {840ax1 = abands[acol++];841ax2 = abands[acol++];842} else {843if ((flags & INCLUDE_B) == 0) break;844ax1 = ax2 = hix;845}846continue;847}848if (x >= bx2) {849if (bcol < bcolend) {850bx1 = bbands[bcol++];851bx2 = bbands[bcol++];852} else {853if ((flags & INCLUDE_A) == 0) break;854bx1 = bx2 = hix;855}856continue;857}858int xend;859boolean appendit;860if (x < bx1) {861if (x < ax1) {862xend = Math.min(ax1, bx1);863appendit = false;864} else {865xend = Math.min(ax2, bx1);866appendit = ((flags & INCLUDE_A) != 0);867}868} else if (x < ax1) {869xend = Math.min(ax1, bx2);870appendit = ((flags & INCLUDE_B) != 0);871} else {872xend = Math.min(ax2, bx2);873appendit = ((flags & INCLUDE_COMMON) != 0);874}875if (appendit) {876box[0] = x;877box[2] = xend;878appendSpan(box);879}880x = xend;881}882}883y = yend;884}885endRow(box);886calcBBox();887}888889/**890* Returns a Region object that represents the bounds of the891* intersection of this object with the bounds of the specified892* Region object.893* <p>894* The return value may be this same object if no clipping occurs895* and this Region is rectangular.896*/897public Region getBoundsIntersection(Rectangle r) {898return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height);899}900901/**902* Returns a Region object that represents the bounds of the903* intersection of this object with the bounds of the specified904* rectangular area in x, y, width, height format.905* <p>906* The return value may be this same object if no clipping occurs907* and this Region is rectangular.908*/909public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) {910return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));911}912913/**914* Returns a Region object that represents the bounds of the915* intersection of this object with the bounds of the specified916* rectangular area in lox, loy, hix, hiy format.917* <p>918* The return value may be this same object if no clipping occurs919* and this Region is rectangular.920*/921public Region getBoundsIntersectionXYXY(int lox, int loy,922int hix, int hiy)923{924if (this.bands == null &&925this.lox >= lox && this.loy >= loy &&926this.hix <= hix && this.hiy <= hiy)927{928return this;929}930return new Region((lox < this.lox) ? this.lox : lox,931(loy < this.loy) ? this.loy : loy,932(hix > this.hix) ? this.hix : hix,933(hiy > this.hiy) ? this.hiy : hiy);934}935936/**937* Returns a Region object that represents the intersection of938* this object with the bounds of the specified Region object.939* <p>940* The return value may be this same object or the argument941* Region object if no clipping occurs and the Regions are942* rectangular.943*/944public Region getBoundsIntersection(Region r) {945if (this.encompasses(r)) {946return r;947}948if (r.encompasses(this)) {949return this;950}951return new Region((r.lox < this.lox) ? this.lox : r.lox,952(r.loy < this.loy) ? this.loy : r.loy,953(r.hix > this.hix) ? this.hix : r.hix,954(r.hiy > this.hiy) ? this.hiy : r.hiy);955}956957/**958* Appends a single span defined by the 4 parameters959* spanlox, spanloy, spanhix, spanhiy.960* This span must be at a higher starting Y coordinate than961* the previous data or it must have a Y range equal to the962* highest Y band in the region and a higher X coordinate963* than any of the spans in that band.964*/965private void appendSpan(int box[]) {966int spanlox, spanloy, spanhix, spanhiy;967if ((spanlox = box[0]) < lox) spanlox = lox;968if ((spanloy = box[1]) < loy) spanloy = loy;969if ((spanhix = box[2]) > hix) spanhix = hix;970if ((spanhiy = box[3]) > hiy) spanhiy = hiy;971if (spanhix <= spanlox || spanhiy <= spanloy) {972return;973}974975int curYrow = box[4];976if (endIndex == 0 || spanloy >= bands[curYrow + 1]) {977if (bands == null) {978bands = new int[INIT_SIZE];979} else {980needSpace(5);981endRow(box);982curYrow = box[4];983}984bands[endIndex++] = spanloy;985bands[endIndex++] = spanhiy;986bands[endIndex++] = 0;987} else if (spanloy == bands[curYrow] &&988spanhiy == bands[curYrow + 1] &&989spanlox >= bands[endIndex - 1]) {990if (spanlox == bands[endIndex - 1]) {991bands[endIndex - 1] = spanhix;992return;993}994needSpace(2);995} else {996throw new InternalError("bad span");997}998bands[endIndex++] = spanlox;999bands[endIndex++] = spanhix;1000bands[curYrow + 2]++;1001}10021003private void needSpace(int num) {1004if (endIndex + num >= bands.length) {1005int[] newbands = new int[bands.length + GROW_SIZE];1006System.arraycopy(bands, 0, newbands, 0, endIndex);1007bands = newbands;1008}1009}10101011private void endRow(int box[]) {1012int cur = box[4];1013int prev = box[5];1014if (cur > prev) {1015int[] bands = this.bands;1016if (bands[prev + 1] == bands[cur] &&1017bands[prev + 2] == bands[cur + 2])1018{1019int num = bands[cur + 2] * 2;1020cur += 3;1021prev += 3;1022while (num > 0) {1023if (bands[cur++] != bands[prev++]) {1024break;1025}1026num--;1027}1028if (num == 0) {1029// prev == box[4]1030bands[box[5] + 1] = bands[prev + 1];1031endIndex = prev;1032return;1033}1034}1035}1036box[5] = box[4];1037box[4] = endIndex;1038}10391040private void calcBBox() {1041int[] bands = this.bands;1042if (endIndex <= 5) {1043if (endIndex == 0) {1044lox = loy = hix = hiy = 0;1045} else {1046loy = bands[0];1047hiy = bands[1];1048lox = bands[3];1049hix = bands[4];1050endIndex = 0;1051}1052this.bands = null;1053return;1054}1055int lox = this.hix;1056int hix = this.lox;1057int hiyindex = 0;10581059int i = 0;1060while (i < endIndex) {1061hiyindex = i;1062int numbands = bands[i + 2];1063i += 3;1064if (lox > bands[i]) {1065lox = bands[i];1066}1067i += numbands * 2;1068if (hix < bands[i - 1]) {1069hix = bands[i - 1];1070}1071}10721073this.lox = lox;1074this.loy = bands[0];1075this.hix = hix;1076this.hiy = bands[hiyindex + 1];1077}10781079/**1080* Returns the lowest X coordinate in the Region.1081*/1082public final int getLoX() {1083return lox;1084}10851086/**1087* Returns the lowest Y coordinate in the Region.1088*/1089public final int getLoY() {1090return loy;1091}10921093/**1094* Returns the highest X coordinate in the Region.1095*/1096public final int getHiX() {1097return hix;1098}10991100/**1101* Returns the highest Y coordinate in the Region.1102*/1103public final int getHiY() {1104return hiy;1105}11061107/**1108* Returns the width of this Region clipped to the range (0 - MAX_INT).1109*/1110public final int getWidth() {1111if (hix < lox) return 0;1112int w;1113if ((w = hix - lox) < 0) {1114w = Integer.MAX_VALUE;1115}1116return w;1117}11181119/**1120* Returns the height of this Region clipped to the range (0 - MAX_INT).1121*/1122public final int getHeight() {1123if (hiy < loy) return 0;1124int h;1125if ((h = hiy - loy) < 0) {1126h = Integer.MAX_VALUE;1127}1128return h;1129}11301131/**1132* Returns true iff this Region encloses no area.1133*/1134public boolean isEmpty() {1135return (hix <= lox || hiy <= loy);1136}11371138/**1139* Returns true iff this Region represents a single simple1140* rectangular area.1141*/1142public boolean isRectangular() {1143return (bands == null);1144}11451146/**1147* Returns true iff this Region contains the specified coordinate.1148*/1149public boolean contains(int x, int y) {1150if (x < lox || x >= hix || y < loy || y >= hiy) return false;1151if (bands == null) return true;1152int i = 0;1153while (i < endIndex) {1154if (y < bands[i++]) {1155return false;1156}1157if (y >= bands[i++]) {1158int numspans = bands[i++];1159i += numspans * 2;1160} else {1161int end = bands[i++];1162end = i + end * 2;1163while (i < end) {1164if (x < bands[i++]) return false;1165if (x < bands[i++]) return true;1166}1167return false;1168}1169}1170return false;1171}11721173/**1174* Returns true iff this Region lies inside the indicated1175* rectangular area specified in x, y, width, height format1176* with appropriate clipping performed as per the dimAdd method.1177*/1178public boolean isInsideXYWH(int x, int y, int w, int h) {1179return isInsideXYXY(x, y, dimAdd(x, w), dimAdd(y, h));1180}11811182/**1183* Returns true iff this Region lies inside the indicated1184* rectangular area specified in lox, loy, hix, hiy format.1185*/1186public boolean isInsideXYXY(int lox, int loy, int hix, int hiy) {1187return (this.lox >= lox && this.loy >= loy &&1188this.hix <= hix && this.hiy <= hiy);11891190}11911192/**1193* Quickly checks if this Region lies inside the specified1194* Region object.1195* <p>1196* This method will return false if the specified Region1197* object is not a simple rectangle.1198*/1199public boolean isInsideQuickCheck(Region r) {1200return (r.bands == null &&1201r.lox <= this.lox && r.loy <= this.loy &&1202r.hix >= this.hix && r.hiy >= this.hiy);1203}12041205/**1206* Quickly checks if this Region intersects the specified1207* rectangular area specified in lox, loy, hix, hiy format.1208* <p>1209* This method tests only against the bounds of this region1210* and does not bother to test if the rectangular region1211* actually intersects any bands.1212*/1213public boolean intersectsQuickCheckXYXY(int lox, int loy,1214int hix, int hiy)1215{1216return (hix > this.lox && lox < this.hix &&1217hiy > this.loy && loy < this.hiy);1218}12191220/**1221* Quickly checks if this Region intersects the specified1222* Region object.1223* <p>1224* This method tests only against the bounds of this region1225* and does not bother to test if the rectangular region1226* actually intersects any bands.1227*/1228public boolean intersectsQuickCheck(Region r) {1229return (r.hix > this.lox && r.lox < this.hix &&1230r.hiy > this.loy && r.loy < this.hiy);1231}12321233/**1234* Quickly checks if this Region surrounds the specified1235* Region object.1236* <p>1237* This method will return false if this Region object is1238* not a simple rectangle.1239*/1240public boolean encompasses(Region r) {1241return (this.bands == null &&1242this.lox <= r.lox && this.loy <= r.loy &&1243this.hix >= r.hix && this.hiy >= r.hiy);1244}12451246/**1247* Quickly checks if this Region surrounds the specified1248* rectangular area specified in x, y, width, height format.1249* <p>1250* This method will return false if this Region object is1251* not a simple rectangle.1252*/1253public boolean encompassesXYWH(int x, int y, int w, int h) {1254return encompassesXYXY(x, y, dimAdd(x, w), dimAdd(y, h));1255}12561257/**1258* Quickly checks if this Region surrounds the specified1259* rectangular area specified in lox, loy, hix, hiy format.1260* <p>1261* This method will return false if this Region object is1262* not a simple rectangle.1263*/1264public boolean encompassesXYXY(int lox, int loy, int hix, int hiy) {1265return (this.bands == null &&1266this.lox <= lox && this.loy <= loy &&1267this.hix >= hix && this.hiy >= hiy);1268}12691270/**1271* Gets the bbox of the available spans, clipped to the OutputArea.1272*/1273public void getBounds(int pathbox[]) {1274pathbox[0] = lox;1275pathbox[1] = loy;1276pathbox[2] = hix;1277pathbox[3] = hiy;1278}12791280/**1281* Clips the indicated bbox array to the bounds of this Region.1282*/1283public void clipBoxToBounds(int bbox[]) {1284if (bbox[0] < lox) bbox[0] = lox;1285if (bbox[1] < loy) bbox[1] = loy;1286if (bbox[2] > hix) bbox[2] = hix;1287if (bbox[3] > hiy) bbox[3] = hiy;1288}12891290/**1291* Gets an iterator object to iterate over the spans in this region.1292*/1293public RegionIterator getIterator() {1294return new RegionIterator(this);1295}12961297/**1298* Gets a span iterator object that iterates over the spans in this region1299*/1300public SpanIterator getSpanIterator() {1301return new RegionSpanIterator(this);1302}13031304/**1305* Gets a span iterator object that iterates over the spans in this region1306* but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi).1307*/1308public SpanIterator getSpanIterator(int bbox[]) {1309SpanIterator result = getSpanIterator();1310result.intersectClipBox(bbox[0], bbox[1], bbox[2], bbox[3]);1311return result;1312}13131314/**1315* Returns a SpanIterator that is the argument iterator filtered by1316* this region.1317*/1318public SpanIterator filter(SpanIterator si) {1319if (bands == null) {1320si.intersectClipBox(lox, loy, hix, hiy);1321} else {1322si = new RegionClipSpanIterator(this, si);1323}1324return si;1325}13261327public String toString() {1328StringBuffer sb = new StringBuffer();1329sb.append("Region[[");1330sb.append(lox);1331sb.append(", ");1332sb.append(loy);1333sb.append(" => ");1334sb.append(hix);1335sb.append(", ");1336sb.append(hiy);1337sb.append("]");1338if (bands != null) {1339int col = 0;1340while (col < endIndex) {1341sb.append("y{");1342sb.append(bands[col++]);1343sb.append(",");1344sb.append(bands[col++]);1345sb.append("}[");1346int end = bands[col++];1347end = col + end * 2;1348while (col < end) {1349sb.append("x(");1350sb.append(bands[col++]);1351sb.append(", ");1352sb.append(bands[col++]);1353sb.append(")");1354}1355sb.append("]");1356}1357}1358sb.append("]");1359return sb.toString();1360}13611362public int hashCode() {1363return (isEmpty() ? 0 : (lox * 3 + loy * 5 + hix * 7 + hiy * 9));1364}13651366public boolean equals(Object o) {1367if (!(o instanceof Region)) {1368return false;1369}1370Region r = (Region) o;1371if (this.isEmpty()) {1372return r.isEmpty();1373} else if (r.isEmpty()) {1374return false;1375}1376if (r.lox != this.lox || r.loy != this.loy ||1377r.hix != this.hix || r.hiy != this.hiy)1378{1379return false;1380}1381if (this.bands == null) {1382return (r.bands == null);1383} else if (r.bands == null) {1384return false;1385}1386if (this.endIndex != r.endIndex) {1387return false;1388}1389int abands[] = this.bands;1390int bbands[] = r.bands;1391for (int i = 0; i < endIndex; i++) {1392if (abands[i] != bbands[i]) {1393return false;1394}1395}1396return true;1397}1398}139914001401