Path: blob/master/src/java.desktop/share/classes/sun/java2d/pipe/Region.java
41159 views
/*1* Copyright (c) 1998, 2018, 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.Rectangle2D;31import java.awt.geom.RectangularShape;3233import sun.java2d.loops.TransformHelper;3435import static java.lang.Double.isNaN;3637/**38* This class encapsulates a definition of a two dimensional region which39* consists of a number of Y ranges each containing multiple X bands.40* <p>41* A rectangular Region is allowed to have a null band list in which42* case the rectangular shape is defined by the bounding box parameters43* (lox, loy, hix, hiy).44* <p>45* The band list, if present, consists of a list of rows in ascending Y46* order, ending at endIndex which is the index beyond the end of the47* last row. Each row consists of at least 3 + 2n entries (n >= 1)48* where the first 3 entries specify the Y range as start, end, and49* the number of X ranges in that Y range. These 3 entries are50* followed by pairs of X coordinates in ascending order:51* <pre>52* bands[rowstart+0] = Y0; // starting Y coordinate53* bands[rowstart+1] = Y1; // ending Y coordinate - endY > startY54* bands[rowstart+2] = N; // number of X bands - N >= 155*56* bands[rowstart+3] = X10; // starting X coordinate of first band57* bands[rowstart+4] = X11; // ending X coordinate of first band58* bands[rowstart+5] = X20; // starting X coordinate of second band59* bands[rowstart+6] = X21; // ending X coordinate of second band60* ...61* bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band62* bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band63*64* bands[rowstart+3+N*2] = ... // start of next Y row65* </pre>66*/67public final class Region {68private static final int INIT_SIZE = 50;69private static final int GROW_SIZE = 50;7071public static final Region EMPTY_REGION = new Region(0, 0, 0, 0);72public static final Region WHOLE_REGION = new Region(73Integer.MIN_VALUE,74Integer.MIN_VALUE,75Integer.MAX_VALUE,76Integer.MAX_VALUE);7778private int lox;79private int loy;80private int hix;81private int hiy;8283int endIndex;84int[] bands;8586private static native void initIDs();8788static {89initIDs();90}9192/**93* Adds the dimension {@code dim} to the coordinate94* {@code start} with appropriate clipping. If95* {@code dim} is non-positive then the method returns96* the start coordinate. If the sum overflows an integer97* data type then the method returns {@code Integer.MAX_VALUE}.98*/99public static int dimAdd(int start, int dim) {100if (dim <= 0) return start;101if ((dim += start) < start) return Integer.MAX_VALUE;102return dim;103}104105/**106* Adds the delta {@code dv} to the value {@code v} with107* appropriate clipping to the bounds of Integer resolution.108* If the answer would be greater than {@code Integer.MAX_VALUE}109* then {@code Integer.MAX_VALUE} is returned.110* If the answer would be less than {@code Integer.MIN_VALUE}111* then {@code Integer.MIN_VALUE} is returned.112* Otherwise the sum is returned.113*/114public static int clipAdd(int v, int dv) {115int newv = v + dv;116if ((newv > v) != (dv > 0)) {117newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;118}119return newv;120}121122/**123* Returns the closest {@code int} to the argument, with ties rounding to124* negative infinity.125* <p>126* Special cases:127* <ul><li>If the argument is NaN, the result is 0.128* <li>If the argument is negative infinity or any value less than or129* equal to the value of {@code Integer.MIN_VALUE}, the result is130* equal to the value of {@code Integer.MIN_VALUE}.131* <li>If the argument is positive infinity or any value greater than or132* equal to the value of {@code Integer.MAX_VALUE}, the result is133* equal to the value of {@code Integer.MAX_VALUE}.</ul>134*135* @param coordinate a floating-point value to be rounded to an integer136* @return the value of the argument rounded to the nearest137* {@code int} value.138*/139public static int clipRound(final double coordinate) {140final double newv = coordinate - 0.5;141if (newv < Integer.MIN_VALUE) {142return Integer.MIN_VALUE;143}144if (newv > Integer.MAX_VALUE) {145return Integer.MAX_VALUE;146}147return (int) Math.ceil(newv);148}149150/**151* Multiply the scale factor {@code sv} and the value {@code v} with152* appropriate clipping to the bounds of Integer resolution. If the answer153* would be greater than {@code Integer.MAX_VALUE} then {@code154* Integer.MAX_VALUE} is returned. If the answer would be less than {@code155* Integer.MIN_VALUE} then {@code Integer.MIN_VALUE} is returned. Otherwise156* the multiplication is returned.157*/158public static int clipScale(final int v, final double sv) {159if (sv == 1.0) {160return v;161}162final double newv = v * sv;163if (newv < Integer.MIN_VALUE) {164return Integer.MIN_VALUE;165}166if (newv > Integer.MAX_VALUE) {167return Integer.MAX_VALUE;168}169return (int) Math.round(newv);170}171172private Region(int lox, int loy, int hix, int hiy) {173this.lox = lox;174this.loy = loy;175this.hix = hix;176this.hiy = hiy;177}178179private Region(int lox, int loy, int hix, int hiy, int[] bands, int end) {180this.lox = lox;181this.loy = loy;182this.hix = hix;183this.hiy = hiy;184this.bands = bands;185this.endIndex = end;186}187188/**189* Returns a Region object covering the pixels which would be190* touched by a fill or clip operation on a Graphics implementation191* on the specified Shape object under the optionally specified192* AffineTransform object.193*194* @param s a non-null Shape object specifying the geometry enclosing195* the pixels of interest196* @param at an optional {@code AffineTransform} to be applied to the197* coordinates as they are returned in the iteration, or198* {@code null} if untransformed coordinates are desired199*/200public static Region getInstance(Shape s, AffineTransform at) {201return getInstance(WHOLE_REGION, false, s, at);202}203204/**205* Returns a Region object covering the pixels which would be206* touched by a fill or clip operation on a Graphics implementation207* on the specified Shape object under the optionally specified208* AffineTransform object further restricted by the specified209* device bounds.210* <p>211* Note that only the bounds of the specified Region are used to212* restrict the resulting Region.213* If devBounds is non-rectangular and clipping to the specific214* bands of devBounds is needed, then an intersection of the215* resulting Region with devBounds must be performed in a216* subsequent step.217*218* @param devBounds a non-null Region specifying some bounds to219* clip the geometry to220* @param s a non-null Shape object specifying the geometry enclosing221* the pixels of interest222* @param at an optional {@code AffineTransform} to be applied to the223* coordinates as they are returned in the iteration, or224* {@code null} if untransformed coordinates are desired225*/226public static Region getInstance(Region devBounds,227Shape s, AffineTransform at)228{229return getInstance(devBounds, false, s, at);230}231232/**233* Returns a Region object covering the pixels which would be234* touched by a fill or clip operation on a Graphics implementation235* on the specified Shape object under the optionally specified236* AffineTransform object further restricted by the specified237* device bounds.238* If the normalize parameter is true then coordinate normalization239* is performed as per the 2D Graphics non-antialiasing implementation240* of the VALUE_STROKE_NORMALIZE hint.241* <p>242* Note that only the bounds of the specified Region are used to243* restrict the resulting Region.244* If devBounds is non-rectangular and clipping to the specific245* bands of devBounds is needed, then an intersection of the246* resulting Region with devBounds must be performed in a247* subsequent step.248*249* @param devBounds a non-null Region specifying some bounds to250* clip the geometry to251* @param normalize a boolean indicating whether or not to apply252* normalization253* @param s a non-null Shape object specifying the geometry enclosing254* the pixels of interest255* @param at an optional {@code AffineTransform} to be applied to the256* coordinates as they are returned in the iteration, or257* {@code null} if untransformed coordinates are desired258*/259public static Region getInstance(Region devBounds, boolean normalize,260Shape s, AffineTransform at)261{262// Optimize for empty shapes to avoid involving the SpanIterator263if (s instanceof RectangularShape &&264((RectangularShape)s).isEmpty())265{266return EMPTY_REGION;267}268269int[] box = new int[4];270ShapeSpanIterator sr = new ShapeSpanIterator(normalize);271try {272sr.setOutputArea(devBounds);273sr.appendPath(s.getPathIterator(at));274sr.getPathBox(box);275return Region.getInstance(box, sr);276} finally {277sr.dispose();278}279}280281/**282* Returns a Region object with a rectangle of interest specified by the283* indicated rectangular area in lox, loy, hix, hiy and edges array, which284* is located relative to the rectangular area. Edges array - 0,1 are y285* range, 2N,2N+1 are x ranges, 1 per y range.286*287* @see TransformHelper288*/289static Region getInstance(final int lox, final int loy, final int hix,290final int hiy, final int[] edges) {291final int y1 = edges[0];292final int y2 = edges[1];293if (hiy <= loy || hix <= lox || y2 <= y1) {294return EMPTY_REGION;295}296// rowsNum * (3 + 1 * 2)297final int[] bands = new int[(y2 - y1) * 5];298int end = 0;299int index = 2;300for (int y = y1; y < y2; ++y) {301final int spanlox = Math.max(clipAdd(lox, edges[index++]), lox);302final int spanhix = Math.min(clipAdd(lox, edges[index++]), hix);303if (spanlox < spanhix) {304final int spanloy = Math.max(clipAdd(loy, y), loy);305final int spanhiy = Math.min(clipAdd(spanloy, 1), hiy);306if (spanloy < spanhiy) {307bands[end++] = spanloy;308bands[end++] = spanhiy;309bands[end++] = 1; // 1 span per row310bands[end++] = spanlox;311bands[end++] = spanhix;312}313}314}315return end != 0 ? new Region(lox, loy, hix, hiy, bands, end)316: EMPTY_REGION;317}318319/**320* Returns a Region object with a rectangle of interest specified321* by the indicated Rectangle object.322* <p>323* This method can also be used to create a simple rectangular324* region.325*/326public static Region getInstance(Rectangle r) {327return Region.getInstanceXYWH(r.x, r.y, r.width, r.height);328}329330/**331* Returns a Region object with a rectangle of interest specified332* by the indicated rectangular area in x, y, width, height format.333* <p>334* This method can also be used to create a simple rectangular335* region.336*/337public static Region getInstanceXYWH(int x, int y, int w, int h) {338return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h));339}340341/**342* Returns a Region object with a rectangle of interest specified343* by the indicated span array.344* <p>345* This method can also be used to create a simple rectangular346* region.347*/348public static Region getInstance(int[] box) {349return new Region(box[0], box[1], box[2], box[3]);350}351352/**353* Returns a Region object with a rectangle of interest specified354* by the indicated rectangular area in lox, loy, hix, hiy format.355* <p>356* This method can also be used to create a simple rectangular357* region.358*/359public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) {360return new Region(lox, loy, hix, hiy);361}362363/**364* Returns a Region object with a rectangle of interest specified by the365* indicated rectangular area in lox, loy, hix, hiy format.366* <p/>367* Appends the list of spans returned from the indicated SpanIterator. Each368* span must be at a higher starting Y coordinate than the previous data or369* it must have a Y range equal to the highest Y band in the region and a370* higher X coordinate than any of the spans in that band.371*/372public static Region getInstance(int[] box, SpanIterator si) {373Region ret = new Region(box[0], box[1], box[2], box[3]);374ret.appendSpans(si);375return ret;376}377378/**379* Appends the list of spans returned from the indicated380* SpanIterator. Each span must be at a higher starting381* Y coordinate than the previous data or it must have a382* Y range equal to the highest Y band in the region and a383* higher X coordinate than any of the spans in that band.384*/385private void appendSpans(SpanIterator si) {386int[] box = new int[6];387388while (si.nextSpan(box)) {389appendSpan(box);390}391392endRow(box);393calcBBox();394}395396/**397* Returns a Region object that represents the same list of rectangles as398* the current Region object, scaled by the specified sx, sy factors.399*/400public Region getScaledRegion(final double sx, final double sy) {401if (sx == 0 || sy == 0 || this == EMPTY_REGION) {402return EMPTY_REGION;403}404if ((sx == 1.0 && sy == 1.0) || (this == WHOLE_REGION)) {405return this;406}407408int tlox = clipScale(lox, sx);409int tloy = clipScale(loy, sy);410int thix = clipScale(hix, sx);411int thiy = clipScale(hiy, sy);412Region ret = new Region(tlox, tloy, thix, thiy);413int[] bands = this.bands;414if (bands != null) {415int end = endIndex;416int[] newbands = new int[end];417int i = 0; // index for source bands418int j = 0; // index for translated newbands419int ncol;420while (i < end) {421int y1, y2;422newbands[j++] = y1 = clipScale(bands[i++], sy);423newbands[j++] = y2 = clipScale(bands[i++], sy);424newbands[j++] = ncol = bands[i++];425int savej = j;426if (y1 < y2) {427while (--ncol >= 0) {428int x1 = clipScale(bands[i++], sx);429int x2 = clipScale(bands[i++], sx);430if (x1 < x2) {431newbands[j++] = x1;432newbands[j++] = x2;433}434}435} else {436i += ncol * 2;437}438// Did we get any non-empty bands in this row?439if (j > savej) {440newbands[savej-1] = (j - savej) / 2;441} else {442j = savej - 3;443}444}445if (j <= 5) {446if (j < 5) {447// No rows or bands were generated...448ret.lox = ret.loy = ret.hix = ret.hiy = 0;449} else {450// Only generated one single rect in the end...451ret.loy = newbands[0];452ret.hiy = newbands[1];453ret.lox = newbands[3];454ret.hix = newbands[4];455}456// ret.endIndex and ret.bands were never initialized...457// ret.endIndex = 0;458// ret.newbands = null;459} else {460// Generated multiple bands and/or multiple rows...461ret.endIndex = j;462ret.bands = newbands;463}464}465return ret;466}467468469/**470* Returns a Region object that represents the same list of471* rectangles as the current Region object, translated by472* the specified dx, dy translation factors.473*/474public Region getTranslatedRegion(int dx, int dy) {475if ((dx | dy) == 0) {476return this;477}478int tlox = lox + dx;479int tloy = loy + dy;480int thix = hix + dx;481int thiy = hiy + dy;482if ((tlox > lox) != (dx > 0) ||483(tloy > loy) != (dy > 0) ||484(thix > hix) != (dx > 0) ||485(thiy > hiy) != (dy > 0))486{487return getSafeTranslatedRegion(dx, dy);488}489Region ret = new Region(tlox, tloy, thix, thiy);490int[] bands = this.bands;491if (bands != null) {492int end = endIndex;493ret.endIndex = end;494int[] newbands = new int[end];495ret.bands = newbands;496int i = 0;497int ncol;498while (i < end) {499newbands[i] = bands[i] + dy; i++;500newbands[i] = bands[i] + dy; i++;501newbands[i] = ncol = bands[i]; i++;502while (--ncol >= 0) {503newbands[i] = bands[i] + dx; i++;504newbands[i] = bands[i] + dx; i++;505}506}507}508return ret;509}510511private Region getSafeTranslatedRegion(int dx, int dy) {512int tlox = clipAdd(lox, dx);513int tloy = clipAdd(loy, dy);514int thix = clipAdd(hix, dx);515int thiy = clipAdd(hiy, dy);516Region ret = new Region(tlox, tloy, thix, thiy);517int[] bands = this.bands;518if (bands != null) {519int end = endIndex;520int[] newbands = new int[end];521int i = 0; // index for source bands522int j = 0; // index for translated newbands523int ncol;524while (i < end) {525int y1, y2;526newbands[j++] = y1 = clipAdd(bands[i++], dy);527newbands[j++] = y2 = clipAdd(bands[i++], dy);528newbands[j++] = ncol = bands[i++];529int savej = j;530if (y1 < y2) {531while (--ncol >= 0) {532int x1 = clipAdd(bands[i++], dx);533int x2 = clipAdd(bands[i++], dx);534if (x1 < x2) {535newbands[j++] = x1;536newbands[j++] = x2;537}538}539} else {540i += ncol * 2;541}542// Did we get any non-empty bands in this row?543if (j > savej) {544newbands[savej-1] = (j - savej) / 2;545} else {546j = savej - 3;547}548}549if (j <= 5) {550if (j < 5) {551// No rows or bands were generated...552ret.lox = ret.loy = ret.hix = ret.hiy = 0;553} else {554// Only generated one single rect in the end...555ret.loy = newbands[0];556ret.hiy = newbands[1];557ret.lox = newbands[3];558ret.hix = newbands[4];559}560// ret.endIndex and ret.bands were never initialized...561// ret.endIndex = 0;562// ret.newbands = null;563} else {564// Generated multiple bands and/or multiple rows...565ret.endIndex = j;566ret.bands = newbands;567}568}569return ret;570}571572/**573* Returns a Region object that represents the intersection of574* this object with the specified Rectangle. The return value575* may be this same object if no clipping occurs.576*/577public Region getIntersection(Rectangle r) {578return getIntersectionXYWH(r.x, r.y, r.width, r.height);579}580581/**582* Returns a Region object that represents the intersection of583* this object with the specified rectangular area. The return584* value may be this same object if no clipping occurs.585*/586public Region getIntersectionXYWH(int x, int y, int w, int h) {587return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));588}589590/**591* Returns a Region object that represents the intersection of592* this object with the specified Rectangle2D. The return value593* may be this same object if no clipping occurs.594*/595public Region getIntersection(final Rectangle2D r) {596if (r instanceof Rectangle) {597return getIntersection((Rectangle) r);598}599return getIntersectionXYXY(r.getMinX(), r.getMinY(), r.getMaxX(),600r.getMaxY());601}602603/**604* Returns a Region object that represents the intersection of605* this object with the specified rectangular area. The return606* value may be this same object if no clipping occurs.607*/608public Region getIntersectionXYXY(double lox, double loy, double hix,609double hiy) {610if (isNaN(lox) || isNaN(loy) || isNaN(hix) || isNaN(hiy)) {611return EMPTY_REGION;612}613return getIntersectionXYXY(clipRound(lox), clipRound(loy),614clipRound(hix), clipRound(hiy));615}616617/**618* Returns a Region object that represents the intersection of619* this object with the specified rectangular area. The return620* value may be this same object if no clipping occurs.621*/622public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) {623if (isInsideXYXY(lox, loy, hix, hiy)) {624return this;625}626Region ret = new Region((lox < this.lox) ? this.lox : lox,627(loy < this.loy) ? this.loy : loy,628(hix > this.hix) ? this.hix : hix,629(hiy > this.hiy) ? this.hiy : hiy);630if (bands != null) {631ret.appendSpans(this.getSpanIterator());632}633return ret;634}635636/**637* Returns a Region object that represents the intersection of this638* object with the specified Region object.639* <p>640* If {@code A} and {@code B} are both Region Objects and641* {@code C = A.getIntersection(B);} then a point will642* be contained in {@code C} iff it is contained in both643* {@code A} and {@code B}.644* <p>645* The return value may be this same object or the argument646* Region object if no clipping occurs.647*/648public Region getIntersection(Region r) {649if (this.isInsideQuickCheck(r)) {650return this;651}652if (r.isInsideQuickCheck(this)) {653return r;654}655Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox,656(r.loy < this.loy) ? this.loy : r.loy,657(r.hix > this.hix) ? this.hix : r.hix,658(r.hiy > this.hiy) ? this.hiy : r.hiy);659if (!ret.isEmpty()) {660ret.filterSpans(this, r, INCLUDE_COMMON);661}662return ret;663}664665/**666* Returns a Region object that represents the union of this667* object with the specified Region object.668* <p>669* If {@code A} and {@code B} are both Region Objects and670* {@code C = A.getUnion(B);} then a point will671* be contained in {@code C} iff it is contained in either672* {@code A} or {@code B}.673* <p>674* The return value may be this same object or the argument675* Region object if no augmentation occurs.676*/677public Region getUnion(Region r) {678if (r.isEmpty() || r.isInsideQuickCheck(this)) {679return this;680}681if (this.isEmpty() || this.isInsideQuickCheck(r)) {682return r;683}684Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,685(r.loy > this.loy) ? this.loy : r.loy,686(r.hix < this.hix) ? this.hix : r.hix,687(r.hiy < this.hiy) ? this.hiy : r.hiy);688ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON);689return ret;690}691692/**693* Returns a Region object that represents the difference of the694* specified Region object subtracted from this object.695* <p>696* If {@code A} and {@code B} are both Region Objects and697* {@code C = A.getDifference(B);} then a point will698* be contained in {@code C} iff it is contained in699* {@code A} but not contained in {@code B}.700* <p>701* The return value may be this same object or the argument702* Region object if no clipping occurs.703*/704public Region getDifference(Region r) {705if (!r.intersectsQuickCheck(this)) {706return this;707}708if (this.isInsideQuickCheck(r)) {709return EMPTY_REGION;710}711Region ret = new Region(this.lox, this.loy, this.hix, this.hiy);712ret.filterSpans(this, r, INCLUDE_A);713return ret;714}715716/**717* Returns a Region object that represents the exclusive or of this718* object with the specified Region object.719* <p>720* If {@code A} and {@code B} are both Region Objects and721* {@code C = A.getExclusiveOr(B);} then a point will722* be contained in {@code C} iff it is contained in either723* {@code A} or {@code B}, but not if it is contained in both.724* <p>725* The return value may be this same object or the argument726* Region object if either is empty.727*/728public Region getExclusiveOr(Region r) {729if (r.isEmpty()) {730return this;731}732if (this.isEmpty()) {733return r;734}735Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,736(r.loy > this.loy) ? this.loy : r.loy,737(r.hix < this.hix) ? this.hix : r.hix,738(r.hiy < this.hiy) ? this.hiy : r.hiy);739ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B);740return ret;741}742743private static final int INCLUDE_A = 1;744private static final int INCLUDE_B = 2;745private static final int INCLUDE_COMMON = 4;746747private void filterSpans(Region ra, Region rb, int flags) {748int[] abands = ra.bands;749int[] bbands = rb.bands;750if (abands == null) {751abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix};752}753if (bbands == null) {754bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix};755}756int[] box = new int[6];757int acolstart = 0;758int ay1 = abands[acolstart++];759int ay2 = abands[acolstart++];760int acolend = abands[acolstart++];761acolend = acolstart + 2 * acolend;762int bcolstart = 0;763int by1 = bbands[bcolstart++];764int by2 = bbands[bcolstart++];765int bcolend = bbands[bcolstart++];766bcolend = bcolstart + 2 * bcolend;767int y = loy;768while (y < hiy) {769if (y >= ay2) {770if (acolend < ra.endIndex) {771acolstart = acolend;772ay1 = abands[acolstart++];773ay2 = abands[acolstart++];774acolend = abands[acolstart++];775acolend = acolstart + 2 * acolend;776} else {777if ((flags & INCLUDE_B) == 0) break;778ay1 = ay2 = hiy;779}780continue;781}782if (y >= by2) {783if (bcolend < rb.endIndex) {784bcolstart = bcolend;785by1 = bbands[bcolstart++];786by2 = bbands[bcolstart++];787bcolend = bbands[bcolstart++];788bcolend = bcolstart + 2 * bcolend;789} else {790if ((flags & INCLUDE_A) == 0) break;791by1 = by2 = hiy;792}793continue;794}795int yend;796if (y < by1) {797if (y < ay1) {798y = Math.min(ay1, by1);799continue;800}801// We are in a set of rows that belong only to A802yend = Math.min(ay2, by1);803if ((flags & INCLUDE_A) != 0) {804box[1] = y;805box[3] = yend;806int acol = acolstart;807while (acol < acolend) {808box[0] = abands[acol++];809box[2] = abands[acol++];810appendSpan(box);811}812}813} else if (y < ay1) {814// We are in a set of rows that belong only to B815yend = Math.min(by2, ay1);816if ((flags & INCLUDE_B) != 0) {817box[1] = y;818box[3] = yend;819int bcol = bcolstart;820while (bcol < bcolend) {821box[0] = bbands[bcol++];822box[2] = bbands[bcol++];823appendSpan(box);824}825}826} else {827// We are in a set of rows that belong to both A and B828yend = Math.min(ay2, by2);829box[1] = y;830box[3] = yend;831int acol = acolstart;832int bcol = bcolstart;833int ax1 = abands[acol++];834int ax2 = abands[acol++];835int bx1 = bbands[bcol++];836int bx2 = bbands[bcol++];837int x = Math.min(ax1, bx1);838if (x < lox) x = lox;839while (x < hix) {840if (x >= ax2) {841if (acol < acolend) {842ax1 = abands[acol++];843ax2 = abands[acol++];844} else {845if ((flags & INCLUDE_B) == 0) break;846ax1 = ax2 = hix;847}848continue;849}850if (x >= bx2) {851if (bcol < bcolend) {852bx1 = bbands[bcol++];853bx2 = bbands[bcol++];854} else {855if ((flags & INCLUDE_A) == 0) break;856bx1 = bx2 = hix;857}858continue;859}860int xend;861boolean appendit;862if (x < bx1) {863if (x < ax1) {864xend = Math.min(ax1, bx1);865appendit = false;866} else {867xend = Math.min(ax2, bx1);868appendit = ((flags & INCLUDE_A) != 0);869}870} else if (x < ax1) {871xend = Math.min(ax1, bx2);872appendit = ((flags & INCLUDE_B) != 0);873} else {874xend = Math.min(ax2, bx2);875appendit = ((flags & INCLUDE_COMMON) != 0);876}877if (appendit) {878box[0] = x;879box[2] = xend;880appendSpan(box);881}882x = xend;883}884}885y = yend;886}887endRow(box);888calcBBox();889}890891/**892* Returns a Region object that represents the bounds of the893* intersection of this object with the bounds of the specified894* Region object.895* <p>896* The return value may be this same object if no clipping occurs897* and this Region is rectangular.898*/899public Region getBoundsIntersection(Rectangle r) {900return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height);901}902903/**904* Returns a Region object that represents the bounds of the905* intersection of this object with the bounds of the specified906* rectangular area in x, y, width, height format.907* <p>908* The return value may be this same object if no clipping occurs909* and this Region is rectangular.910*/911public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) {912return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));913}914915/**916* Returns a Region object that represents the bounds of the917* intersection of this object with the bounds of the specified918* rectangular area in lox, loy, hix, hiy format.919* <p>920* The return value may be this same object if no clipping occurs921* and this Region is rectangular.922*/923public Region getBoundsIntersectionXYXY(int lox, int loy,924int hix, int hiy)925{926if (this.bands == null &&927this.lox >= lox && this.loy >= loy &&928this.hix <= hix && this.hiy <= hiy)929{930return this;931}932return new Region((lox < this.lox) ? this.lox : lox,933(loy < this.loy) ? this.loy : loy,934(hix > this.hix) ? this.hix : hix,935(hiy > this.hiy) ? this.hiy : hiy);936}937938/**939* Returns a Region object that represents the intersection of940* this object with the bounds of the specified Region object.941* <p>942* The return value may be this same object or the argument943* Region object if no clipping occurs and the Regions are944* rectangular.945*/946public Region getBoundsIntersection(Region r) {947if (this.encompasses(r)) {948return r;949}950if (r.encompasses(this)) {951return this;952}953return new Region((r.lox < this.lox) ? this.lox : r.lox,954(r.loy < this.loy) ? this.loy : r.loy,955(r.hix > this.hix) ? this.hix : r.hix,956(r.hiy > this.hiy) ? this.hiy : r.hiy);957}958959/**960* Appends a single span defined by the 4 parameters961* spanlox, spanloy, spanhix, spanhiy.962* This span must be at a higher starting Y coordinate than963* the previous data or it must have a Y range equal to the964* highest Y band in the region and a higher X coordinate965* than any of the spans in that band.966*/967private void appendSpan(int[] box) {968int spanlox, spanloy, spanhix, spanhiy;969if ((spanlox = box[0]) < lox) spanlox = lox;970if ((spanloy = box[1]) < loy) spanloy = loy;971if ((spanhix = box[2]) > hix) spanhix = hix;972if ((spanhiy = box[3]) > hiy) spanhiy = hiy;973if (spanhix <= spanlox || spanhiy <= spanloy) {974return;975}976977int curYrow = box[4];978if (endIndex == 0 || spanloy >= bands[curYrow + 1]) {979if (bands == null) {980bands = new int[INIT_SIZE];981} else {982needSpace(5);983endRow(box);984curYrow = box[4];985}986bands[endIndex++] = spanloy;987bands[endIndex++] = spanhiy;988bands[endIndex++] = 0;989} else if (spanloy == bands[curYrow] &&990spanhiy == bands[curYrow + 1] &&991spanlox >= bands[endIndex - 1]) {992if (spanlox == bands[endIndex - 1]) {993bands[endIndex - 1] = spanhix;994return;995}996needSpace(2);997} else {998throw new InternalError("bad span");999}1000bands[endIndex++] = spanlox;1001bands[endIndex++] = spanhix;1002bands[curYrow + 2]++;1003}10041005private void needSpace(int num) {1006if (endIndex + num >= bands.length) {1007int[] newbands = new int[bands.length + GROW_SIZE];1008System.arraycopy(bands, 0, newbands, 0, endIndex);1009bands = newbands;1010}1011}10121013private void endRow(int[] box) {1014int cur = box[4];1015int prev = box[5];1016if (cur > prev) {1017int[] bands = this.bands;1018if (bands[prev + 1] == bands[cur] &&1019bands[prev + 2] == bands[cur + 2])1020{1021int num = bands[cur + 2] * 2;1022cur += 3;1023prev += 3;1024while (num > 0) {1025if (bands[cur++] != bands[prev++]) {1026break;1027}1028num--;1029}1030if (num == 0) {1031// prev == box[4]1032bands[box[5] + 1] = bands[prev + 1];1033endIndex = prev;1034return;1035}1036}1037}1038box[5] = box[4];1039box[4] = endIndex;1040}10411042private void calcBBox() {1043int[] bands = this.bands;1044if (endIndex <= 5) {1045if (endIndex == 0) {1046lox = loy = hix = hiy = 0;1047} else {1048loy = bands[0];1049hiy = bands[1];1050lox = bands[3];1051hix = bands[4];1052endIndex = 0;1053}1054this.bands = null;1055return;1056}1057int lox = this.hix;1058int hix = this.lox;1059int hiyindex = 0;10601061int i = 0;1062while (i < endIndex) {1063hiyindex = i;1064int numbands = bands[i + 2];1065i += 3;1066if (lox > bands[i]) {1067lox = bands[i];1068}1069i += numbands * 2;1070if (hix < bands[i - 1]) {1071hix = bands[i - 1];1072}1073}10741075this.lox = lox;1076this.loy = bands[0];1077this.hix = hix;1078this.hiy = bands[hiyindex + 1];1079}10801081/**1082* Returns the lowest X coordinate in the Region.1083*/1084public int getLoX() {1085return lox;1086}10871088/**1089* Returns the lowest Y coordinate in the Region.1090*/1091public int getLoY() {1092return loy;1093}10941095/**1096* Returns the highest X coordinate in the Region.1097*/1098public int getHiX() {1099return hix;1100}11011102/**1103* Returns the highest Y coordinate in the Region.1104*/1105public int getHiY() {1106return hiy;1107}11081109/**1110* Returns the width of this Region clipped to the range (0 - MAX_INT).1111*/1112public int getWidth() {1113if (hix < lox) return 0;1114int w;1115if ((w = hix - lox) < 0) {1116w = Integer.MAX_VALUE;1117}1118return w;1119}11201121/**1122* Returns the height of this Region clipped to the range (0 - MAX_INT).1123*/1124public int getHeight() {1125if (hiy < loy) return 0;1126int h;1127if ((h = hiy - loy) < 0) {1128h = Integer.MAX_VALUE;1129}1130return h;1131}11321133/**1134* Returns true iff this Region encloses no area.1135*/1136public boolean isEmpty() {1137return (hix <= lox || hiy <= loy);1138}11391140/**1141* Returns true iff this Region represents a single simple1142* rectangular area.1143*/1144public boolean isRectangular() {1145return (bands == null);1146}11471148/**1149* Returns true iff this Region contains the specified coordinate.1150*/1151public boolean contains(int x, int y) {1152if (x < lox || x >= hix || y < loy || y >= hiy) return false;1153if (bands == null) return true;1154int i = 0;1155while (i < endIndex) {1156if (y < bands[i++]) {1157return false;1158}1159if (y >= bands[i++]) {1160int numspans = bands[i++];1161i += numspans * 2;1162} else {1163int end = bands[i++];1164end = i + end * 2;1165while (i < end) {1166if (x < bands[i++]) return false;1167if (x < bands[i++]) return true;1168}1169return false;1170}1171}1172return false;1173}11741175/**1176* Returns true iff this Region lies inside the indicated1177* rectangular area specified in x, y, width, height format1178* with appropriate clipping performed as per the dimAdd method.1179*/1180public boolean isInsideXYWH(int x, int y, int w, int h) {1181return isInsideXYXY(x, y, dimAdd(x, w), dimAdd(y, h));1182}11831184/**1185* Returns true iff this Region lies inside the indicated1186* rectangular area specified in lox, loy, hix, hiy format.1187*/1188public boolean isInsideXYXY(int lox, int loy, int hix, int hiy) {1189return (this.lox >= lox && this.loy >= loy &&1190this.hix <= hix && this.hiy <= hiy);11911192}11931194/**1195* Quickly checks if this Region lies inside the specified1196* Region object.1197* <p>1198* This method will return false if the specified Region1199* object is not a simple rectangle.1200*/1201public boolean isInsideQuickCheck(Region r) {1202return (r.bands == null &&1203r.lox <= this.lox && r.loy <= this.loy &&1204r.hix >= this.hix && r.hiy >= this.hiy);1205}12061207/**1208* Quickly checks if this Region intersects the specified1209* rectangular area specified in lox, loy, hix, hiy format.1210* <p>1211* This method tests only against the bounds of this region1212* and does not bother to test if the rectangular region1213* actually intersects any bands.1214*/1215public boolean intersectsQuickCheckXYXY(int lox, int loy,1216int hix, int hiy)1217{1218return (hix > this.lox && lox < this.hix &&1219hiy > this.loy && loy < this.hiy);1220}12211222/**1223* Quickly checks if this Region intersects the specified1224* Region object.1225* <p>1226* This method tests only against the bounds of this region1227* and does not bother to test if the rectangular region1228* actually intersects any bands.1229*/1230public boolean intersectsQuickCheck(Region r) {1231return (r.hix > this.lox && r.lox < this.hix &&1232r.hiy > this.loy && r.loy < this.hiy);1233}12341235/**1236* Quickly checks if this Region surrounds the specified1237* Region object.1238* <p>1239* This method will return false if this Region object is1240* not a simple rectangle.1241*/1242public boolean encompasses(Region r) {1243return (this.bands == null &&1244this.lox <= r.lox && this.loy <= r.loy &&1245this.hix >= r.hix && this.hiy >= r.hiy);1246}12471248/**1249* Quickly checks if this Region surrounds the specified1250* rectangular area specified in x, y, width, height format.1251* <p>1252* This method will return false if this Region object is1253* not a simple rectangle.1254*/1255public boolean encompassesXYWH(int x, int y, int w, int h) {1256return encompassesXYXY(x, y, dimAdd(x, w), dimAdd(y, h));1257}12581259/**1260* Quickly checks if this Region surrounds the specified1261* rectangular area specified in lox, loy, hix, hiy format.1262* <p>1263* This method will return false if this Region object is1264* not a simple rectangle.1265*/1266public boolean encompassesXYXY(int lox, int loy, int hix, int hiy) {1267return (this.bands == null &&1268this.lox <= lox && this.loy <= loy &&1269this.hix >= hix && this.hiy >= hiy);1270}12711272/**1273* Gets the bbox of the available spans, clipped to the OutputArea.1274*/1275public void getBounds(int[] pathbox) {1276pathbox[0] = lox;1277pathbox[1] = loy;1278pathbox[2] = hix;1279pathbox[3] = hiy;1280}12811282/**1283* Clips the indicated bbox array to the bounds of this Region.1284*/1285public void clipBoxToBounds(int[] bbox) {1286if (bbox[0] < lox) bbox[0] = lox;1287if (bbox[1] < loy) bbox[1] = loy;1288if (bbox[2] > hix) bbox[2] = hix;1289if (bbox[3] > hiy) bbox[3] = hiy;1290}12911292/**1293* Gets an iterator object to iterate over the spans in this region.1294*/1295public RegionIterator getIterator() {1296return new RegionIterator(this);1297}12981299/**1300* Gets a span iterator object that iterates over the spans in this region1301*/1302public SpanIterator getSpanIterator() {1303return new RegionSpanIterator(this);1304}13051306/**1307* Gets a span iterator object that iterates over the spans in this region1308* but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi).1309*/1310public SpanIterator getSpanIterator(int[] bbox) {1311SpanIterator result = getSpanIterator();1312result.intersectClipBox(bbox[0], bbox[1], bbox[2], bbox[3]);1313return result;1314}13151316/**1317* Returns a SpanIterator that is the argument iterator filtered by1318* this region.1319*/1320public SpanIterator filter(SpanIterator si) {1321if (bands == null) {1322si.intersectClipBox(lox, loy, hix, hiy);1323} else {1324si = new RegionClipSpanIterator(this, si);1325}1326return si;1327}13281329@Override1330public String toString() {1331StringBuilder sb = new StringBuilder();1332sb.append("Region[[");1333sb.append(lox);1334sb.append(", ");1335sb.append(loy);1336sb.append(" => ");1337sb.append(hix);1338sb.append(", ");1339sb.append(hiy);1340sb.append(']');1341if (bands != null) {1342int col = 0;1343while (col < endIndex) {1344sb.append("y{");1345sb.append(bands[col++]);1346sb.append(',');1347sb.append(bands[col++]);1348sb.append("}[");1349int end = bands[col++];1350end = col + end * 2;1351while (col < end) {1352sb.append("x(");1353sb.append(bands[col++]);1354sb.append(", ");1355sb.append(bands[col++]);1356sb.append(')');1357}1358sb.append(']');1359}1360}1361sb.append(']');1362return sb.toString();1363}13641365@Override1366public int hashCode() {1367return (isEmpty() ? 0 : (lox * 3 + loy * 5 + hix * 7 + hiy * 9));1368}13691370@Override1371public boolean equals(Object o) {1372if (this == o) {1373return true;1374}1375if (!(o instanceof Region)) {1376return false;1377}1378Region r = (Region) o;1379if (this.isEmpty()) {1380return r.isEmpty();1381} else if (r.isEmpty()) {1382return false;1383}1384if (r.lox != this.lox || r.loy != this.loy ||1385r.hix != this.hix || r.hiy != this.hiy)1386{1387return false;1388}1389if (this.bands == null) {1390return (r.bands == null);1391} else if (r.bands == null) {1392return false;1393}1394if (this.endIndex != r.endIndex) {1395return false;1396}1397int[] abands = this.bands;1398int[] bbands = r.bands;1399for (int i = 0; i < endIndex; i++) {1400if (abands[i] != bbands[i]) {1401return false;1402}1403}1404return true;1405}1406}140714081409