1 /*
   2  * Copyright (c) 2006, 2018, 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 java.awt.geom;
  27 
  28 import java.awt.Rectangle;
  29 import java.awt.Shape;
  30 import java.io.Serializable;
  31 import java.io.StreamCorruptedException;
  32 import java.util.Arrays;
  33 
  34 import sun.awt.geom.Curve;
  35 
  36 /**
  37  * The {@code Path2D} class provides a simple, yet flexible
  38  * shape which represents an arbitrary geometric path.
  39  * It can fully represent any path which can be iterated by the
  40  * {@link PathIterator} interface including all of its segment
  41  * types and winding rules and it implements all of the
  42  * basic hit testing methods of the {@link Shape} interface.
  43  * <p>
  44  * Use {@link Path2D.Float} when dealing with data that can be represented
  45  * and used with floating point precision.  Use {@link Path2D.Double}
  46  * for data that requires the accuracy or range of double precision.
  47  * <p>
  48  * {@code Path2D} provides exactly those facilities required for
  49  * basic construction and management of a geometric path and
  50  * implementation of the above interfaces with little added
  51  * interpretation.
  52  * If it is useful to manipulate the interiors of closed
  53  * geometric shapes beyond simple hit testing then the
  54  * {@link Area} class provides additional capabilities
  55  * specifically targeted at closed figures.
  56  * While both classes nominally implement the {@code Shape}
  57  * interface, they differ in purpose and together they provide
  58  * two useful views of a geometric shape where {@code Path2D}
  59  * deals primarily with a trajectory formed by path segments
  60  * and {@code Area} deals more with interpretation and manipulation
  61  * of enclosed regions of 2D geometric space.
  62  * <p>
  63  * The {@link PathIterator} interface has more detailed descriptions
  64  * of the types of segments that make up a path and the winding rules
  65  * that control how to determine which regions are inside or outside
  66  * the path.
  67  *
  68  * @author Jim Graham
  69  * @since 1.6
  70  */
  71 public abstract class Path2D implements Shape, Cloneable {
  72     /**
  73      * An even-odd winding rule for determining the interior of
  74      * a path.
  75      *
  76      * @see PathIterator#WIND_EVEN_ODD
  77      * @since 1.6
  78      */
  79     public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
  80 
  81     /**
  82      * A non-zero winding rule for determining the interior of a
  83      * path.
  84      *
  85      * @see PathIterator#WIND_NON_ZERO
  86      * @since 1.6
  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     transient byte[] pointTypes;
  99     transient int numTypes;
 100     transient int numCoords;
 101     transient 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     static final int EXPAND_MIN = 10; // ensure > 6 (cubics)
 107 
 108     /**
 109      * Constructs a new empty {@code Path2D} object.
 110      * It is assumed that the package sibling subclass that is
 111      * defaulting to this constructor will fill in all values.
 112      *
 113      * @since 1.6
 114      */
 115     /* private protected */
 116     Path2D() {
 117     }
 118 
 119     /**
 120      * Constructs a new {@code Path2D} object from the given
 121      * specified initial values.
 122      * This method is only intended for internal use and should
 123      * not be made public if the other constructors for this class
 124      * are ever exposed.
 125      *
 126      * @param rule the winding rule
 127      * @param initialTypes the size to make the initial array to
 128      *                     store the path segment types
 129      * @since 1.6
 130      */
 131     /* private protected */
 132     Path2D(int rule, int initialTypes) {
 133         setWindingRule(rule);
 134         this.pointTypes = new byte[initialTypes];
 135     }
 136 
 137     abstract float[] cloneCoordsFloat(AffineTransform at);
 138     abstract double[] cloneCoordsDouble(AffineTransform at);
 139     abstract void append(float x, float y);
 140     abstract void append(double x, double y);
 141     abstract Point2D getPoint(int coordindex);
 142     abstract void needRoom(boolean needMove, int newCoords);
 143     abstract int pointCrossings(double px, double py);
 144     abstract int rectCrossings(double rxmin, double rymin,
 145                                double rxmax, double rymax);
 146 
 147     static byte[] expandPointTypes(byte[] oldPointTypes, int needed) {
 148         final int oldSize = oldPointTypes.length;
 149         final int newSizeMin = oldSize + needed;
 150         if (newSizeMin < oldSize) {
 151             // hard overflow failure - we can't even accommodate
 152             // new items without overflowing
 153             throw new ArrayIndexOutOfBoundsException(
 154                           "pointTypes exceeds maximum capacity !");
 155         }
 156         // growth algorithm computation
 157         int grow = oldSize;
 158         if (grow > EXPAND_MAX) {
 159             grow = Math.max(EXPAND_MAX, oldSize >> 3); // 1/8th min
 160         } else if (grow < EXPAND_MIN) {
 161             grow = EXPAND_MIN;
 162         }
 163         assert grow > 0;
 164 
 165         int newSize = oldSize + grow;
 166         if (newSize < newSizeMin) {
 167             // overflow in growth algorithm computation
 168             newSize = Integer.MAX_VALUE;
 169         }
 170         while (true) {
 171             try {
 172                 // try allocating the larger array
 173                 return Arrays.copyOf(oldPointTypes, newSize);
 174             } catch (OutOfMemoryError oome) {
 175                 if (newSize == newSizeMin) {
 176                     throw oome;
 177                 }
 178             }
 179             newSize = newSizeMin + (newSize - newSizeMin) / 2;
 180         }
 181     }
 182 
 183     /**
 184      * The {@code Float} class defines a geometric path with
 185      * coordinates stored in single precision floating point.
 186      *
 187      * @since 1.6
 188      */
 189     public static class Float extends Path2D implements Serializable {
 190         transient float[] floatCoords;
 191 
 192         /**
 193          * Constructs a new empty single precision {@code Path2D} object
 194          * with a default winding rule of {@link #WIND_NON_ZERO}.
 195          *
 196          * @since 1.6
 197          */
 198         public Float() {
 199             this(WIND_NON_ZERO, INIT_SIZE);
 200         }
 201 
 202         /**
 203          * Constructs a new empty single precision {@code Path2D} object
 204          * with the specified winding rule to control operations that
 205          * require the interior of the path to be defined.
 206          *
 207          * @param rule the winding rule
 208          * @see #WIND_EVEN_ODD
 209          * @see #WIND_NON_ZERO
 210          * @since 1.6
 211          */
 212         public Float(int rule) {
 213             this(rule, INIT_SIZE);
 214         }
 215 
 216         /**
 217          * Constructs a new empty single precision {@code Path2D} object
 218          * with the specified winding rule and the specified initial
 219          * capacity to store path segments.
 220          * This number is an initial guess as to how many path segments
 221          * will be added to the path, but the storage is expanded as
 222          * needed to store whatever path segments are added.
 223          *
 224          * @param rule the winding rule
 225          * @param initialCapacity the estimate for the number of path segments
 226          *                        in the path
 227          * @see #WIND_EVEN_ODD
 228          * @see #WIND_NON_ZERO
 229          * @since 1.6
 230          */
 231         public Float(int rule, int initialCapacity) {
 232             super(rule, initialCapacity);
 233             floatCoords = new float[initialCapacity * 2];
 234         }
 235 
 236         /**
 237          * Constructs a new single precision {@code Path2D} object
 238          * from an arbitrary {@link Shape} object.
 239          * All of the initial geometry and the winding rule for this path are
 240          * taken from the specified {@code Shape} object.
 241          *
 242          * @param s the specified {@code Shape} object
 243          * @since 1.6
 244          */
 245         public Float(Shape s) {
 246             this(s, null);
 247         }
 248 
 249         /**
 250          * Constructs a new single precision {@code Path2D} object
 251          * from an arbitrary {@link Shape} object, transformed by an
 252          * {@link AffineTransform} object.
 253          * All of the initial geometry and the winding rule for this path are
 254          * taken from the specified {@code Shape} object and transformed
 255          * by the specified {@code AffineTransform} object.
 256          *
 257          * @param s the specified {@code Shape} object
 258          * @param at the specified {@code AffineTransform} object
 259          * @since 1.6
 260          */
 261         public Float(Shape s, AffineTransform at) {
 262             if (s instanceof Path2D) {
 263                 Path2D p2d = (Path2D) s;
 264                 setWindingRule(p2d.windingRule);
 265                 this.numTypes = p2d.numTypes;
 266                 // trim arrays:
 267                 this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
 268                 this.numCoords = p2d.numCoords;
 269                 this.floatCoords = p2d.cloneCoordsFloat(at);
 270             } else {
 271                 PathIterator pi = s.getPathIterator(at);
 272                 setWindingRule(pi.getWindingRule());
 273                 this.pointTypes = new byte[INIT_SIZE];
 274                 this.floatCoords = new float[INIT_SIZE * 2];
 275                 append(pi, false);
 276             }
 277         }
 278 
 279         @Override
 280         public final void trimToSize() {
 281             // trim arrays:
 282             if (numTypes < pointTypes.length) {
 283                 this.pointTypes = Arrays.copyOf(pointTypes, numTypes);
 284             }
 285             if (numCoords < floatCoords.length) {
 286                 this.floatCoords = Arrays.copyOf(floatCoords, numCoords);
 287             }
 288         }
 289 
 290         @Override
 291         float[] cloneCoordsFloat(AffineTransform at) {
 292             // trim arrays:
 293             float[] ret;
 294             if (at == null) {
 295                 ret = Arrays.copyOf(floatCoords, numCoords);
 296             } else {
 297                 ret = new float[numCoords];
 298                 at.transform(floatCoords, 0, ret, 0, numCoords / 2);
 299             }
 300             return ret;
 301         }
 302 
 303         @Override
 304         double[] cloneCoordsDouble(AffineTransform at) {
 305             // trim arrays:
 306             double[] ret = new double[numCoords];
 307             if (at == null) {
 308                 for (int i = 0; i < numCoords; i++) {
 309                     ret[i] = floatCoords[i];
 310                 }
 311             } else {
 312                 at.transform(floatCoords, 0, ret, 0, numCoords / 2);
 313             }
 314             return ret;
 315         }
 316 
 317         void append(float x, float y) {
 318             floatCoords[numCoords++] = x;
 319             floatCoords[numCoords++] = y;
 320         }
 321 
 322         void append(double x, double y) {
 323             floatCoords[numCoords++] = (float) x;
 324             floatCoords[numCoords++] = (float) y;
 325         }
 326 
 327         Point2D getPoint(int coordindex) {
 328             return new Point2D.Float(floatCoords[coordindex],
 329                                      floatCoords[coordindex+1]);
 330         }
 331 
 332         @Override
 333         void needRoom(boolean needMove, int newCoords) {
 334             if ((numTypes == 0) && needMove) {
 335                 throw new IllegalPathStateException("missing initial moveto "+
 336                                                     "in path definition");
 337             }
 338             if (numTypes >= pointTypes.length) {
 339                 pointTypes = expandPointTypes(pointTypes, 1);
 340             }
 341             if (numCoords > (floatCoords.length - newCoords)) {
 342                 floatCoords = expandCoords(floatCoords, newCoords);
 343             }
 344         }
 345 
 346         static float[] expandCoords(float[] oldCoords, int needed) {
 347             final int oldSize = oldCoords.length;
 348             final int newSizeMin = oldSize + needed;
 349             if (newSizeMin < oldSize) {
 350                 // hard overflow failure - we can't even accommodate
 351                 // new items without overflowing
 352                 throw new ArrayIndexOutOfBoundsException(
 353                               "coords exceeds maximum capacity !");
 354             }
 355             // growth algorithm computation
 356             int grow = oldSize;
 357             if (grow > EXPAND_MAX_COORDS) {
 358                 grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
 359             } else if (grow < EXPAND_MIN) {
 360                 grow = EXPAND_MIN;
 361             }
 362             assert grow > needed;
 363 
 364             int newSize = oldSize + grow;
 365             if (newSize < newSizeMin) {
 366                 // overflow in growth algorithm computation
 367                 newSize = Integer.MAX_VALUE;
 368             }
 369             while (true) {
 370                 try {
 371                     // try allocating the larger array
 372                     return Arrays.copyOf(oldCoords, newSize);
 373                 } catch (OutOfMemoryError oome) {
 374                     if (newSize == newSizeMin) {
 375                         throw oome;
 376                     }
 377                 }
 378                 newSize = newSizeMin + (newSize - newSizeMin) / 2;
 379             }
 380         }
 381 
 382         /**
 383          * {@inheritDoc}
 384          * @since 1.6
 385          */
 386         public final synchronized void moveTo(double x, double y) {
 387             if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
 388                 floatCoords[numCoords-2] = (float) x;
 389                 floatCoords[numCoords-1] = (float) y;
 390             } else {
 391                 needRoom(false, 2);
 392                 pointTypes[numTypes++] = SEG_MOVETO;
 393                 floatCoords[numCoords++] = (float) x;
 394                 floatCoords[numCoords++] = (float) y;
 395             }
 396         }
 397 
 398         /**
 399          * Adds a point to the path by moving to the specified
 400          * coordinates specified in float precision.
 401          * <p>
 402          * This method provides a single precision variant of
 403          * the double precision {@code moveTo()} method on the
 404          * base {@code Path2D} class.
 405          *
 406          * @param x the specified X coordinate
 407          * @param y the specified Y coordinate
 408          * @see Path2D#moveTo
 409          * @since 1.6
 410          */
 411         public final synchronized void moveTo(float x, float y) {
 412             if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
 413                 floatCoords[numCoords-2] = x;
 414                 floatCoords[numCoords-1] = y;
 415             } else {
 416                 needRoom(false, 2);
 417                 pointTypes[numTypes++] = SEG_MOVETO;
 418                 floatCoords[numCoords++] = x;
 419                 floatCoords[numCoords++] = y;
 420             }
 421         }
 422 
 423         /**
 424          * {@inheritDoc}
 425          * @since 1.6
 426          */
 427         public final synchronized void lineTo(double x, double y) {
 428             needRoom(true, 2);
 429             pointTypes[numTypes++] = SEG_LINETO;
 430             floatCoords[numCoords++] = (float) x;
 431             floatCoords[numCoords++] = (float) y;
 432         }
 433 
 434         /**
 435          * Adds a point to the path by drawing a straight line from the
 436          * current coordinates to the new specified coordinates
 437          * specified in float precision.
 438          * <p>
 439          * This method provides a single precision variant of
 440          * the double precision {@code lineTo()} method on the
 441          * base {@code Path2D} class.
 442          *
 443          * @param x the specified X coordinate
 444          * @param y the specified Y coordinate
 445          * @see Path2D#lineTo
 446          * @since 1.6
 447          */
 448         public final synchronized void lineTo(float x, float y) {
 449             needRoom(true, 2);
 450             pointTypes[numTypes++] = SEG_LINETO;
 451             floatCoords[numCoords++] = x;
 452             floatCoords[numCoords++] = y;
 453         }
 454 
 455         /**
 456          * {@inheritDoc}
 457          * @since 1.6
 458          */
 459         public final synchronized void quadTo(double x1, double y1,
 460                                               double x2, double y2)
 461         {
 462             needRoom(true, 4);
 463             pointTypes[numTypes++] = SEG_QUADTO;
 464             floatCoords[numCoords++] = (float) x1;
 465             floatCoords[numCoords++] = (float) y1;
 466             floatCoords[numCoords++] = (float) x2;
 467             floatCoords[numCoords++] = (float) y2;
 468         }
 469 
 470         /**
 471          * Adds a curved segment, defined by two new points, to the path by
 472          * drawing a Quadratic curve that intersects both the current
 473          * coordinates and the specified coordinates {@code (x2,y2)},
 474          * using the specified point {@code (x1,y1)} as a quadratic
 475          * parametric control point.
 476          * All coordinates are specified in float precision.
 477          * <p>
 478          * This method provides a single precision variant of
 479          * the double precision {@code quadTo()} method on the
 480          * base {@code Path2D} class.
 481          *
 482          * @param x1 the X coordinate of the quadratic control point
 483          * @param y1 the Y coordinate of the quadratic control point
 484          * @param x2 the X coordinate of the final end point
 485          * @param y2 the Y coordinate of the final end point
 486          * @see Path2D#quadTo
 487          * @since 1.6
 488          */
 489         public final synchronized void quadTo(float x1, float y1,
 490                                               float x2, float y2)
 491         {
 492             needRoom(true, 4);
 493             pointTypes[numTypes++] = SEG_QUADTO;
 494             floatCoords[numCoords++] = x1;
 495             floatCoords[numCoords++] = y1;
 496             floatCoords[numCoords++] = x2;
 497             floatCoords[numCoords++] = y2;
 498         }
 499 
 500         /**
 501          * {@inheritDoc}
 502          * @since 1.6
 503          */
 504         public final synchronized void curveTo(double x1, double y1,
 505                                                double x2, double y2,
 506                                                double x3, double y3)
 507         {
 508             needRoom(true, 6);
 509             pointTypes[numTypes++] = SEG_CUBICTO;
 510             floatCoords[numCoords++] = (float) x1;
 511             floatCoords[numCoords++] = (float) y1;
 512             floatCoords[numCoords++] = (float) x2;
 513             floatCoords[numCoords++] = (float) y2;
 514             floatCoords[numCoords++] = (float) x3;
 515             floatCoords[numCoords++] = (float) y3;
 516         }
 517 
 518         /**
 519          * Adds a curved segment, defined by three new points, to the path by
 520          * drawing a B&eacute;zier curve that intersects both the current
 521          * coordinates and the specified coordinates {@code (x3,y3)},
 522          * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
 523          * B&eacute;zier control points.
 524          * All coordinates are specified in float precision.
 525          * <p>
 526          * This method provides a single precision variant of
 527          * the double precision {@code curveTo()} method on the
 528          * base {@code Path2D} class.
 529          *
 530          * @param x1 the X coordinate of the first B&eacute;zier control point
 531          * @param y1 the Y coordinate of the first B&eacute;zier control point
 532          * @param x2 the X coordinate of the second B&eacute;zier control point
 533          * @param y2 the Y coordinate of the second B&eacute;zier control point
 534          * @param x3 the X coordinate of the final end point
 535          * @param y3 the Y coordinate of the final end point
 536          * @see Path2D#curveTo
 537          * @since 1.6
 538          */
 539         public final synchronized void curveTo(float x1, float y1,
 540                                                float x2, float y2,
 541                                                float x3, float y3)
 542         {
 543             needRoom(true, 6);
 544             pointTypes[numTypes++] = SEG_CUBICTO;
 545             floatCoords[numCoords++] = x1;
 546             floatCoords[numCoords++] = y1;
 547             floatCoords[numCoords++] = x2;
 548             floatCoords[numCoords++] = y2;
 549             floatCoords[numCoords++] = x3;
 550             floatCoords[numCoords++] = y3;
 551         }
 552 
 553         int pointCrossings(double px, double py) {
 554             if (numTypes == 0) {
 555                 return 0;
 556             }
 557             double movx, movy, curx, cury, endx, endy;
 558             float[] coords = floatCoords;
 559             curx = movx = coords[0];
 560             cury = movy = coords[1];
 561             int crossings = 0;
 562             int ci = 2;
 563             for (int i = 1; i < numTypes; i++) {
 564                 switch (pointTypes[i]) {
 565                 case PathIterator.SEG_MOVETO:
 566                     if (cury != movy) {
 567                         crossings +=
 568                             Curve.pointCrossingsForLine(px, py,
 569                                                         curx, cury,
 570                                                         movx, movy);
 571                     }
 572                     movx = curx = coords[ci++];
 573                     movy = cury = coords[ci++];
 574                     break;
 575                 case PathIterator.SEG_LINETO:
 576                     crossings +=
 577                         Curve.pointCrossingsForLine(px, py,
 578                                                     curx, cury,
 579                                                     endx = coords[ci++],
 580                                                     endy = coords[ci++]);
 581                     curx = endx;
 582                     cury = endy;
 583                     break;
 584                 case PathIterator.SEG_QUADTO:
 585                     crossings +=
 586                         Curve.pointCrossingsForQuad(px, py,
 587                                                     curx, cury,
 588                                                     coords[ci++],
 589                                                     coords[ci++],
 590                                                     endx = coords[ci++],
 591                                                     endy = coords[ci++],
 592                                                     0);
 593                     curx = endx;
 594                     cury = endy;
 595                     break;
 596             case PathIterator.SEG_CUBICTO:
 597                     crossings +=
 598                         Curve.pointCrossingsForCubic(px, py,
 599                                                      curx, cury,
 600                                                      coords[ci++],
 601                                                      coords[ci++],
 602                                                      coords[ci++],
 603                                                      coords[ci++],
 604                                                      endx = coords[ci++],
 605                                                      endy = coords[ci++],
 606                                                      0);
 607                     curx = endx;
 608                     cury = endy;
 609                     break;
 610                 case PathIterator.SEG_CLOSE:
 611                     if (cury != movy) {
 612                         crossings +=
 613                             Curve.pointCrossingsForLine(px, py,
 614                                                         curx, cury,
 615                                                         movx, movy);
 616                     }
 617                     curx = movx;
 618                     cury = movy;
 619                     break;
 620                 }
 621             }
 622             if (cury != movy) {
 623                 crossings +=
 624                     Curve.pointCrossingsForLine(px, py,
 625                                                 curx, cury,
 626                                                 movx, movy);
 627             }
 628             return crossings;
 629         }
 630 
 631         int rectCrossings(double rxmin, double rymin,
 632                           double rxmax, double rymax)
 633         {
 634             if (numTypes == 0) {
 635                 return 0;
 636             }
 637             float[] coords = floatCoords;
 638             double curx, cury, movx, movy, endx, endy;
 639             curx = movx = coords[0];
 640             cury = movy = coords[1];
 641             int crossings = 0;
 642             int ci = 2;
 643             for (int i = 1;
 644                  crossings != Curve.RECT_INTERSECTS && i < numTypes;
 645                  i++)
 646             {
 647                 switch (pointTypes[i]) {
 648                 case PathIterator.SEG_MOVETO:
 649                     if (curx != movx || cury != movy) {
 650                         crossings =
 651                             Curve.rectCrossingsForLine(crossings,
 652                                                        rxmin, rymin,
 653                                                        rxmax, rymax,
 654                                                        curx, cury,
 655                                                        movx, movy);
 656                     }
 657                     // Count should always be a multiple of 2 here.
 658                     // assert((crossings & 1) != 0);
 659                     movx = curx = coords[ci++];
 660                     movy = cury = coords[ci++];
 661                     break;
 662                 case PathIterator.SEG_LINETO:
 663                     crossings =
 664                         Curve.rectCrossingsForLine(crossings,
 665                                                    rxmin, rymin,
 666                                                    rxmax, rymax,
 667                                                    curx, cury,
 668                                                    endx = coords[ci++],
 669                                                    endy = coords[ci++]);
 670                     curx = endx;
 671                     cury = endy;
 672                     break;
 673                 case PathIterator.SEG_QUADTO:
 674                     crossings =
 675                         Curve.rectCrossingsForQuad(crossings,
 676                                                    rxmin, rymin,
 677                                                    rxmax, rymax,
 678                                                    curx, cury,
 679                                                    coords[ci++],
 680                                                    coords[ci++],
 681                                                    endx = coords[ci++],
 682                                                    endy = coords[ci++],
 683                                                    0);
 684                     curx = endx;
 685                     cury = endy;
 686                     break;
 687                 case PathIterator.SEG_CUBICTO:
 688                     crossings =
 689                         Curve.rectCrossingsForCubic(crossings,
 690                                                     rxmin, rymin,
 691                                                     rxmax, rymax,
 692                                                     curx, cury,
 693                                                     coords[ci++],
 694                                                     coords[ci++],
 695                                                     coords[ci++],
 696                                                     coords[ci++],
 697                                                     endx = coords[ci++],
 698                                                     endy = coords[ci++],
 699                                                     0);
 700                     curx = endx;
 701                     cury = endy;
 702                     break;
 703                 case PathIterator.SEG_CLOSE:
 704                     if (curx != movx || cury != movy) {
 705                         crossings =
 706                             Curve.rectCrossingsForLine(crossings,
 707                                                        rxmin, rymin,
 708                                                        rxmax, rymax,
 709                                                        curx, cury,
 710                                                        movx, movy);
 711                     }
 712                     curx = movx;
 713                     cury = movy;
 714                     // Count should always be a multiple of 2 here.
 715                     // assert((crossings & 1) != 0);
 716                     break;
 717                 }
 718             }
 719             if (crossings != Curve.RECT_INTERSECTS &&
 720                 (curx != movx || cury != movy))
 721             {
 722                 crossings =
 723                     Curve.rectCrossingsForLine(crossings,
 724                                                rxmin, rymin,
 725                                                rxmax, rymax,
 726                                                curx, cury,
 727                                                movx, movy);
 728             }
 729             // Count should always be a multiple of 2 here.
 730             // assert((crossings & 1) != 0);
 731             return crossings;
 732         }
 733 
 734         /**
 735          * {@inheritDoc}
 736          * @since 1.6
 737          */
 738         public final void append(PathIterator pi, boolean connect) {
 739             float[] coords = new float[6];
 740             while (!pi.isDone()) {
 741                 switch (pi.currentSegment(coords)) {
 742                 case SEG_MOVETO:
 743                     if (!connect || numTypes < 1 || numCoords < 1) {
 744                         moveTo(coords[0], coords[1]);
 745                         break;
 746                     }
 747                     if (pointTypes[numTypes - 1] != SEG_CLOSE &&
 748                         floatCoords[numCoords-2] == coords[0] &&
 749                         floatCoords[numCoords-1] == coords[1])
 750                     {
 751                         // Collapse out initial moveto/lineto
 752                         break;
 753                     }
 754                     lineTo(coords[0], coords[1]);
 755                     break;
 756                 case SEG_LINETO:
 757                     lineTo(coords[0], coords[1]);
 758                     break;
 759                 case SEG_QUADTO:
 760                     quadTo(coords[0], coords[1],
 761                            coords[2], coords[3]);
 762                     break;
 763                 case SEG_CUBICTO:
 764                     curveTo(coords[0], coords[1],
 765                             coords[2], coords[3],
 766                             coords[4], coords[5]);
 767                     break;
 768                 case SEG_CLOSE:
 769                     closePath();
 770                     break;
 771                 }
 772                 pi.next();
 773                 connect = false;
 774             }
 775         }
 776 
 777         /**
 778          * {@inheritDoc}
 779          * @since 1.6
 780          */
 781         public final void transform(AffineTransform at) {
 782             at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2);
 783         }
 784 
 785         /**
 786          * {@inheritDoc}
 787          * @since 1.6
 788          */
 789         public final synchronized Rectangle2D getBounds2D() {
 790             float x1, y1, x2, y2;
 791             int i = numCoords;
 792             if (i > 0) {
 793                 y1 = y2 = floatCoords[--i];
 794                 x1 = x2 = floatCoords[--i];
 795                 while (i > 0) {
 796                     float y = floatCoords[--i];
 797                     float x = floatCoords[--i];
 798                     if (x < x1) x1 = x;
 799                     if (y < y1) y1 = y;
 800                     if (x > x2) x2 = x;
 801                     if (y > y2) y2 = y;
 802                 }
 803             } else {
 804                 x1 = y1 = x2 = y2 = 0.0f;
 805             }
 806             return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);
 807         }
 808 
 809         /**
 810          * {@inheritDoc}
 811          * <p>
 812          * The iterator for this class is not multi-threaded safe,
 813          * which means that the {@code Path2D} class does not
 814          * guarantee that modifications to the geometry of this
 815          * {@code Path2D} object do not affect any iterations of
 816          * that geometry that are already in process.
 817          *
 818          * @since 1.6
 819          */
 820         public final PathIterator getPathIterator(AffineTransform at) {
 821             if (at == null) {
 822                 return new CopyIterator(this);
 823             } else {
 824                 return new TxIterator(this, at);
 825             }
 826         }
 827 
 828         /**
 829          * Creates a new object of the same class as this object.
 830          *
 831          * @return     a clone of this instance.
 832          * @exception  OutOfMemoryError    if there is not enough memory.
 833          * @see        java.lang.Cloneable
 834          * @since      1.6
 835          */
 836         public final Object clone() {
 837             // Note: It would be nice to have this return Path2D
 838             // but one of our subclasses (GeneralPath) needs to
 839             // offer "public Object clone()" for backwards
 840             // compatibility so we cannot restrict it further.
 841             // REMIND: Can we do both somehow?
 842             if (this instanceof GeneralPath) {
 843                 return new GeneralPath(this);
 844             } else {
 845                 return new Path2D.Float(this);
 846             }
 847         }
 848 
 849         /*
 850          * JDK 1.6 serialVersionUID
 851          */
 852         private static final long serialVersionUID = 6990832515060788886L;
 853 
 854         /**
 855          * Writes the default serializable fields to the
 856          * {@code ObjectOutputStream} followed by an explicit
 857          * serialization of the path segments stored in this
 858          * path.
 859          *
 860          * @serialData
 861          * <a id="Path2DSerialData"><!-- --></a>
 862          * <ol>
 863          * <li>The default serializable fields.
 864          * There are no default serializable fields as of 1.6.
 865          * <li>followed by
 866          * a byte indicating the storage type of the original object
 867          * as a hint (SERIAL_STORAGE_FLT_ARRAY)
 868          * <li>followed by
 869          * an integer indicating the number of path segments to follow (NP)
 870          * or -1 to indicate an unknown number of path segments follows
 871          * <li>followed by
 872          * an integer indicating the total number of coordinates to follow (NC)
 873          * or -1 to indicate an unknown number of coordinates follows
 874          * (NC should always be even since coordinates always appear in pairs
 875          *  representing an x,y pair)
 876          * <li>followed by
 877          * a byte indicating the winding rule
 878          * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
 879          *  {@link #WIND_NON_ZERO WIND_NON_ZERO})
 880          * <li>followed by
 881          * {@code NP} (or unlimited if {@code NP < 0}) sets of values consisting of
 882          * a single byte indicating a path segment type
 883          * followed by one or more pairs of float or double
 884          * values representing the coordinates of the path segment
 885          * <li>followed by
 886          * a byte indicating the end of the path (SERIAL_PATH_END).
 887          * </ol>
 888          * <p>
 889          * The following byte value constants are used in the serialized form
 890          * of {@code Path2D} objects:
 891          *
 892          * <table class="striped">
 893          * <caption>Constants</caption>
 894          * <thead>
 895          * <tr>
 896          * <th>Constant Name</th>
 897          * <th>Byte Value</th>
 898          * <th>Followed by</th>
 899          * <th>Description</th>
 900          * </tr>
 901          * </thead>
 902          * <tbody>
 903          * <tr>
 904          * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
 905          * <td>0x30</td>
 906          * <td></td>
 907          * <td>A hint that the original {@code Path2D} object stored
 908          * the coordinates in a Java array of floats.</td>
 909          * </tr>
 910          * <tr>
 911          * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
 912          * <td>0x31</td>
 913          * <td></td>
 914          * <td>A hint that the original {@code Path2D} object stored
 915          * the coordinates in a Java array of doubles.</td>
 916          * </tr>
 917          * <tr>
 918          * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
 919          * <td>0x40</td>
 920          * <td>2 floats</td>
 921          * <td>A {@link #moveTo moveTo} path segment follows.</td>
 922          * </tr>
 923          * <tr>
 924          * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
 925          * <td>0x41</td>
 926          * <td>2 floats</td>
 927          * <td>A {@link #lineTo lineTo} path segment follows.</td>
 928          * </tr>
 929          * <tr>
 930          * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
 931          * <td>0x42</td>
 932          * <td>4 floats</td>
 933          * <td>A {@link #quadTo quadTo} path segment follows.</td>
 934          * </tr>
 935          * <tr>
 936          * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
 937          * <td>0x43</td>
 938          * <td>6 floats</td>
 939          * <td>A {@link #curveTo curveTo} path segment follows.</td>
 940          * </tr>
 941          * <tr>
 942          * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
 943          * <td>0x50</td>
 944          * <td>2 doubles</td>
 945          * <td>A {@link #moveTo moveTo} path segment follows.</td>
 946          * </tr>
 947          * <tr>
 948          * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
 949          * <td>0x51</td>
 950          * <td>2 doubles</td>
 951          * <td>A {@link #lineTo lineTo} path segment follows.</td>
 952          * </tr>
 953          * <tr>
 954          * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
 955          * <td>0x52</td>
 956          * <td>4 doubles</td>
 957          * <td>A {@link #curveTo curveTo} path segment follows.</td>
 958          * </tr>
 959          * <tr>
 960          * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
 961          * <td>0x53</td>
 962          * <td>6 doubles</td>
 963          * <td>A {@link #curveTo curveTo} path segment follows.</td>
 964          * </tr>
 965          * <tr>
 966          * <td>{@code SERIAL_SEG_CLOSE}</td>
 967          * <td>0x60</td>
 968          * <td></td>
 969          * <td>A {@link #closePath closePath} path segment.</td>
 970          * </tr>
 971          * <tr>
 972          * <td>{@code SERIAL_PATH_END}</td>
 973          * <td>0x61</td>
 974          * <td></td>
 975          * <td>There are no more path segments following.</td>
 976          * </tbody>
 977          * </table>
 978          *
 979          * @since 1.6
 980          */
 981         private void writeObject(java.io.ObjectOutputStream s)
 982             throws java.io.IOException
 983         {
 984             super.writeObject(s, false);
 985         }
 986 
 987         /**
 988          * Reads the default serializable fields from the
 989          * {@code ObjectInputStream} followed by an explicit
 990          * serialization of the path segments stored in this
 991          * path.
 992          * <p>
 993          * There are no default serializable fields as of 1.6.
 994          * <p>
 995          * The serial data for this object is described in the
 996          * writeObject method.
 997          *
 998          * @since 1.6
 999          */
