< prev index next >

openjfx9/modules/javafx.graphics/src/main/java/com/sun/javafx/geom/Path2D.java

Print this page


   1 /*
   2  * Copyright (c) 2006, 2014, 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 com.sun.javafx.geom;
  27 
  28 import com.sun.javafx.geom.transform.BaseTransform;

  29 
  30 /**
  31  * The {@code Path2D} class provides a simple, yet flexible
  32  * shape which represents an arbitrary geometric path.
  33  * It can fully represent any path which can be iterated by the
  34  * {@link PathIterator} interface including all of its segment
  35  * types and winding rules and it implements all of the
  36  * basic hit testing methods of the {@link Shape} interface.
  37  * <p>
  38  * Use {@link Path2D} when dealing with data that can be represented
  39  * and used with floating point precision.
  40  * <p>
  41  * {@code Path2D} provides exactly those facilities required for
  42  * basic construction and management of a geometric path and
  43  * implementation of the above interfaces with little added
  44  * interpretation.
  45  * If it is useful to manipulate the interiors of closed
  46  * geometric shapes beyond simple hit testing then the
  47  * {@link Area} class provides additional capabilities
  48  * specifically targeted at closed figures.


  84      *
  85      * @see PathIterator#WIND_NON_ZERO
  86      */
  87     public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
  88 
  89     // For code simplicity, copy these constants to our namespace
  90     // and cast them to byte constants for easy storage.
  91     private static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
  92     private static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
  93     private static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
  94     private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
  95     private static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
  96 
  97     byte[] pointTypes;
  98     int numTypes;
  99     int numCoords;
 100     int windingRule;
 101 
 102     static final int INIT_SIZE = 20;
 103     static final int EXPAND_MAX = 500;

 104 
 105     float floatCoords[];
 106     float moveX, moveY;
 107     float prevX, prevY;
 108     float currX, currY;
 109 
 110     /**
 111      * Constructs a new empty single precision {@code Path2D} object
 112      * with a default winding rule of {@link #WIND_NON_ZERO}.
 113      */
 114     public Path2D() {
 115         this(WIND_NON_ZERO, INIT_SIZE);
 116     }
 117 
 118     /**
 119      * Constructs a new empty single precision {@code Path2D} object
 120      * with the specified winding rule to control operations that
 121      * require the interior of the path to be defined.
 122      *
 123      * @param rule the winding rule


 159     public Path2D(Shape s) {
 160         this(s, null);
 161     }
 162 
 163     /**
 164      * Constructs a new single precision {@code Path2D} object
 165      * from an arbitrary {@link Shape} object, transformed by an
 166      * {@link BaseTransform} object.
 167      * All of the initial geometry and the winding rule for this path are
 168      * taken from the specified {@code Shape} object and transformed
 169      * by the specified {@code BaseTransform} object.
 170      *
 171      * @param s the specified {@code Shape} object
 172      * @param tx the specified {@code BaseTransform} object
 173      */
 174     public Path2D(Shape s, BaseTransform tx) {
 175         if (s instanceof Path2D) {
 176             Path2D p2d = (Path2D) s;
 177             setWindingRule(p2d.windingRule);
 178             this.numTypes = p2d.numTypes;
 179             //this.pointTypes = Arrays.copyOf(p2d.pointTypes,
 180             //                                p2d.pointTypes.length); // jk16 dependency
 181             this.pointTypes = copyOf(p2d.pointTypes,
 182                                             p2d.pointTypes.length);
 183             this.numCoords = p2d.numCoords;
 184             if (tx == null || tx.isIdentity()) {
 185                 this.floatCoords = copyOf(p2d.floatCoords, numCoords);
 186                 this.moveX = p2d.moveX;
 187                 this.moveY = p2d.moveY;
 188                 this.prevX = p2d.prevX;
 189                 this.prevY = p2d.prevY;
 190                 this.currX = p2d.currX;
 191                 this.currY = p2d.currY;
 192             } else {
 193                 this.floatCoords = new float[numCoords + 6];
 194                 tx.transform(p2d.floatCoords, 0, this.floatCoords, 0, numCoords / 2);
 195                 floatCoords[numCoords + 0] = moveX;
 196                 floatCoords[numCoords + 1] = moveY;
 197                 floatCoords[numCoords + 2] = prevX;
 198                 floatCoords[numCoords + 3] = prevY;
 199                 floatCoords[numCoords + 4] = currX;
 200                 floatCoords[numCoords + 5] = currY;
 201                 tx.transform(this.floatCoords, numCoords, this.floatCoords, numCoords, 3);
 202                 moveX = floatCoords[numCoords + 0];
 203                 moveY = floatCoords[numCoords + 1];
 204                 prevX = floatCoords[numCoords + 2];
 205                 prevY = floatCoords[numCoords + 3];


 319             //     (x1, y1) -> (x3, y3)
 320             // We also need to deal with upside down and/or backwards rectangles
 321             int x, y, w, h;
 322             if (x2 < x0) { x = x2; w = x0 - x2; }
 323             else         { x = x0; w = x2 - x0; }
 324             if (y2 < y0) { y = y2; h = y0 - y2; }
 325             else         { y = y0; h = y2 - y0; }
 326             // Overflow protection...
 327             if (w < 0) return false;
 328             if (h < 0) return false;
 329 
 330             if (retrect != null) {
 331                 retrect.setBounds(x, y, w, h);
 332             }
 333             return true;
 334         }
 335         return false;
 336     }
 337 
 338     void needRoom(boolean needMove, int newCoords) {
 339         if (needMove && numTypes == 0) {
 340             throw new IllegalPathStateException("missing initial moveto "+
 341                                                 "in path definition");
 342         }
 343         int size = pointTypes.length;
 344         if (size == 0) {
 345             pointTypes = new byte[2];
 346         } else if (numTypes >= size) {
 347             int grow = size;
 348             if (grow > EXPAND_MAX) {
 349                 grow = EXPAND_MAX;
 350             }
 351             //pointTypes = Arrays.copyOf(pointTypes, size+grow); // jk16 dependency
 352             pointTypes = copyOf(pointTypes, size+grow);
 353         }
 354         size = floatCoords.length;
 355         if (numCoords + newCoords > size) {
 356             int grow = size;
 357             if (grow > EXPAND_MAX * 2) {
 358                 grow = EXPAND_MAX * 2;

































 359             }
 360             if (grow < newCoords) {
 361                 grow = newCoords;

































 362             }
 363             //floatCoords = Arrays.copyOf(floatCoords, size+grow); // jk16 dependency
 364             floatCoords = copyOf(floatCoords, size+grow);
 365         }
 366     }
 367 
 368     /**
 369      * Adds a point to the path by moving to the specified
 370      * coordinates specified in float precision.
 371      *
 372      * @param x the specified X coordinate
 373      * @param y the specified Y coordinate
 374      */
 375     public final void moveTo(float x, float y) {
 376         if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
 377             floatCoords[numCoords-2] = moveX = prevX = currX = x;
 378             floatCoords[numCoords-1] = moveY = prevY = currY = y;
 379         } else {
 380             needRoom(false, 2);
 381             pointTypes[numTypes++] = SEG_MOVETO;
 382             floatCoords[numCoords++] = moveX = prevX = currX = x;
 383             floatCoords[numCoords++] = moveY = prevY = currY = y;
 384         }


2271         int typeIdx;
2272         int pointIdx;
2273         Path2D path;
2274 
2275         Iterator(Path2D path) {
2276             this.path = path;
2277         }
2278 
2279         public int getWindingRule() {
2280             return path.getWindingRule();
2281         }
2282 
2283         public boolean isDone() {
2284             return (typeIdx >= path.numTypes);
2285         }
2286 
2287         public void next() {
2288             int type = path.pointTypes[typeIdx++];
2289             pointIdx += curvecoords[type];
2290         }
2291     }
2292 
2293     // jk16 dependency methods
2294     static byte[] copyOf(byte[] original, int newLength) {
2295         byte[] copy = new byte[newLength];
2296         System.arraycopy(original, 0, copy, 0,
2297                          Math.min(original.length, newLength));
2298         return copy;
2299     }
2300     static float[] copyOf(float[] original, int newLength) {
2301         float[] copy = new float[newLength];
2302         System.arraycopy(original, 0, copy, 0,
2303                          Math.min(original.length, newLength));
2304         return copy;
2305     }
2306 
2307     public void setTo(Path2D otherPath) {
2308         numTypes = otherPath.numTypes;
2309         numCoords = otherPath.numCoords;
2310         if (numTypes > pointTypes.length) {
2311             pointTypes = new byte[numTypes];
2312         }
2313         System.arraycopy(otherPath.pointTypes, 0, pointTypes, 0, numTypes);
2314         if (numCoords > floatCoords.length) {
2315             floatCoords = new float[numCoords];
2316         }
2317         System.arraycopy(otherPath.floatCoords, 0, floatCoords, 0, numCoords);
2318         windingRule = otherPath.windingRule;
2319         moveX = otherPath.moveX;
2320         moveY = otherPath.moveY;
2321         prevX = otherPath.prevX;
2322         prevY = otherPath.prevY;
2323         currX = otherPath.currX;
2324         currY = otherPath.currY;
   1 /*
   2  * Copyright (c) 2006, 2016, 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 com.sun.javafx.geom;
  27 
  28 import com.sun.javafx.geom.transform.BaseTransform;
  29 import java.util.Arrays;
  30 
  31 /**
  32  * The {@code Path2D} class provides a simple, yet flexible
  33  * shape which represents an arbitrary geometric path.
  34  * It can fully represent any path which can be iterated by the
  35  * {@link PathIterator} interface including all of its segment
  36  * types and winding rules and it implements all of the
  37  * basic hit testing methods of the {@link Shape} interface.
  38  * <p>
  39  * Use {@link Path2D} when dealing with data that can be represented
  40  * and used with floating point precision.
  41  * <p>
  42  * {@code Path2D} provides exactly those facilities required for
  43  * basic construction and management of a geometric path and
  44  * implementation of the above interfaces with little added
  45  * interpretation.
  46  * If it is useful to manipulate the interiors of closed
  47  * geometric shapes beyond simple hit testing then the
  48  * {@link Area} class provides additional capabilities
  49  * specifically targeted at closed figures.


  85      *
  86      * @see PathIterator#WIND_NON_ZERO
  87      */
  88     public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
  89 
  90     // For code simplicity, copy these constants to our namespace
  91     // and cast them to byte constants for easy storage.
  92     private static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
  93     private static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
  94     private static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
  95     private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
  96     private static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
  97 
  98     byte[] pointTypes;
  99     int numTypes;
 100     int numCoords;
 101     int windingRule;
 102 
 103     static final int INIT_SIZE = 20;
 104     static final int EXPAND_MAX = 500;
 105     static final int EXPAND_MAX_COORDS = EXPAND_MAX * 2;
 106 
 107     float floatCoords[];
 108     float moveX, moveY;
 109     float prevX, prevY;
 110     float currX, currY;
 111 
 112     /**
 113      * Constructs a new empty single precision {@code Path2D} object
 114      * with a default winding rule of {@link #WIND_NON_ZERO}.
 115      */
 116     public Path2D() {
 117         this(WIND_NON_ZERO, INIT_SIZE);
 118     }
 119 
 120     /**
 121      * Constructs a new empty single precision {@code Path2D} object
 122      * with the specified winding rule to control operations that
 123      * require the interior of the path to be defined.
 124      *
 125      * @param rule the winding rule


 161     public Path2D(Shape s) {
 162         this(s, null);
 163     }
 164 
 165     /**
 166      * Constructs a new single precision {@code Path2D} object
 167      * from an arbitrary {@link Shape} object, transformed by an
 168      * {@link BaseTransform} object.
 169      * All of the initial geometry and the winding rule for this path are
 170      * taken from the specified {@code Shape} object and transformed
 171      * by the specified {@code BaseTransform} object.
 172      *
 173      * @param s the specified {@code Shape} object
 174      * @param tx the specified {@code BaseTransform} object
 175      */
 176     public Path2D(Shape s, BaseTransform tx) {
 177         if (s instanceof Path2D) {
 178             Path2D p2d = (Path2D) s;
 179             setWindingRule(p2d.windingRule);
 180             this.numTypes = p2d.numTypes;
 181             this.pointTypes = Arrays.copyOf(p2d.pointTypes, numTypes);



 182             this.numCoords = p2d.numCoords;
 183             if (tx == null || tx.isIdentity()) {
 184                 this.floatCoords = Arrays.copyOf(p2d.floatCoords, numCoords);
 185                 this.moveX = p2d.moveX;
 186                 this.moveY = p2d.moveY;
 187                 this.prevX = p2d.prevX;
 188                 this.prevY = p2d.prevY;
 189                 this.currX = p2d.currX;
 190                 this.currY = p2d.currY;
 191             } else {
 192                 this.floatCoords = new float[numCoords + 6];
 193                 tx.transform(p2d.floatCoords, 0, this.floatCoords, 0, numCoords / 2);
 194                 floatCoords[numCoords + 0] = moveX;
 195                 floatCoords[numCoords + 1] = moveY;
 196                 floatCoords[numCoords + 2] = prevX;
 197                 floatCoords[numCoords + 3] = prevY;
 198                 floatCoords[numCoords + 4] = currX;
 199                 floatCoords[numCoords + 5] = currY;
 200                 tx.transform(this.floatCoords, numCoords, this.floatCoords, numCoords, 3);
 201                 moveX = floatCoords[numCoords + 0];
 202                 moveY = floatCoords[numCoords + 1];
 203                 prevX = floatCoords[numCoords + 2];
 204                 prevY = floatCoords[numCoords + 3];


 318             //     (x1, y1) -> (x3, y3)
 319             // We also need to deal with upside down and/or backwards rectangles
 320             int x, y, w, h;
 321             if (x2 < x0) { x = x2; w = x0 - x2; }
 322             else         { x = x0; w = x2 - x0; }
 323             if (y2 < y0) { y = y2; h = y0 - y2; }
 324             else         { y = y0; h = y2 - y0; }
 325             // Overflow protection...
 326             if (w < 0) return false;
 327             if (h < 0) return false;
 328 
 329             if (retrect != null) {
 330                 retrect.setBounds(x, y, w, h);
 331             }
 332             return true;
 333         }
 334         return false;
 335     }
 336 
 337     void needRoom(boolean needMove, int newCoords) {
 338         if (needMove && (numTypes == 0)) {
 339             throw new IllegalPathStateException("missing initial moveto "+
 340                                                 "in path definition");
 341         }
 342         int size = pointTypes.length;
 343         if (size == 0) {
 344             pointTypes = new byte[2];
 345         } else if (numTypes >= size) {
 346             pointTypes = expandPointTypes(pointTypes, 1);





 347         }
 348         size = floatCoords.length;
 349         if (numCoords > (floatCoords.length - newCoords)) {
 350             floatCoords = expandCoords(floatCoords, newCoords);
 351         }
 352     }
 353 
 354     static byte[] expandPointTypes(byte[] oldPointTypes, int needed) {
 355         final int oldSize = oldPointTypes.length;
 356         final int newSizeMin = oldSize + needed;
 357         if (newSizeMin < oldSize) {
 358             // hard overflow failure - we can't even accommodate
 359             // new items without overflowing
 360             throw new ArrayIndexOutOfBoundsException(
 361                           "pointTypes exceeds maximum capacity !");
 362         }
 363         // growth algorithm computation
 364         int grow = oldSize;
 365         if (grow > EXPAND_MAX) {
 366             grow = Math.max(EXPAND_MAX, oldSize >> 3); // 1/8th min
 367         } else if (grow < INIT_SIZE) {
 368             grow = INIT_SIZE; // ensure > 6 (cubics)
 369         }
 370         assert grow > 0;
 371 
 372         int newSize = oldSize + grow;
 373         if (newSize < newSizeMin) {
 374             // overflow in growth algorithm computation
 375             newSize = Integer.MAX_VALUE;
 376         }
 377 
 378         while (true) {
 379             try {
 380                 // try allocating the larger array
 381                 return Arrays.copyOf(oldPointTypes, newSize);
 382             } catch (OutOfMemoryError oome) {
 383                 if (newSize == newSizeMin) {
 384                     throw oome;
 385                 }
 386             }
 387             newSize = newSizeMin + (newSize - newSizeMin) / 2;
 388         }
 389     }
 390 
 391     static float[] expandCoords(float[] oldCoords, int needed) {
 392         final int oldSize = oldCoords.length;
 393         final int newSizeMin = oldSize + needed;
 394         if (newSizeMin < oldSize) {
 395             // hard overflow failure - we can't even accommodate
 396             // new items without overflowing
 397             throw new ArrayIndexOutOfBoundsException(
 398                           "coords exceeds maximum capacity !");
 399         }
 400         // growth algorithm computation
 401         int grow = oldSize;
 402         if (grow > EXPAND_MAX_COORDS) {
 403             grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
 404         } else if (grow < INIT_SIZE) {
 405             grow = INIT_SIZE; // ensure > 6 (cubics)
 406         }
 407         assert grow > needed;
 408 
 409         int newSize = oldSize + grow;
 410         if (newSize < newSizeMin) {
 411             // overflow in growth algorithm computation
 412             newSize = Integer.MAX_VALUE;
 413         }
 414         while (true) {
 415             try {
 416                 // try allocating the larger array
 417                 return Arrays.copyOf(oldCoords, newSize);
 418             } catch (OutOfMemoryError oome) {
 419                 if (newSize == newSizeMin) {
 420                     throw oome;
 421                 }
 422             }
 423             newSize = newSizeMin + (newSize - newSizeMin) / 2;

 424         }
 425     }
 426 
 427     /**
 428      * Adds a point to the path by moving to the specified
 429      * coordinates specified in float precision.
 430      *
 431      * @param x the specified X coordinate
 432      * @param y the specified Y coordinate
 433      */
 434     public final void moveTo(float x, float y) {
 435         if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
 436             floatCoords[numCoords-2] = moveX = prevX = currX = x;
 437             floatCoords[numCoords-1] = moveY = prevY = currY = y;
 438         } else {
 439             needRoom(false, 2);
 440             pointTypes[numTypes++] = SEG_MOVETO;
 441             floatCoords[numCoords++] = moveX = prevX = currX = x;
 442             floatCoords[numCoords++] = moveY = prevY = currY = y;
 443         }


2330         int typeIdx;
2331         int pointIdx;
2332         Path2D path;
2333 
2334         Iterator(Path2D path) {
2335             this.path = path;
2336         }
2337 
2338         public int getWindingRule() {
2339             return path.getWindingRule();
2340         }
2341 
2342         public boolean isDone() {
2343             return (typeIdx >= path.numTypes);
2344         }
2345 
2346         public void next() {
2347             int type = path.pointTypes[typeIdx++];
2348             pointIdx += curvecoords[type];
2349         }














2350     }
2351 
2352     public void setTo(Path2D otherPath) {
2353         numTypes = otherPath.numTypes;
2354         numCoords = otherPath.numCoords;
2355         if (numTypes > pointTypes.length) {
2356             pointTypes = new byte[numTypes];
2357         }
2358         System.arraycopy(otherPath.pointTypes, 0, pointTypes, 0, numTypes);
2359         if (numCoords > floatCoords.length) {
2360             floatCoords = new float[numCoords];
2361         }
2362         System.arraycopy(otherPath.floatCoords, 0, floatCoords, 0, numCoords);
2363         windingRule = otherPath.windingRule;
2364         moveX = otherPath.moveX;
2365         moveY = otherPath.moveY;
2366         prevX = otherPath.prevX;
2367         prevY = otherPath.prevY;
2368         currX = otherPath.currX;
2369         currY = otherPath.currY;
< prev index next >