Home » openjdk-7 » sun » java2d » pipe » [javadoc | source]

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

Home » openjdk-7 » sun » java2d » pipe » [javadoc | source]