1000         private void readObject(java.io.ObjectInputStream s)
1001             throws java.lang.ClassNotFoundException, java.io.IOException
1002         {
1003             super.readObject(s, false);
1004         }
1005 
1006         static class CopyIterator extends Path2D.Iterator {
1007             float[] floatCoords;
1008 
1009             CopyIterator(Path2D.Float p2df) {
1010                 super(p2df);
1011                 this.floatCoords = p2df.floatCoords;
1012             }
1013 
1014             public int currentSegment(float[] coords) {
1015                 int type = path.pointTypes[typeIdx];
1016                 int numCoords = curvecoords[type];
1017                 if (numCoords > 0) {
1018                     System.arraycopy(floatCoords, pointIdx,
1019                                      coords, 0, numCoords);
1020                 }
1021                 return type;
1022             }
1023 
1024             public int currentSegment(double[] coords) {
1025                 int type = path.pointTypes[typeIdx];
1026                 int numCoords = curvecoords[type];
1027                 if (numCoords > 0) {
1028                     for (int i = 0; i < numCoords; i++) {
1029                         coords[i] = floatCoords[pointIdx + i];
1030                     }
1031                 }
1032                 return type;
1033             }
1034         }
1035 
1036         static class TxIterator extends Path2D.Iterator {
1037             float[] floatCoords;
1038             AffineTransform affine;
1039 
1040             TxIterator(Path2D.Float p2df, AffineTransform at) {
1041                 super(p2df);
1042                 this.floatCoords = p2df.floatCoords;
1043                 this.affine = at;
1044             }
1045 
1046             public int currentSegment(float[] coords) {
1047                 int type = path.pointTypes[typeIdx];
1048                 int numCoords = curvecoords[type];
1049                 if (numCoords > 0) {
1050                     affine.transform(floatCoords, pointIdx,
1051                                      coords, 0, numCoords / 2);
1052                 }
1053                 return type;
1054             }
1055 
1056             public int currentSegment(double[] coords) {
1057                 int type = path.pointTypes[typeIdx];
1058                 int numCoords = curvecoords[type];
1059                 if (numCoords > 0) {
1060                     affine.transform(floatCoords, pointIdx,
1061                                      coords, 0, numCoords / 2);
1062                 }
1063                 return type;
1064             }
1065         }
1066 
1067     }
1068 
1069     /**
1070      * The {@code Double} class defines a geometric path with
1071      * coordinates stored in double precision floating point.
1072      *
1073      * @since 1.6
1074      */
1075     public static class Double extends Path2D implements Serializable {
1076         transient double[] doubleCoords;
1077 
1078         /**
1079          * Constructs a new empty double precision {@code Path2D} object
1080          * with a default winding rule of {@link #WIND_NON_ZERO}.
1081          *
1082          * @since 1.6
1083          */
1084         public Double() {
1085             this(WIND_NON_ZERO, INIT_SIZE);
1086         }
1087 
1088         /**
1089          * Constructs a new empty double precision {@code Path2D} object
1090          * with the specified winding rule to control operations that
1091          * require the interior of the path to be defined.
1092          *
1093          * @param rule the winding rule
1094          * @see #WIND_EVEN_ODD
1095          * @see #WIND_NON_ZERO
1096          * @since 1.6
1097          */
1098         public Double(int rule) {
1099             this(rule, INIT_SIZE);
1100         }
1101 
1102         /**
1103          * Constructs a new empty double precision {@code Path2D} object
1104          * with the specified winding rule and the specified initial
1105          * capacity to store path segments.
1106          * This number is an initial guess as to how many path segments
1107          * are in the path, but the storage is expanded as needed to store
1108          * whatever path segments are added to this path.
1109          *
1110          * @param rule the winding rule
1111          * @param initialCapacity the estimate for the number of path segments
1112          *                        in the path
1113          * @see #WIND_EVEN_ODD
1114          * @see #WIND_NON_ZERO
1115          * @since 1.6
1116          */
1117         public Double(int rule, int initialCapacity) {
1118             super(rule, initialCapacity);
1119             doubleCoords = new double[initialCapacity * 2];
1120         }
1121 
1122         /**
1123          * Constructs a new double precision {@code Path2D} object
1124          * from an arbitrary {@link Shape} object.
1125          * All of the initial geometry and the winding rule for this path are
1126          * taken from the specified {@code Shape} object.
1127          *
1128          * @param s the specified {@code Shape} object
1129          * @since 1.6
1130          */
1131         public Double(Shape s) {
1132             this(s, null);
1133         }
1134 
1135         /**
1136          * Constructs a new double precision {@code Path2D} object
1137          * from an arbitrary {@link Shape} object, transformed by an
1138          * {@link AffineTransform} object.
1139          * All of the initial geometry and the winding rule for this path are
1140          * taken from the specified {@code Shape} object and transformed
1141          * by the specified {@code AffineTransform} object.
1142          *
1143          * @param s the specified {@code Shape} object
1144          * @param at the specified {@code AffineTransform} object
1145          * @since 1.6
1146          */
1147         public Double(Shape s, AffineTransform at) {
1148             if (s instanceof Path2D) {
1149                 Path2D p2d = (Path2D) s;
1150                 setWindingRule(p2d.windingRule);
1151                 this.numTypes = p2d.numTypes;
1152                 // trim arrays:
1153                 this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
1154                 this.numCoords = p2d.numCoords;
1155                 this.doubleCoords = p2d.cloneCoordsDouble(at);
1156             } else {
1157                 PathIterator pi = s.getPathIterator(at);
1158                 setWindingRule(pi.getWindingRule());
1159                 this.pointTypes = new byte[INIT_SIZE];
1160                 this.doubleCoords = new double[INIT_SIZE * 2];
1161                 append(pi, false);
1162             }
1163         }
1164 
1165         @Override
1166         public final void trimToSize() {
1167             // trim arrays:
1168             if (numTypes < pointTypes.length) {
1169                 this.pointTypes = Arrays.copyOf(pointTypes, numTypes);
1170             }
1171             if (numCoords < doubleCoords.length) {
1172                 this.doubleCoords = Arrays.copyOf(doubleCoords, numCoords);
1173             }
1174         }
1175 
1176         @Override
1177         float[] cloneCoordsFloat(AffineTransform at) {
1178             // trim arrays:
1179             float[] ret = new float[numCoords];
1180             if (at == null) {
1181                 for (int i = 0; i < numCoords; i++) {
1182                     ret[i] = (float) doubleCoords[i];
1183                 }
1184             } else {
1185                 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1186             }
1187             return ret;
1188         }
1189 
1190         @Override
1191         double[] cloneCoordsDouble(AffineTransform at) {
1192             // trim arrays:
1193             double[] ret;
1194             if (at == null) {
1195                 ret = Arrays.copyOf(doubleCoords, numCoords);
1196             } else {
1197                 ret = new double[numCoords];
1198                 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1199             }
1200             return ret;
1201         }
1202 
1203         void append(float x, float y) {
1204             doubleCoords[numCoords++] = x;
1205             doubleCoords[numCoords++] = y;
1206         }
1207 
1208         void append(double x, double y) {
1209             doubleCoords[numCoords++] = x;
1210             doubleCoords[numCoords++] = y;
1211         }
1212 
1213         Point2D getPoint(int coordindex) {
1214             return new Point2D.Double(doubleCoords[coordindex],
1215                                       doubleCoords[coordindex+1]);
1216         }
1217 
1218         @Override
1219         void needRoom(boolean needMove, int newCoords) {
1220             if ((numTypes == 0) && needMove) {
1221                 throw new IllegalPathStateException("missing initial moveto "+
1222                                                     "in path definition");
1223             }
1224             if (numTypes >= pointTypes.length) {
1225                 pointTypes = expandPointTypes(pointTypes, 1);
1226             }
1227             if (numCoords > (doubleCoords.length - newCoords)) {
1228                 doubleCoords = expandCoords(doubleCoords, newCoords);
1229             }
1230         }
1231 
1232         static double[] expandCoords(double[] oldCoords, int needed) {
1233             final int oldSize = oldCoords.length;
1234             final int newSizeMin = oldSize + needed;
1235             if (newSizeMin < oldSize) {
1236                 // hard overflow failure - we can't even accommodate
1237                 // new items without overflowing
1238                 throw new ArrayIndexOutOfBoundsException(
1239                               "coords exceeds maximum capacity !");
1240             }
1241             // growth algorithm computation
1242             int grow = oldSize;
1243             if (grow > EXPAND_MAX_COORDS) {
1244                 grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
1245             } else if (grow < EXPAND_MIN) {
1246                 grow = EXPAND_MIN;
1247             }
1248             assert grow > needed;
1249 
1250             int newSize = oldSize + grow;
1251             if (newSize < newSizeMin) {
1252                 // overflow in growth algorithm computation
1253                 newSize = Integer.MAX_VALUE;
1254             }
1255             while (true) {
1256                 try {
1257                     // try allocating the larger array
1258                     return Arrays.copyOf(oldCoords, newSize);
1259                 } catch (OutOfMemoryError oome) {
1260                     if (newSize == newSizeMin) {
1261                         throw oome;
1262                     }
1263                 }
1264                 newSize = newSizeMin + (newSize - newSizeMin) / 2;
1265             }
1266         }
1267 
1268         /**
1269          * {@inheritDoc}
1270          * @since 1.6
1271          */
1272         public final synchronized void moveTo(double x, double y) {
1273             if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
1274                 doubleCoords[numCoords-2] = x;
1275                 doubleCoords[numCoords-1] = y;
1276             } else {
1277                 needRoom(false, 2);
1278                 pointTypes[numTypes++] = SEG_MOVETO;
1279                 doubleCoords[numCoords++] = x;
1280                 doubleCoords[numCoords++] = y;
1281             }
1282         }
1283 
1284         /**
1285          * {@inheritDoc}
1286          * @since 1.6
1287          */
1288         public final synchronized void lineTo(double x, double y) {
1289             needRoom(true, 2);
1290             pointTypes[numTypes++] = SEG_LINETO;
1291             doubleCoords[numCoords++] = x;
1292             doubleCoords[numCoords++] = y;
1293         }
1294 
1295         /**
1296          * {@inheritDoc}
1297          * @since 1.6
1298          */
1299         public final synchronized void quadTo(double x1, double y1,
1300                                               double x2, double y2)
1301         {
1302             needRoom(true, 4);
1303             pointTypes[numTypes++] = SEG_QUADTO;
1304             doubleCoords[numCoords++] = x1;
1305             doubleCoords[numCoords++] = y1;
1306             doubleCoords[numCoords++] = x2;
1307             doubleCoords[numCoords++] = y2;
1308         }
1309 
1310         /**
1311          * {@inheritDoc}
1312          * @since 1.6
1313          */
1314         public final synchronized void curveTo(double x1, double y1,
1315                                                double x2, double y2,
1316                                                double x3, double y3)
1317         {
1318             needRoom(true, 6);
1319             pointTypes[numTypes++] = SEG_CUBICTO;
1320             doubleCoords[numCoords++] = x1;
1321             doubleCoords[numCoords++] = y1;
1322             doubleCoords[numCoords++] = x2;
1323             doubleCoords[numCoords++] = y2;
1324             doubleCoords[numCoords++] = x3;
1325             doubleCoords[numCoords++] = y3;
1326         }
1327 
1328         int pointCrossings(double px, double py) {
1329             if (numTypes == 0) {
1330                 return 0;
1331             }
1332             double movx, movy, curx, cury, endx, endy;
1333             double[] coords = doubleCoords;
1334             curx = movx = coords[0];
1335             cury = movy = coords[1];
1336             int crossings = 0;
1337             int ci = 2;
1338             for (int i = 1; i < numTypes; i++) {
1339                 switch (pointTypes[i]) {
1340                 case PathIterator.SEG_MOVETO:
1341                     if (cury != movy) {
1342                         crossings +=
1343                             Curve.pointCrossingsForLine(px, py,
1344                                                         curx, cury,
1345                                                         movx, movy);
1346                     }
1347                     movx = curx = coords[ci++];
1348                     movy = cury = coords[ci++];
1349                     break;
1350                 case PathIterator.SEG_LINETO:
1351                     crossings +=
1352                         Curve.pointCrossingsForLine(px, py,
1353                                                     curx, cury,
1354                                                     endx = coords[ci++],
1355                                                     endy = coords[ci++]);
1356                     curx = endx;
1357                     cury = endy;
1358                     break;
1359                 case PathIterator.SEG_QUADTO:
1360                     crossings +=
1361                         Curve.pointCrossingsForQuad(px, py,
1362                                                     curx, cury,
1363                                                     coords[ci++],
1364                                                     coords[ci++],
1365                                                     endx = coords[ci++],
1366                                                     endy = coords[ci++],
1367                                                     0);
1368                     curx = endx;
1369                     cury = endy;
1370                     break;
1371             case PathIterator.SEG_CUBICTO:
1372                     crossings +=
1373                         Curve.pointCrossingsForCubic(px, py,
1374                                                      curx, cury,
1375                                                      coords[ci++],
1376                                                      coords[ci++],
1377                                                      coords[ci++],
1378                                                      coords[ci++],
1379                                                      endx = coords[ci++],
1380                                                      endy = coords[ci++],
1381                                                      0);
1382                     curx = endx;
1383                     cury = endy;
1384                     break;
1385                 case PathIterator.SEG_CLOSE:
1386                     if (cury != movy) {
1387                         crossings +=
1388                             Curve.pointCrossingsForLine(px, py,
1389                                                         curx, cury,
1390                                                         movx, movy);
1391                     }
1392                     curx = movx;
1393                     cury = movy;
1394                     break;
1395                 }
1396             }
1397             if (cury != movy) {
1398                 crossings +=
1399                     Curve.pointCrossingsForLine(px, py,
1400                                                 curx, cury,
1401                                                 movx, movy);
1402             }
1403             return crossings;
1404         }
1405 
1406         int rectCrossings(double rxmin, double rymin,
1407                           double rxmax, double rymax)
1408         {
1409             if (numTypes == 0) {
1410                 return 0;
1411             }
1412             double[] coords = doubleCoords;
1413             double curx, cury, movx, movy, endx, endy;
1414             curx = movx = coords[0];
1415             cury = movy = coords[1];
1416             int crossings = 0;
1417             int ci = 2;
1418             for (int i = 1;
1419                  crossings != Curve.RECT_INTERSECTS && i < numTypes;
1420                  i++)
1421             {
1422                 switch (pointTypes[i]) {
1423                 case PathIterator.SEG_MOVETO:
1424                     if (curx != movx || cury != movy) {
1425                         crossings =
1426                             Curve.rectCrossingsForLine(crossings,
1427                                                        rxmin, rymin,
1428                                                        rxmax, rymax,
1429                                                        curx, cury,
1430                                                        movx, movy);
1431                     }
1432                     // Count should always be a multiple of 2 here.
1433                     // assert((crossings & 1) != 0);
1434                     movx = curx = coords[ci++];
1435                     movy = cury = coords[ci++];
1436                     break;
1437                 case PathIterator.SEG_LINETO:
1438                     endx = coords[ci++];
1439                     endy = coords[ci++];
1440                     crossings =
1441                         Curve.rectCrossingsForLine(crossings,
1442                                                    rxmin, rymin,
1443                                                    rxmax, rymax,
1444                                                    curx, cury,
1445                                                    endx, endy);
1446                     curx = endx;
1447                     cury = endy;
1448                     break;
1449                 case PathIterator.SEG_QUADTO:
1450                     crossings =
1451                         Curve.rectCrossingsForQuad(crossings,
1452                                                    rxmin, rymin,
1453                                                    rxmax, rymax,
1454                                                    curx, cury,
1455                                                    coords[ci++],
1456                                                    coords[ci++],
1457                                                    endx = coords[ci++],
1458                                                    endy = coords[ci++],
1459                                                    0);
1460                     curx = endx;
1461                     cury = endy;
1462                     break;
1463                 case PathIterator.SEG_CUBICTO:
1464                     crossings =
1465                         Curve.rectCrossingsForCubic(crossings,
1466                                                     rxmin, rymin,
1467                                                     rxmax, rymax,
1468                                                     curx, cury,
1469                                                     coords[ci++],
1470                                                     coords[ci++],
1471                                                     coords[ci++],
1472                                                     coords[ci++],
1473                                                     endx = coords[ci++],
1474                                                     endy = coords[ci++],
1475                                                     0);
1476                     curx = endx;
1477                     cury = endy;
1478                     break;
1479                 case PathIterator.SEG_CLOSE:
1480                     if (curx != movx || cury != movy) {
1481                         crossings =
1482                             Curve.rectCrossingsForLine(crossings,
1483                                                        rxmin, rymin,
1484                                                        rxmax, rymax,
1485                                                        curx, cury,
1486                                                        movx, movy);
1487                     }
1488                     curx = movx;
1489                     cury = movy;
1490                     // Count should always be a multiple of 2 here.
1491                     // assert((crossings & 1) != 0);
1492                     break;
1493                 }
1494             }
1495             if (crossings != Curve.RECT_INTERSECTS &&
1496                 (curx != movx || cury != movy))
1497             {
1498                 crossings =
1499                     Curve.rectCrossingsForLine(crossings,
1500                                                rxmin, rymin,
1501                                                rxmax, rymax,
1502                                                curx, cury,
1503                                                movx, movy);
1504             }
1505             // Count should always be a multiple of 2 here.
1506             // assert((crossings & 1) != 0);
1507             return crossings;
1508         }
1509 
1510         /**
1511          * {@inheritDoc}
1512          * @since 1.6
1513          */
1514         public final void append(PathIterator pi, boolean connect) {
1515             double[] coords = new double[6];
1516             while (!pi.isDone()) {
1517                 switch (pi.currentSegment(coords)) {
1518                 case SEG_MOVETO:
1519                     if (!connect || numTypes < 1 || numCoords < 1) {
1520                         moveTo(coords[0], coords[1]);
1521                         break;
1522                     }
1523                     if (pointTypes[numTypes - 1] != SEG_CLOSE &&
1524                         doubleCoords[numCoords-2] == coords[0] &&
1525                         doubleCoords[numCoords-1] == coords[1])
1526                     {
1527                         // Collapse out initial moveto/lineto
1528                         break;
1529                     }
1530                     lineTo(coords[0], coords[1]);
1531                     break;
1532                 case SEG_LINETO:
1533                     lineTo(coords[0], coords[1]);
1534                     break;
1535                 case SEG_QUADTO:
1536                     quadTo(coords[0], coords[1],
1537                            coords[2], coords[3]);
1538                     break;
1539                 case SEG_CUBICTO:
1540                     curveTo(coords[0], coords[1],
1541                             coords[2], coords[3],
1542                             coords[4], coords[5]);
1543                     break;
1544                 case SEG_CLOSE:
1545                     closePath();
1546                     break;
1547                 }
1548                 pi.next();
1549                 connect = false;
1550             }
1551         }
1552 
1553         /**
1554          * {@inheritDoc}
1555          * @since 1.6
1556          */
1557         public final void transform(AffineTransform at) {
1558             at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2);
1559         }
1560 
1561         /**
1562          * {@inheritDoc}
1563          * @since 1.6
1564          */
1565         public final synchronized Rectangle2D getBounds2D() {
1566             double x1, y1, x2, y2;
1567             int i = numCoords;
1568             if (i > 0) {
1569                 y1 = y2 = doubleCoords[--i];
1570                 x1 = x2 = doubleCoords[--i];
1571                 while (i > 0) {
1572                     double y = doubleCoords[--i];
1573                     double x = doubleCoords[--i];
1574                     if (x < x1) x1 = x;
1575                     if (y < y1) y1 = y;
1576                     if (x > x2) x2 = x;
1577                     if (y > y2) y2 = y;
1578                 }
1579             } else {
1580                 x1 = y1 = x2 = y2 = 0.0;
1581             }
1582             return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
1583         }
1584 
1585         /**
1586          * {@inheritDoc}
1587          * <p>
1588          * The iterator for this class is not multi-threaded safe,
1589          * which means that the {@code Path2D} class does not
1590          * guarantee that modifications to the geometry of this
1591          * {@code Path2D} object do not affect any iterations of
1592          * that geometry that are already in process.
1593          *
1594          * @param at an {@code AffineTransform}
1595          * @return a new {@code PathIterator} that iterates along the boundary
1596          *         of this {@code Shape} and provides access to the geometry
1597          *         of this {@code Shape}'s outline
1598          * @since 1.6
1599          */
1600         public final PathIterator getPathIterator(AffineTransform at) {
1601             if (at == null) {
1602                 return new CopyIterator(this);
1603             } else {
1604                 return new TxIterator(this, at);
1605             }
1606         }
1607 
1608         /**
1609          * Creates a new object of the same class as this object.
1610          *
1611          * @return     a clone of this instance.
1612          * @exception  OutOfMemoryError    if there is not enough memory.
1613          * @see        java.lang.Cloneable
1614          * @since      1.6
1615          */
1616         public final Object clone() {
1617             // Note: It would be nice to have this return Path2D
1618             // but one of our subclasses (GeneralPath) needs to
1619             // offer "public Object clone()" for backwards
1620             // compatibility so we cannot restrict it further.
1621             // REMIND: Can we do both somehow?
1622             return new Path2D.Double(this);
1623         }
1624 
1625         /*
1626          * JDK 1.6 serialVersionUID
1627          */
1628         private static final long serialVersionUID = 1826762518450014216L;
1629 
1630         /**
1631          * Writes the default serializable fields to the
1632          * {@code ObjectOutputStream} followed by an explicit
1633          * serialization of the path segments stored in this
1634          * path.
1635          *
1636          * @serialData
1637          * <a id="Path2DSerialData"><!-- --></a>
1638          * <ol>
1639          * <li>The default serializable fields.
1640          * There are no default serializable fields as of 1.6.
1641          * <li>followed by
1642          * a byte indicating the storage type of the original object
1643          * as a hint (SERIAL_STORAGE_DBL_ARRAY)
1644          * <li>followed by
1645          * an integer indicating the number of path segments to follow (NP)
1646          * or -1 to indicate an unknown number of path segments follows
1647          * <li>followed by
1648          * an integer indicating the total number of coordinates to follow (NC)
1649          * or -1 to indicate an unknown number of coordinates follows
1650          * (NC should always be even since coordinates always appear in pairs
1651          *  representing an x,y pair)
1652          * <li>followed by
1653          * a byte indicating the winding rule
1654          * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
1655          *  {@link #WIND_NON_ZERO WIND_NON_ZERO})
1656          * <li>followed by
1657          * {@code NP} (or unlimited if {@code NP < 0}) sets of values consisting of
1658          * a single byte indicating a path segment type
1659          * followed by one or more pairs of float or double
1660          * values representing the coordinates of the path segment
1661          * <li>followed by
1662          * a byte indicating the end of the path (SERIAL_PATH_END).
1663          * </ol>
1664          * <p>
1665          * The following byte value constants are used in the serialized form
1666          * of {@code Path2D} objects:
1667          * <table class="striped">
1668          * <caption>Constants</caption>
1669          * <thead>
1670          * <tr>
1671          * <th>Constant Name</th>
1672          * <th>Byte Value</th>
1673          * <th>Followed by</th>
1674          * <th>Description</th>
1675          * </tr>
1676          * </thead>
1677          * <tbody>
1678          * <tr>
1679          * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
1680          * <td>0x30</td>
1681          * <td></td>
1682          * <td>A hint that the original {@code Path2D} object stored
1683          * the coordinates in a Java array of floats.</td>
1684          * </tr>
1685          * <tr>
1686          * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
1687          * <td>0x31</td>
1688          * <td></td>
1689          * <td>A hint that the original {@code Path2D} object stored
1690          * the coordinates in a Java array of doubles.</td>
1691          * </tr>
1692          * <tr>
1693          * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
1694          * <td>0x40</td>
1695          * <td>2 floats</td>
1696          * <td>A {@link #moveTo moveTo} path segment follows.</td>
1697          * </tr>
1698          * <tr>
1699          * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
1700          * <td>0x41</td>
1701          * <td>2 floats</td>
1702          * <td>A {@link #lineTo lineTo} path segment follows.</td>
1703          * </tr>
1704          * <tr>
1705          * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
1706          * <td>0x42</td>
1707          * <td>4 floats</td>
1708          * <td>A {@link #quadTo quadTo} path segment follows.</td>
1709          * </tr>
1710          * <tr>
1711          * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
1712          * <td>0x43</td>
1713          * <td>6 floats</td>
1714          * <td>A {@link #curveTo curveTo} path segment follows.</td>
1715          * </tr>
1716          * <tr>
1717          * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
1718          * <td>0x50</td>
1719          * <td>2 doubles</td>
1720          * <td>A {@link #moveTo moveTo} path segment follows.</td>
1721          * </tr>
1722          * <tr>
1723          * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
1724          * <td>0x51</td>
1725          * <td>2 doubles</td>
1726          * <td>A {@link #lineTo lineTo} path segment follows.</td>
1727          * </tr>
1728          * <tr>
1729          * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
1730          * <td>0x52</td>
1731          * <td>4 doubles</td>
1732          * <td>A {@link #curveTo curveTo} path segment follows.</td>
1733          * </tr>
1734          * <tr>
1735          * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
1736          * <td>0x53</td>
1737          * <td>6 doubles</td>
1738          * <td>A {@link #curveTo curveTo} path segment follows.</td>
1739          * </tr>
1740          * <tr>
1741          * <td>{@code SERIAL_SEG_CLOSE}</td>
1742          * <td>0x60</td>
1743          * <td></td>
1744          * <td>A {@link #closePath closePath} path segment.</td>
1745          * </tr>
1746          * <tr>
1747          * <td>{@code SERIAL_PATH_END}</td>
1748          * <td>0x61</td>
1749          * <td></td>
1750          * <td>There are no more path segments following.</td>
1751          * </tbody>
1752          * </table>
1753          *
1754          * @since 1.6
1755          */
1756         private void writeObject(java.io.ObjectOutputStream s)
1757             throws java.io.IOException
1758         {
1759             super.writeObject(s, true);
1760         }
1761 
1762         /**
1763          * Reads the default serializable fields from the
1764          * {@code ObjectInputStream} followed by an explicit
1765          * serialization of the path segments stored in this
1766          * path.
1767          * <p>
1768          * There are no default serializable fields as of 1.6.
1769          * <p>
1770          * The serial data for this object is described in the
1771          * writeObject method.
1772          *
1773          * @since 1.6
1774          */
1775         private void readObject(java.io.ObjectInputStream s)
1776             throws java.lang.ClassNotFoundException, java.io.IOException
1777         {
1778             super.readObject(s, true);
1779         }
1780 
1781         static class CopyIterator extends Path2D.Iterator {
1782             double[] doubleCoords;
1783 
1784             CopyIterator(Path2D.Double p2dd) {
1785                 super(p2dd);
1786                 this.doubleCoords = p2dd.doubleCoords;
1787             }
1788 
1789             public int currentSegment(float[] coords) {
1790                 int type = path.pointTypes[typeIdx];
1791                 int numCoords = curvecoords[type];
1792                 if (numCoords > 0) {
1793                     for (int i = 0; i < numCoords; i++) {
1794                         coords[i] = (float) doubleCoords[pointIdx + i];
1795                     }
1796                 }
1797                 return type;
1798             }
1799 
1800             public int currentSegment(double[] coords) {
1801                 int type = path.pointTypes[typeIdx];
1802                 int numCoords = curvecoords[type];
1803                 if (numCoords > 0) {
1804                     System.arraycopy(doubleCoords, pointIdx,
1805                                      coords, 0, numCoords);
1806                 }
1807                 return type;
1808             }
1809         }
1810 
1811         static class TxIterator extends Path2D.Iterator {
1812             double[] doubleCoords;
1813             AffineTransform affine;
1814 
1815             TxIterator(Path2D.Double p2dd, AffineTransform at) {
1816                 super(p2dd);
1817                 this.doubleCoords = p2dd.doubleCoords;
1818                 this.affine = at;
1819             }
1820 
1821             public int currentSegment(float[] coords) {
1822                 int type = path.pointTypes[typeIdx];
1823                 int numCoords = curvecoords[type];
1824                 if (numCoords > 0) {
1825                     affine.transform(doubleCoords, pointIdx,
1826                                      coords, 0, numCoords / 2);
1827                 }
1828                 return type;
1829             }
1830 
1831             public int currentSegment(double[] coords) {
1832                 int type = path.pointTypes[typeIdx];
1833                 int numCoords = curvecoords[type];
1834                 if (numCoords > 0) {
1835                     affine.transform(doubleCoords, pointIdx,
1836                                      coords, 0, numCoords / 2);
1837                 }
1838                 return type;
1839             }
1840         }
1841     }
1842 
1843     /**
1844      * Adds a point to the path by moving to the specified
1845      * coordinates specified in double precision.
1846      *
1847      * @param x the specified X coordinate
1848      * @param y the specified Y coordinate
1849      * @since 1.6
1850      */
1851     public abstract void moveTo(double x, double y);
1852 
1853     /**
1854      * Adds a point to the path by drawing a straight line from the
1855      * current coordinates to the new specified coordinates
1856      * specified in double precision.
1857      *
1858      * @param x the specified X coordinate
1859      * @param y the specified Y coordinate
1860      * @since 1.6
1861      */
1862     public abstract void lineTo(double x, double y);
1863 
1864     /**
1865      * Adds a curved segment, defined by two new points, to the path by
1866      * drawing a Quadratic curve that intersects both the current
1867      * coordinates and the specified coordinates {@code (x2,y2)},
1868      * using the specified point {@code (x1,y1)} as a quadratic
1869      * parametric control point.
1870      * All coordinates are specified in double precision.
1871      *
1872      * @param x1 the X coordinate of the quadratic control point
1873      * @param y1 the Y coordinate of the quadratic control point
1874      * @param x2 the X coordinate of the final end point
1875      * @param y2 the Y coordinate of the final end point
1876      * @since 1.6
1877      */
1878     public abstract void quadTo(double x1, double y1,
1879                                 double x2, double y2);
1880 
1881     /**
1882      * Adds a curved segment, defined by three new points, to the path by
1883      * drawing a B&eacute;zier curve that intersects both the current
1884      * coordinates and the specified coordinates {@code (x3,y3)},
1885      * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
1886      * B&eacute;zier control points.
1887      * All coordinates are specified in double precision.
1888      *
1889      * @param x1 the X coordinate of the first B&eacute;zier control point
1890      * @param y1 the Y coordinate of the first B&eacute;zier control point
1891      * @param x2 the X coordinate of the second B&eacute;zier control point
1892      * @param y2 the Y coordinate of the second B&eacute;zier control point
1893      * @param x3 the X coordinate of the final end point
1894      * @param y3 the Y coordinate of the final end point
1895      * @since 1.6
1896      */
1897     public abstract void curveTo(double x1, double y1,
1898                                  double x2, double y2,
1899                                  double x3, double y3);
1900 
1901     /**
1902      * Closes the current subpath by drawing a straight line back to
1903      * the coordinates of the last {@code moveTo}.  If the path is already
1904      * closed then this method has no effect.
1905      *
1906      * @since 1.6
1907      */
1908     public final synchronized void closePath() {
1909         if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
1910             needRoom(true, 0);
1911             pointTypes[numTypes++] = SEG_CLOSE;
1912         }
1913     }
1914 
1915     /**
1916      * Appends the geometry of the specified {@code Shape} object to the
1917      * path, possibly connecting the new geometry to the existing path
1918      * segments with a line segment.
1919      * If the {@code connect} parameter is {@code true} and the
1920      * path is not empty then any initial {@code moveTo} in the
1921      * geometry of the appended {@code Shape}
1922      * is turned into a {@code lineTo} segment.
1923      * If the destination coordinates of such a connecting {@code lineTo}
1924      * segment match the ending coordinates of a currently open
1925      * subpath then the segment is omitted as superfluous.
1926      * The winding rule of the specified {@code Shape} is ignored
1927      * and the appended geometry is governed by the winding
1928      * rule specified for this path.
1929      *
1930      * @param s the {@code Shape} whose geometry is appended
1931      *          to this path
1932      * @param connect a boolean to control whether or not to turn an initial
1933      *                {@code moveTo} segment into a {@code lineTo} segment
1934      *                to connect the new geometry to the existing path
1935      * @since 1.6
1936      */
1937     public final void append(Shape s, boolean connect) {
1938         append(s.getPathIterator(null), connect);
1939     }
1940 
1941     /**
1942      * Appends the geometry of the specified
1943      * {@link PathIterator} object
1944      * to the path, possibly connecting the new geometry to the existing
1945      * path segments with a line segment.
1946      * If the {@code connect} parameter is {@code true} and the
1947      * path is not empty then any initial {@code moveTo} in the
1948      * geometry of the appended {@code Shape} is turned into a
1949      * {@code lineTo} segment.
1950      * If the destination coordinates of such a connecting {@code lineTo}
1951      * segment match the ending coordinates of a currently open
1952      * subpath then the segment is omitted as superfluous.
1953      * The winding rule of the specified {@code Shape} is ignored
1954      * and the appended geometry is governed by the winding
1955      * rule specified for this path.
1956      *
1957      * @param pi the {@code PathIterator} whose geometry is appended to
1958      *           this path
1959      * @param connect a boolean to control whether or not to turn an initial
1960      *                {@code moveTo} segment into a {@code lineTo} segment
1961      *                to connect the new geometry to the existing path
1962      * @since 1.6
1963      */
1964     public abstract void append(PathIterator pi, boolean connect);
1965 
1966     /**
1967      * Returns the fill style winding rule.
1968      *
1969      * @return an integer representing the current winding rule.
1970      * @see #WIND_EVEN_ODD
1971      * @see #WIND_NON_ZERO
1972      * @see #setWindingRule
1973      * @since 1.6
1974      */
1975     public final synchronized int getWindingRule() {
1976         return windingRule;
1977     }
1978 
1979     /**
1980      * Sets the winding rule for this path to the specified value.
1981      *
1982      * @param rule an integer representing the specified
1983      *             winding rule
1984      * @exception IllegalArgumentException if
1985      *          {@code rule} is not either
1986      *          {@link #WIND_EVEN_ODD} or
1987      *          {@link #WIND_NON_ZERO}
1988      * @see #getWindingRule
1989      * @since 1.6
1990      */
1991     public final void setWindingRule(int rule) {
1992         if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
1993             throw new IllegalArgumentException("winding rule must be "+
1994                                                "WIND_EVEN_ODD or "+
1995                                                "WIND_NON_ZERO");
1996         }
1997         windingRule = rule;
1998     }
1999 
2000     /**
2001      * Returns the coordinates most recently added to the end of the path
2002      * as a {@link Point2D} object.
2003      *
2004      * @return a {@code Point2D} object containing the ending coordinates of
2005      *         the path or {@code null} if there are no points in the path.
2006      * @since 1.6
2007      */
2008     public final synchronized Point2D getCurrentPoint() {
2009         int index = numCoords;
2010         if (numTypes < 1 || index < 1) {
2011             return null;
2012         }
2013         if (pointTypes[numTypes - 1] == SEG_CLOSE) {
2014         loop:
2015             for (int i = numTypes - 2; i > 0; i--) {
2016                 switch (pointTypes[i]) {
2017                 case SEG_MOVETO:
2018                     break loop;
2019                 case SEG_LINETO:
2020                     index -= 2;
2021                     break;
2022                 case SEG_QUADTO:
2023                     index -= 4;
2024                     break;
2025                 case SEG_CUBICTO:
2026                     index -= 6;
2027                     break;
2028                 case SEG_CLOSE:
2029                     break;
2030                 }
2031             }
2032         }
2033         return getPoint(index - 2);
2034     }
2035 
2036     /**
2037      * Resets the path to empty.  The append position is set back to the
2038      * beginning of the path and all coordinates and point types are
2039      * forgotten.
2040      *
2041      * @since 1.6
2042      */
2043     public final synchronized void reset() {
2044         numTypes = numCoords = 0;
2045     }
2046 
2047     /**
2048      * Transforms the geometry of this path using the specified
2049      * {@link AffineTransform}.
2050      * The geometry is transformed in place, which permanently changes the
2051      * boundary defined by this object.
2052      *
2053      * @param at the {@code AffineTransform} used to transform the area
2054      * @since 1.6
2055      */
2056     public abstract void transform(AffineTransform at);
2057 
2058     /**
2059      * Returns a new {@code Shape} representing a transformed version
2060      * of this {@code Path2D}.
2061      * Note that the exact type and coordinate precision of the return
2062      * value is not specified for this method.
2063      * The method will return a Shape that contains no less precision
2064      * for the transformed geometry than this {@code Path2D} currently
2065      * maintains, but it may contain no more precision either.
2066      * If the tradeoff of precision vs. storage size in the result is
2067      * important then the convenience constructors in the
2068      * {@link Path2D.Float#Float(Shape, AffineTransform) Path2D.Float}
2069      * and
2070      * {@link Path2D.Double#Double(Shape, AffineTransform) Path2D.Double}
2071      * subclasses should be used to make the choice explicit.
2072      *
2073      * @param at the {@code AffineTransform} used to transform a
2074      *           new {@code Shape}.
2075      * @return a new {@code Shape}, transformed with the specified
2076      *         {@code AffineTransform}.
2077      * @since 1.6
2078      */
2079     public final synchronized Shape createTransformedShape(AffineTransform at) {
2080         Path2D p2d = (Path2D) clone();
2081         if (at != null) {
2082             p2d.transform(at);
2083         }
2084         return p2d;
2085     }
2086 
2087     /**
2088      * {@inheritDoc}
2089      * @since 1.6
2090      */
2091     public final Rectangle getBounds() {
2092         return getBounds2D().getBounds();
2093     }
2094 
2095     /**
2096      * Tests if the specified coordinates are inside the closed
2097      * boundary of the specified {@link PathIterator}.
2098      * <p>
2099      * This method provides a basic facility for implementors of
2100      * the {@link Shape} interface to implement support for the
2101      * {@link Shape#contains(double, double)} method.
2102      *
2103      * @param pi the specified {@code PathIterator}
2104      * @param x the specified X coordinate
2105      * @param y the specified Y coordinate
2106      * @return {@code true} if the specified coordinates are inside the
2107      *         specified {@code PathIterator}; {@code false} otherwise
2108      * @since 1.6
2109      */
2110     public static boolean contains(PathIterator pi, double x, double y) {
2111         if (x * 0.0 + y * 0.0 == 0.0) {
2112             /* N * 0.0 is 0.0 only if N is finite.
2113              * Here we know that both x and y are finite.
2114              */
2115             int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
2116             int cross = Curve.pointCrossingsForPath(pi, x, y);
2117             return ((cross & mask) != 0);
2118         } else {
2119             /* Either x or y was infinite or NaN.
2120              * A NaN always produces a negative response to any test
2121              * and Infinity values cannot be "inside" any path so
2122              * they should return false as well.
2123              */
2124             return false;
2125         }
2126     }
2127 
2128     /**
2129      * Tests if the specified {@link Point2D} is inside the closed
2130      * boundary of the specified {@link PathIterator}.
2131      * <p>
2132      * This method provides a basic facility for implementors of
2133      * the {@link Shape} interface to implement support for the
2134      * {@link Shape#contains(Point2D)} method.
2135      *
2136      * @param pi the specified {@code PathIterator}
2137      * @param p the specified {@code Point2D}
2138      * @return {@code true} if the specified coordinates are inside the
2139      *         specified {@code PathIterator}; {@code false} otherwise
2140      * @since 1.6
2141      */
2142     public static boolean contains(PathIterator pi, Point2D p) {
2143         return contains(pi, p.getX(), p.getY());
2144     }
2145 
2146     /**
2147      * {@inheritDoc}
2148      * @since 1.6
2149      */
2150     public final boolean contains(double x, double y) {
2151         if (x * 0.0 + y * 0.0 == 0.0) {
2152             /* N * 0.0 is 0.0 only if N is finite.
2153              * Here we know that both x and y are finite.
2154              */
2155             if (numTypes < 2) {
2156                 return false;
2157             }
2158             int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
2159             return ((pointCrossings(x, y) & mask) != 0);
2160         } else {
2161             /* Either x or y was infinite or NaN.
2162              * A NaN always produces a negative response to any test
2163              * and Infinity values cannot be "inside" any path so
2164              * they should return false as well.
2165              */
2166             return false;
2167         }
2168     }
2169 
2170     /**
2171      * {@inheritDoc}
2172      * @since 1.6
2173      */
2174     public final boolean contains(Point2D p) {
2175         return contains(p.getX(), p.getY());
2176     }
2177 
2178     /**
2179      * Tests if the specified rectangular area is entirely inside the
2180      * closed boundary of the specified {@link PathIterator}.
2181      * <p>
2182      * This method provides a basic facility for implementors of
2183      * the {@link Shape} interface to implement support for the
2184      * {@link Shape#contains(double, double, double, double)} method.
2185      * <p>
2186      * This method object may conservatively return false in
2187      * cases where the specified rectangular area intersects a
2188      * segment of the path, but that segment does not represent a
2189      * boundary between the interior and exterior of the path.
2190      * Such segments could lie entirely within the interior of the
2191      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2192      * winding rule or if the segments are retraced in the reverse
2193      * direction such that the two sets of segments cancel each
2194      * other out without any exterior area falling between them.
2195      * To determine whether segments represent true boundaries of
2196      * the interior of the path would require extensive calculations
2197      * involving all of the segments of the path and the winding
2198      * rule and are thus beyond the scope of this implementation.
2199      *
2200      * @param pi the specified {@code PathIterator}
2201      * @param x the specified X coordinate
2202      * @param y the specified Y coordinate
2203      * @param w the width of the specified rectangular area
2204      * @param h the height of the specified rectangular area
2205      * @return {@code true} if the specified {@code PathIterator} contains
2206      *         the specified rectangular area; {@code false} otherwise.
2207      * @since 1.6
2208      */
2209     public static boolean contains(PathIterator pi,
2210                                    double x, double y, double w, double h)
2211     {
2212         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2213             /* [xy]+[wh] is NaN if any of those values are NaN,
2214              * or if adding the two together would produce NaN
2215              * by virtue of adding opposing Infinte values.
2216              * Since we need to add them below, their sum must
2217              * not be NaN.
2218              * We return false because NaN always produces a
2219              * negative response to tests
2220              */
2221             return false;
2222         }
2223         if (w <= 0 || h <= 0) {
2224             return false;
2225         }
2226         int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2227         int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h);
2228         return (crossings != Curve.RECT_INTERSECTS &&
2229                 (crossings & mask) != 0);
2230     }
2231 
2232     /**
2233      * Tests if the specified {@link Rectangle2D} is entirely inside the
2234      * closed boundary of the specified {@link PathIterator}.
2235      * <p>
2236      * This method provides a basic facility for implementors of
2237      * the {@link Shape} interface to implement support for the
2238      * {@link Shape#contains(Rectangle2D)} method.
2239      * <p>
2240      * This method object may conservatively return false in
2241      * cases where the specified rectangular area intersects a
2242      * segment of the path, but that segment does not represent a
2243      * boundary between the interior and exterior of the path.
2244      * Such segments could lie entirely within the interior of the
2245      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2246      * winding rule or if the segments are retraced in the reverse
2247      * direction such that the two sets of segments cancel each
2248      * other out without any exterior area falling between them.
2249      * To determine whether segments represent true boundaries of
2250      * the interior of the path would require extensive calculations
2251      * involving all of the segments of the path and the winding
2252      * rule and are thus beyond the scope of this implementation.
2253      *
2254      * @param pi the specified {@code PathIterator}
2255      * @param r a specified {@code Rectangle2D}
2256      * @return {@code true} if the specified {@code PathIterator} contains
2257      *         the specified {@code Rectangle2D}; {@code false} otherwise.
2258      * @since 1.6
2259      */
2260     public static boolean contains(PathIterator pi, Rectangle2D r) {
2261         return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
2262     }
2263 
2264     /**
2265      * {@inheritDoc}
2266      * <p>
2267      * This method object may conservatively return false in
2268      * cases where the specified rectangular area intersects a
2269      * segment of the path, but that segment does not represent a
2270      * boundary between the interior and exterior of the path.
2271      * Such segments could lie entirely within the interior of the
2272      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2273      * winding rule or if the segments are retraced in the reverse
2274      * direction such that the two sets of segments cancel each
2275      * other out without any exterior area falling between them.
2276      * To determine whether segments represent true boundaries of
2277      * the interior of the path would require extensive calculations
2278      * involving all of the segments of the path and the winding
2279      * rule and are thus beyond the scope of this implementation.
2280      *
2281      * @since 1.6
2282      */
2283     public final boolean contains(double x, double y, double w, double h) {
2284         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2285             /* [xy]+[wh] is NaN if any of those values are NaN,
2286              * or if adding the two together would produce NaN
2287              * by virtue of adding opposing Infinte values.
2288              * Since we need to add them below, their sum must
2289              * not be NaN.
2290              * We return false because NaN always produces a
2291              * negative response to tests
2292              */
2293             return false;
2294         }
2295         if (w <= 0 || h <= 0) {
2296             return false;
2297         }
2298         int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2299         int crossings = rectCrossings(x, y, x+w, y+h);
2300         return (crossings != Curve.RECT_INTERSECTS &&
2301                 (crossings & mask) != 0);
2302     }
2303 
2304     /**
2305      * {@inheritDoc}
2306      * <p>
2307      * This method object may conservatively return false in
2308      * cases where the specified rectangular area intersects a
2309      * segment of the path, but that segment does not represent a
2310      * boundary between the interior and exterior of the path.
2311      * Such segments could lie entirely within the interior of the
2312      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2313      * winding rule or if the segments are retraced in the reverse
2314      * direction such that the two sets of segments cancel each
2315      * other out without any exterior area falling between them.
2316      * To determine whether segments represent true boundaries of
2317      * the interior of the path would require extensive calculations
2318      * involving all of the segments of the path and the winding
2319      * rule and are thus beyond the scope of this implementation.
2320      *
2321      * @since 1.6
2322      */
2323     public final boolean contains(Rectangle2D r) {
2324         return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2325     }
2326 
2327     /**
2328      * Tests if the interior of the specified {@link PathIterator}
2329      * intersects the interior of a specified set of rectangular
2330      * coordinates.
2331      * <p>
2332      * This method provides a basic facility for implementors of
2333      * the {@link Shape} interface to implement support for the
2334      * {@link Shape#intersects(double, double, double, double)} method.
2335      * <p>
2336      * This method object may conservatively return true in
2337      * cases where the specified rectangular area intersects a
2338      * segment of the path, but that segment does not represent a
2339      * boundary between the interior and exterior of the path.
2340      * Such a case may occur if some set of segments of the
2341      * path are retraced in the reverse direction such that the
2342      * two sets of segments cancel each other out without any
2343      * interior area between them.
2344      * To determine whether segments represent true boundaries of
2345      * the interior of the path would require extensive calculations
2346      * involving all of the segments of the path and the winding
2347      * rule and are thus beyond the scope of this implementation.
2348      *
2349      * @param pi the specified {@code PathIterator}
2350      * @param x the specified X coordinate
2351      * @param y the specified Y coordinate
2352      * @param w the width of the specified rectangular coordinates
2353      * @param h the height of the specified rectangular coordinates
2354      * @return {@code true} if the specified {@code PathIterator} and
2355      *         the interior of the specified set of rectangular
2356      *         coordinates intersect each other; {@code false} otherwise.
2357      * @since 1.6
2358      */
2359     public static boolean intersects(PathIterator pi,
2360                                      double x, double y, double w, double h)
2361     {
2362         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2363             /* [xy]+[wh] is NaN if any of those values are NaN,
2364              * or if adding the two together would produce NaN
2365              * by virtue of adding opposing Infinte values.
2366              * Since we need to add them below, their sum must
2367              * not be NaN.
2368              * We return false because NaN always produces a
2369              * negative response to tests
2370              */
2371             return false;
2372         }
2373         if (w <= 0 || h <= 0) {
2374             return false;
2375         }
2376         int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2377         int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h);
2378         return (crossings == Curve.RECT_INTERSECTS ||
2379                 (crossings & mask) != 0);
2380     }
2381 
2382     /**
2383      * Tests if the interior of the specified {@link PathIterator}
2384      * intersects the interior of a specified {@link Rectangle2D}.
2385      * <p>
2386      * This method provides a basic facility for implementors of
2387      * the {@link Shape} interface to implement support for the
2388      * {@link Shape#intersects(Rectangle2D)} method.
2389      * <p>
2390      * This method object may conservatively return true in
2391      * cases where the specified rectangular area intersects a
2392      * segment of the path, but that segment does not represent a
2393      * boundary between the interior and exterior of the path.
2394      * Such a case may occur if some set of segments of the
2395      * path are retraced in the reverse direction such that the
2396      * two sets of segments cancel each other out without any
2397      * interior area between them.
2398      * To determine whether segments represent true boundaries of
2399      * the interior of the path would require extensive calculations
2400      * involving all of the segments of the path and the winding
2401      * rule and are thus beyond the scope of this implementation.
2402      *
2403      * @param pi the specified {@code PathIterator}
2404      * @param r the specified {@code Rectangle2D}
2405      * @return {@code true} if the specified {@code PathIterator} and
2406      *         the interior of the specified {@code Rectangle2D}
2407      *         intersect each other; {@code false} otherwise.
2408      * @since 1.6
2409      */
2410     public static boolean intersects(PathIterator pi, Rectangle2D r) {
2411         return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
2412     }
2413 
2414     /**
2415      * {@inheritDoc}
2416      * <p>
2417      * This method object may conservatively return true in
2418      * cases where the specified rectangular area intersects a
2419      * segment of the path, but that segment does not represent a
2420      * boundary between the interior and exterior of the path.
2421      * Such a case may occur if some set of segments of the
2422      * path are retraced in the reverse direction such that the
2423      * two sets of segments cancel each other out without any
2424      * interior area between them.
2425      * To determine whether segments represent true boundaries of
2426      * the interior of the path would require extensive calculations
2427      * involving all of the segments of the path and the winding
2428      * rule and are thus beyond the scope of this implementation.
2429      *
2430      * @since 1.6
2431      */
2432     public final boolean intersects(double x, double y, double w, double h) {
2433         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2434             /* [xy]+[wh] is NaN if any of those values are NaN,
2435              * or if adding the two together would produce NaN
2436              * by virtue of adding opposing Infinte values.
2437              * Since we need to add them below, their sum must
2438              * not be NaN.
2439              * We return false because NaN always produces a
2440              * negative response to tests
2441              */
2442             return false;
2443         }
2444         if (w <= 0 || h <= 0) {
2445             return false;
2446         }
2447         int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2448         int crossings = rectCrossings(x, y, x+w, y+h);
2449         return (crossings == Curve.RECT_INTERSECTS ||
2450                 (crossings & mask) != 0);
2451     }
2452 
2453     /**
2454      * {@inheritDoc}
2455      * <p>
2456      * This method object may conservatively return true in
2457      * cases where the specified rectangular area intersects a
2458      * segment of the path, but that segment does not represent a
2459      * boundary between the interior and exterior of the path.
2460      * Such a case may occur if some set of segments of the
2461      * path are retraced in the reverse direction such that the
2462      * two sets of segments cancel each other out without any
2463      * interior area between them.
2464      * To determine whether segments represent true boundaries of
2465      * the interior of the path would require extensive calculations
2466      * involving all of the segments of the path and the winding
2467      * rule and are thus beyond the scope of this implementation.
2468      *
2469      * @since 1.6
2470      */
2471     public final boolean intersects(Rectangle2D r) {
2472         return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2473     }
2474 
2475     /**
2476      * {@inheritDoc}
2477      * <p>
2478      * The iterator for this class is not multi-threaded safe,
2479      * which means that this {@code Path2D} class does not
2480      * guarantee that modifications to the geometry of this
2481      * {@code Path2D} object do not affect any iterations of
2482      * that geometry that are already in process.
2483      *
2484      * @since 1.6
2485      */
2486     public final PathIterator getPathIterator(AffineTransform at,
2487                                               double flatness)
2488     {
2489         return new FlatteningPathIterator(getPathIterator(at), flatness);
2490     }
2491 
2492     /**
2493      * Creates a new object of the same class as this object.
2494      *
2495      * @return     a clone of this instance.
2496      * @exception  OutOfMemoryError            if there is not enough memory.
2497      * @see        java.lang.Cloneable
2498      * @since      1.6
2499      */
2500     public abstract Object clone();
2501         // Note: It would be nice to have this return Path2D
2502         // but one of our subclasses (GeneralPath) needs to
2503         // offer "public Object clone()" for backwards
2504         // compatibility so we cannot restrict it further.
2505         // REMIND: Can we do both somehow?
2506 
2507     /**
2508      * Trims the capacity of this Path2D instance to its current
2509      * size. An application can use this operation to minimize the
2510      * storage of a path.
2511      *
2512      * @since 10
2513      */
2514     public abstract void trimToSize();
2515 
2516     /*
2517      * Support fields and methods for serializing the subclasses.
2518      */
2519     private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
2520     private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;
2521 
2522     private static final byte SERIAL_SEG_FLT_MOVETO    = 0x40;
2523     private static final byte SERIAL_SEG_FLT_LINETO    = 0x41;
2524     private static final byte SERIAL_SEG_FLT_QUADTO    = 0x42;
2525     private static final byte SERIAL_SEG_FLT_CUBICTO   = 0x43;
2526 
2527     private static final byte SERIAL_SEG_DBL_MOVETO    = 0x50;
2528     private static final byte SERIAL_SEG_DBL_LINETO    = 0x51;
2529     private static final byte SERIAL_SEG_DBL_QUADTO    = 0x52;
2530     private static final byte SERIAL_SEG_DBL_CUBICTO   = 0x53;
2531 
2532     private static final byte SERIAL_SEG_CLOSE         = 0x60;
2533     private static final byte SERIAL_PATH_END          = 0x61;
2534 
2535     final void writeObject(java.io.ObjectOutputStream s, boolean isdbl)
2536         throws java.io.IOException
2537     {
2538         s.defaultWriteObject();
2539 
2540         float[] fCoords;
2541         double[] dCoords;
2542 
2543         if (isdbl) {
2544             dCoords = ((Path2D.Double) this).doubleCoords;
2545             fCoords = null;
2546         } else {
2547             fCoords = ((Path2D.Float) this).floatCoords;
2548             dCoords = null;
2549         }
2550 
2551         int numTypes = this.numTypes;
2552 
2553         s.writeByte(isdbl
2554                     ? SERIAL_STORAGE_DBL_ARRAY
2555                     : SERIAL_STORAGE_FLT_ARRAY);
2556         s.writeInt(numTypes);
2557         s.writeInt(numCoords);
2558         s.writeByte((byte) windingRule);
2559 
2560         int cindex = 0;
2561         for (int i = 0; i < numTypes; i++) {
2562             int npoints;
2563             byte serialtype;
2564             switch (pointTypes[i]) {
2565             case SEG_MOVETO:
2566                 npoints = 1;
2567                 serialtype = (isdbl
2568                               ? SERIAL_SEG_DBL_MOVETO
2569                               : SERIAL_SEG_FLT_MOVETO);
2570                 break;
2571             case SEG_LINETO:
2572                 npoints = 1;
2573                 serialtype = (isdbl
2574                               ? SERIAL_SEG_DBL_LINETO
2575                               : SERIAL_SEG_FLT_LINETO);
2576                 break;
2577             case SEG_QUADTO:
2578                 npoints = 2;
2579                 serialtype = (isdbl
2580                               ? SERIAL_SEG_DBL_QUADTO
2581                               : SERIAL_SEG_FLT_QUADTO);
2582                 break;
2583             case SEG_CUBICTO:
2584                 npoints = 3;
2585                 serialtype = (isdbl
2586                               ? SERIAL_SEG_DBL_CUBICTO
2587                               : SERIAL_SEG_FLT_CUBICTO);
2588                 break;
2589             case SEG_CLOSE:
2590                 npoints = 0;
2591                 serialtype = SERIAL_SEG_CLOSE;
2592                 break;
2593 
2594             default:
2595                 // Should never happen
2596                 throw new InternalError("unrecognized path type");
2597             }
2598             s.writeByte(serialtype);
2599             while (--npoints >= 0) {
2600                 if (isdbl) {
2601                     s.writeDouble(dCoords[cindex++]);
2602                     s.writeDouble(dCoords[cindex++]);
2603                 } else {
2604                     s.writeFloat(fCoords[cindex++]);
2605                     s.writeFloat(fCoords[cindex++]);
2606                 }
2607             }
2608         }
2609         s.writeByte(SERIAL_PATH_END);
2610     }
2611 
2612     final void readObject(java.io.ObjectInputStream s, boolean storedbl)
2613         throws java.lang.ClassNotFoundException, java.io.IOException
2614     {
2615         s.defaultReadObject();
2616 
2617         // The subclass calls this method with the storage type that
2618         // they want us to use (storedbl) so we ignore the storage
2619         // method hint from the stream.
2620         s.readByte();
2621         int nT = s.readInt();
2622         int nC = s.readInt();
2623         try {
2624             setWindingRule(s.readByte());
2625         } catch (IllegalArgumentException iae) {
2626             throw new java.io.InvalidObjectException(iae.getMessage());
2627         }
2628 
2629         // Accept the size from the stream only if it is less than INIT_SIZE
2630         // otherwise the size will be based on the real data in the stream
2631         pointTypes = new byte[(nT < 0 || nT > INIT_SIZE) ? INIT_SIZE : nT];
2632         final int initX2 = INIT_SIZE * 2;
2633         if (nC < 0 || nC > initX2) {
2634             nC = initX2;
2635         }
2636         if (storedbl) {
2637             ((Path2D.Double) this).doubleCoords = new double[nC];
2638         } else {
2639             ((Path2D.Float) this).floatCoords = new float[nC];
2640         }
2641 
2642     PATHDONE:
2643         for (int i = 0; nT < 0 || i < nT; i++) {
2644             boolean isdbl;
2645             int npoints;
2646             byte segtype;
2647 
2648             byte serialtype = s.readByte();
2649             switch (serialtype) {
2650             case SERIAL_SEG_FLT_MOVETO:
2651                 isdbl = false;
2652                 npoints = 1;
2653                 segtype = SEG_MOVETO;
2654                 break;
2655             case SERIAL_SEG_FLT_LINETO:
2656                 isdbl = false;
2657                 npoints = 1;
2658                 segtype = SEG_LINETO;
2659                 break;
2660             case SERIAL_SEG_FLT_QUADTO:
2661                 isdbl = false;
2662                 npoints = 2;
2663                 segtype = SEG_QUADTO;
2664                 break;
2665             case SERIAL_SEG_FLT_CUBICTO:
2666                 isdbl = false;
2667                 npoints = 3;
2668                 segtype = SEG_CUBICTO;
2669                 break;
2670 
2671             case SERIAL_SEG_DBL_MOVETO:
2672                 isdbl = true;
2673                 npoints = 1;
2674                 segtype = SEG_MOVETO;
2675                 break;
2676             case SERIAL_SEG_DBL_LINETO:
2677                 isdbl = true;
2678                 npoints = 1;
2679                 segtype = SEG_LINETO;
2680                 break;
2681             case SERIAL_SEG_DBL_QUADTO:
2682                 isdbl = true;
2683                 npoints = 2;
2684                 segtype = SEG_QUADTO;
2685                 break;
2686             case SERIAL_SEG_DBL_CUBICTO:
2687                 isdbl = true;
2688                 npoints = 3;
2689                 segtype = SEG_CUBICTO;
2690                 break;
2691 
2692             case SERIAL_SEG_CLOSE:
2693                 isdbl = false;
2694                 npoints = 0;
2695                 segtype = SEG_CLOSE;
2696                 break;
2697 
2698             case SERIAL_PATH_END:
2699                 if (nT < 0) {
2700                     break PATHDONE;
2701                 }
2702                 throw new StreamCorruptedException("unexpected PATH_END");
2703 
2704             default:
2705                 throw new StreamCorruptedException("unrecognized path type");
2706             }
2707             needRoom(segtype != SEG_MOVETO, npoints * 2);
2708             if (isdbl) {
2709                 while (--npoints >= 0) {
2710                     append(s.readDouble(), s.readDouble());
2711                 }
2712             } else {
2713                 while (--npoints >= 0) {
2714                     append(s.readFloat(), s.readFloat());
2715                 }
2716             }
2717             pointTypes[numTypes++] = segtype;
2718         }
2719         if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
2720             throw new StreamCorruptedException("missing PATH_END");
2721         }
2722     }
2723 
2724     abstract static class Iterator implements PathIterator {
2725         int typeIdx;
2726         int pointIdx;
2727         Path2D path;
2728 
2729         static final int[] curvecoords = {2, 2, 4, 6, 0};
2730 
2731         Iterator(Path2D path) {
2732             this.path = path;
2733         }
2734 
2735         public int getWindingRule() {
2736             return path.getWindingRule();
2737         }
2738 
2739         public boolean isDone() {
2740             return (typeIdx >= path.numTypes);
2741         }
2742 
2743         public void next() {
2744             int type = path.pointTypes[typeIdx++];
2745             pointIdx += curvecoords[type];
2746         }
2747     }
2748 }