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          * <ol>
 862          * <li>The default serializable fields.
 863          * There are no default serializable fields as of 1.6.
 864          * <li>followed by
 865          * a byte indicating the storage type of the original object
 866          * as a hint (SERIAL_STORAGE_FLT_ARRAY)
 867          * <li>followed by
 868          * an integer indicating the number of path segments to follow (NP)
 869          * or -1 to indicate an unknown number of path segments follows
 870          * <li>followed by
 871          * an integer indicating the total number of coordinates to follow (NC)
 872          * or -1 to indicate an unknown number of coordinates follows
 873          * (NC should always be even since coordinates always appear in pairs
 874          *  representing an x,y pair)
 875          * <li>followed by
 876          * a byte indicating the winding rule
 877          * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
 878          *  {@link #WIND_NON_ZERO WIND_NON_ZERO})
 879          * <li>followed by
 880          * {@code NP} (or unlimited if {@code NP < 0}) sets of values consisting of
 881          * a single byte indicating a path segment type
 882          * followed by one or more pairs of float or double
 883          * values representing the coordinates of the path segment
 884          * <li>followed by
 885          * a byte indicating the end of the path (SERIAL_PATH_END).
 886          * </ol>
 887          * <p>
 888          * The following byte value constants are used in the serialized form
 889          * of {@code Path2D} objects:
 890          *
 891          * <table class="striped">
 892          * <caption>Constants</caption>
 893          * <thead>
 894          * <tr>
 895          * <th>Constant Name</th>
 896          * <th>Byte Value</th>
 897          * <th>Followed by</th>
 898          * <th>Description</th>
 899          * </tr>
 900          * </thead>
 901          * <tbody>
 902          * <tr>
 903          * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
 904          * <td>0x30</td>
 905          * <td></td>
 906          * <td>A hint that the original {@code Path2D} object stored
 907          * the coordinates in a Java array of floats.</td>
 908          * </tr>
 909          * <tr>
 910          * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
 911          * <td>0x31</td>
 912          * <td></td>
 913          * <td>A hint that the original {@code Path2D} object stored
 914          * the coordinates in a Java array of doubles.</td>
 915          * </tr>
 916          * <tr>
 917          * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
 918          * <td>0x40</td>
 919          * <td>2 floats</td>
 920          * <td>A {@link #moveTo moveTo} path segment follows.</td>
 921          * </tr>
 922          * <tr>
 923          * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
 924          * <td>0x41</td>
 925          * <td>2 floats</td>
 926          * <td>A {@link #lineTo lineTo} path segment follows.</td>
 927          * </tr>
 928          * <tr>
 929          * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
 930          * <td>0x42</td>
 931          * <td>4 floats</td>
 932          * <td>A {@link #quadTo quadTo} path segment follows.</td>
 933          * </tr>
 934          * <tr>
 935          * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
 936          * <td>0x43</td>
 937          * <td>6 floats</td>
 938          * <td>A {@link #curveTo curveTo} path segment follows.</td>
 939          * </tr>
 940          * <tr>
 941          * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
 942          * <td>0x50</td>
 943          * <td>2 doubles</td>
 944          * <td>A {@link #moveTo moveTo} path segment follows.</td>
 945          * </tr>
 946          * <tr>
 947          * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
 948          * <td>0x51</td>
 949          * <td>2 doubles</td>
 950          * <td>A {@link #lineTo lineTo} path segment follows.</td>
 951          * </tr>
 952          * <tr>
 953          * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
 954          * <td>0x52</td>
 955          * <td>4 doubles</td>
 956          * <td>A {@link #curveTo curveTo} path segment follows.</td>
 957          * </tr>
 958          * <tr>
 959          * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
 960          * <td>0x53</td>
 961          * <td>6 doubles</td>
 962          * <td>A {@link #curveTo curveTo} path segment follows.</td>
 963          * </tr>
 964          * <tr>
 965          * <td>{@code SERIAL_SEG_CLOSE}</td>
 966          * <td>0x60</td>
 967          * <td></td>
 968          * <td>A {@link #closePath closePath} path segment.</td>
 969          * </tr>
 970          * <tr>
 971          * <td>{@code SERIAL_PATH_END}</td>
 972          * <td>0x61</td>
 973          * <td></td>
 974          * <td>There are no more path segments following.</td>
 975          * </tbody>
 976          * </table>
 977          *
 978          * @since 1.6
 979          */
 980         private void writeObject(java.io.ObjectOutputStream s)
 981             throws java.io.IOException
 982         {
 983             super.writeObject(s, false);
 984         }
 985 
 986         /**
 987          * Reads the default serializable fields from the
 988          * {@code ObjectInputStream} followed by an explicit
 989          * serialization of the path segments stored in this
 990          * path.
 991          * <p>
 992          * There are no default serializable fields as of 1.6.
 993          * <p>
 994          * The serial data for this object is described in the
 995          * writeObject method.
 996          *
 997          * @since 1.6
 998          */
 999         private void readObject(java.io.ObjectInputStream s)
1000             throws java.lang.ClassNotFoundException, java.io.IOException
1001         {
1002             super.readObject(s, false);
1003         }
1004 
1005         static class CopyIterator extends Path2D.Iterator {
1006             float[] floatCoords;
1007 
1008             CopyIterator(Path2D.Float p2df) {
1009                 super(p2df);
1010                 this.floatCoords = p2df.floatCoords;
1011             }
1012 
1013             public int currentSegment(float[] coords) {
1014                 int type = path.pointTypes[typeIdx];
1015                 int numCoords = curvecoords[type];
1016                 if (numCoords > 0) {
1017                     System.arraycopy(floatCoords, pointIdx,
1018                                      coords, 0, numCoords);
1019                 }
1020                 return type;
1021             }
1022 
1023             public int currentSegment(double[] coords) {
1024                 int type = path.pointTypes[typeIdx];
1025                 int numCoords = curvecoords[type];
1026                 if (numCoords > 0) {
1027                     for (int i = 0; i < numCoords; i++) {
1028                         coords[i] = floatCoords[pointIdx + i];
1029                     }
1030                 }
1031                 return type;
1032             }
1033         }
1034 
1035         static class TxIterator extends Path2D.Iterator {
1036             float[] floatCoords;
1037             AffineTransform affine;
1038 
1039             TxIterator(Path2D.Float p2df, AffineTransform at) {
1040                 super(p2df);
1041                 this.floatCoords = p2df.floatCoords;
1042                 this.affine = at;
1043             }
1044 
1045             public int currentSegment(float[] coords) {
1046                 int type = path.pointTypes[typeIdx];
1047                 int numCoords = curvecoords[type];
1048                 if (numCoords > 0) {
1049                     affine.transform(floatCoords, pointIdx,
1050                                      coords, 0, numCoords / 2);
1051                 }
1052                 return type;
1053             }
1054 
1055             public int currentSegment(double[] coords) {
1056                 int type = path.pointTypes[typeIdx];
1057                 int numCoords = curvecoords[type];
1058                 if (numCoords > 0) {
1059                     affine.transform(floatCoords, pointIdx,
1060                                      coords, 0, numCoords / 2);
1061                 }
1062                 return type;
1063             }
1064         }
1065 
1066     }
1067 
1068     /**
1069      * The {@code Double} class defines a geometric path with
1070      * coordinates stored in double precision floating point.
1071      *
1072      * @since 1.6
1073      */
1074     public static class Double extends Path2D implements Serializable {
1075         transient double[] doubleCoords;
1076 
1077         /**
1078          * Constructs a new empty double precision {@code Path2D} object
1079          * with a default winding rule of {@link #WIND_NON_ZERO}.
1080          *
1081          * @since 1.6
1082          */
1083         public Double() {
1084             this(WIND_NON_ZERO, INIT_SIZE);
1085         }
1086 
1087         /**
1088          * Constructs a new empty double precision {@code Path2D} object
1089          * with the specified winding rule to control operations that
1090          * require the interior of the path to be defined.
1091          *
1092          * @param rule the winding rule
1093          * @see #WIND_EVEN_ODD
1094          * @see #WIND_NON_ZERO
1095          * @since 1.6
1096          */
1097         public Double(int rule) {
1098             this(rule, INIT_SIZE);
1099         }
1100 
1101         /**
1102          * Constructs a new empty double precision {@code Path2D} object
1103          * with the specified winding rule and the specified initial
1104          * capacity to store path segments.
1105          * This number is an initial guess as to how many path segments
1106          * are in the path, but the storage is expanded as needed to store
1107          * whatever path segments are added to this path.
1108          *
1109          * @param rule the winding rule
1110          * @param initialCapacity the estimate for the number of path segments
1111          *                        in the path
1112          * @see #WIND_EVEN_ODD
1113          * @see #WIND_NON_ZERO
1114          * @since 1.6
1115          */
1116         public Double(int rule, int initialCapacity) {
1117             super(rule, initialCapacity);
1118             doubleCoords = new double[initialCapacity * 2];
1119         }
1120 
1121         /**
1122          * Constructs a new double precision {@code Path2D} object
1123          * from an arbitrary {@link Shape} object.
1124          * All of the initial geometry and the winding rule for this path are
1125          * taken from the specified {@code Shape} object.
1126          *
1127          * @param s the specified {@code Shape} object
1128          * @since 1.6
1129          */
1130         public Double(Shape s) {
1131             this(s, null);
1132         }
1133 
1134         /**
1135          * Constructs a new double precision {@code Path2D} object
1136          * from an arbitrary {@link Shape} object, transformed by an
1137          * {@link AffineTransform} object.
1138          * All of the initial geometry and the winding rule for this path are
1139          * taken from the specified {@code Shape} object and transformed
1140          * by the specified {@code AffineTransform} object.
1141          *
1142          * @param s the specified {@code Shape} object
1143          * @param at the specified {@code AffineTransform} object
1144          * @since 1.6
1145          */
1146         public Double(Shape s, AffineTransform at) {
1147             if (s instanceof Path2D) {
1148                 Path2D p2d = (Path2D) s;
1149                 setWindingRule(p2d.windingRule);
1150                 this.numTypes = p2d.numTypes;
1151                 // trim arrays:
1152                 this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
1153                 this.numCoords = p2d.numCoords;
1154                 this.doubleCoords = p2d.cloneCoordsDouble(at);
1155             } else {
1156                 PathIterator pi = s.getPathIterator(at);
1157                 setWindingRule(pi.getWindingRule());
1158                 this.pointTypes = new byte[INIT_SIZE];
1159                 this.doubleCoords = new double[INIT_SIZE * 2];
1160                 append(pi, false);
1161             }
1162         }
1163 
1164         @Override
1165         public final void trimToSize() {
1166             // trim arrays:
1167             if (numTypes < pointTypes.length) {
1168                 this.pointTypes = Arrays.copyOf(pointTypes, numTypes);
1169             }
1170             if (numCoords < doubleCoords.length) {
1171                 this.doubleCoords = Arrays.copyOf(doubleCoords, numCoords);
1172             }
1173         }
1174 
1175         @Override
1176         float[] cloneCoordsFloat(AffineTransform at) {
1177             // trim arrays:
1178             float[] ret = new float[numCoords];
1179             if (at == null) {
1180                 for (int i = 0; i < numCoords; i++) {
1181                     ret[i] = (float) doubleCoords[i];
1182                 }
1183             } else {
1184                 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1185             }
1186             return ret;
1187         }
1188 
1189         @Override
1190         double[] cloneCoordsDouble(AffineTransform at) {
1191             // trim arrays:
1192             double[] ret;
1193             if (at == null) {
1194                 ret = Arrays.copyOf(doubleCoords, numCoords);
1195             } else {
1196                 ret = new double[numCoords];
1197                 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1198             }
1199             return ret;
1200         }
1201 
1202         void append(float x, float y) {
1203             doubleCoords[numCoords++] = x;
1204             doubleCoords[numCoords++] = y;
1205         }
1206 
1207         void append(double x, double y) {
1208             doubleCoords[numCoords++] = x;
1209             doubleCoords[numCoords++] = y;
1210         }
1211 
1212         Point2D getPoint(int coordindex) {
1213             return new Point2D.Double(doubleCoords[coordindex],
1214                                       doubleCoords[coordindex+1]);
1215         }
1216 
1217         @Override
1218         void needRoom(boolean needMove, int newCoords) {
1219             if ((numTypes == 0) && needMove) {
1220                 throw new IllegalPathStateException("missing initial moveto "+
1221                                                     "in path definition");
1222             }
1223             if (numTypes >= pointTypes.length) {
1224                 pointTypes = expandPointTypes(pointTypes, 1);
1225             }
1226             if (numCoords > (doubleCoords.length - newCoords)) {
1227                 doubleCoords = expandCoords(doubleCoords, newCoords);
1228             }
1229         }
1230 
1231         static double[] expandCoords(double[] oldCoords, int needed) {
1232             final int oldSize = oldCoords.length;
1233             final int newSizeMin = oldSize + needed;
1234             if (newSizeMin < oldSize) {
1235                 // hard overflow failure - we can't even accommodate
1236                 // new items without overflowing
1237                 throw new ArrayIndexOutOfBoundsException(
1238                               "coords exceeds maximum capacity !");
1239             }
1240             // growth algorithm computation
1241             int grow = oldSize;
1242             if (grow > EXPAND_MAX_COORDS) {
1243                 grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
1244             } else if (grow < EXPAND_MIN) {
1245                 grow = EXPAND_MIN;
1246             }
1247             assert grow > needed;
1248 
1249             int newSize = oldSize + grow;
1250             if (newSize < newSizeMin) {
1251                 // overflow in growth algorithm computation
1252                 newSize = Integer.MAX_VALUE;
1253             }
1254             while (true) {
1255                 try {
1256                     // try allocating the larger array
1257                     return Arrays.copyOf(oldCoords, newSize);
1258                 } catch (OutOfMemoryError oome) {
1259                     if (newSize == newSizeMin) {
1260                         throw oome;
1261                     }
1262                 }
1263                 newSize = newSizeMin + (newSize - newSizeMin) / 2;
1264             }
1265         }
1266 
1267         /**
1268          * {@inheritDoc}
1269          * @since 1.6
1270          */
1271         public final synchronized void moveTo(double x, double y) {
1272             if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
1273                 doubleCoords[numCoords-2] = x;
1274                 doubleCoords[numCoords-1] = y;
1275             } else {
1276                 needRoom(false, 2);
1277                 pointTypes[numTypes++] = SEG_MOVETO;
1278                 doubleCoords[numCoords++] = x;
1279                 doubleCoords[numCoords++] = y;
1280             }
1281         }
1282 
1283         /**
1284          * {@inheritDoc}
1285          * @since 1.6
1286          */
1287         public final synchronized void lineTo(double x, double y) {
1288             needRoom(true, 2);
1289             pointTypes[numTypes++] = SEG_LINETO;
1290             doubleCoords[numCoords++] = x;
1291             doubleCoords[numCoords++] = y;
1292         }
1293 
1294         /**
1295          * {@inheritDoc}
1296          * @since 1.6
1297          */
1298         public final synchronized void quadTo(double x1, double y1,
1299                                               double x2, double y2)
1300         {
1301             needRoom(true, 4);
1302             pointTypes[numTypes++] = SEG_QUADTO;
1303             doubleCoords[numCoords++] = x1;
1304             doubleCoords[numCoords++] = y1;
1305             doubleCoords[numCoords++] = x2;
1306             doubleCoords[numCoords++] = y2;
1307         }
1308 
1309         /**
1310          * {@inheritDoc}
1311          * @since 1.6
1312          */
1313         public final synchronized void curveTo(double x1, double y1,
1314                                                double x2, double y2,
1315                                                double x3, double y3)
1316         {
1317             needRoom(true, 6);
1318             pointTypes[numTypes++] = SEG_CUBICTO;
1319             doubleCoords[numCoords++] = x1;
1320             doubleCoords[numCoords++] = y1;
1321             doubleCoords[numCoords++] = x2;
1322             doubleCoords[numCoords++] = y2;
1323             doubleCoords[numCoords++] = x3;
1324             doubleCoords[numCoords++] = y3;
1325         }
1326 
1327         int pointCrossings(double px, double py) {
1328             if (numTypes == 0) {
1329                 return 0;
1330             }
1331             double movx, movy, curx, cury, endx, endy;
1332             double[] coords = doubleCoords;
1333             curx = movx = coords[0];
1334             cury = movy = coords[1];
1335             int crossings = 0;
1336             int ci = 2;
1337             for (int i = 1; i < numTypes; i++) {
1338                 switch (pointTypes[i]) {
1339                 case PathIterator.SEG_MOVETO:
1340                     if (cury != movy) {
1341                         crossings +=
1342                             Curve.pointCrossingsForLine(px, py,
1343                                                         curx, cury,
1344                                                         movx, movy);
1345                     }
1346                     movx = curx = coords[ci++];
1347                     movy = cury = coords[ci++];
1348                     break;
1349                 case PathIterator.SEG_LINETO:
1350                     crossings +=
1351                         Curve.pointCrossingsForLine(px, py,
1352                                                     curx, cury,
1353                                                     endx = coords[ci++],
1354                                                     endy = coords[ci++]);
1355                     curx = endx;
1356                     cury = endy;
1357                     break;
1358                 case PathIterator.SEG_QUADTO:
1359                     crossings +=
1360                         Curve.pointCrossingsForQuad(px, py,
1361                                                     curx, cury,
1362                                                     coords[ci++],
1363                                                     coords[ci++],
1364                                                     endx = coords[ci++],
1365                                                     endy = coords[ci++],
1366                                                     0);
1367                     curx = endx;
1368                     cury = endy;
1369                     break;
1370             case PathIterator.SEG_CUBICTO:
1371                     crossings +=
1372                         Curve.pointCrossingsForCubic(px, py,
1373                                                      curx, cury,
1374                                                      coords[ci++],
1375                                                      coords[ci++],
1376                                                      coords[ci++],
1377                                                      coords[ci++],
1378                                                      endx = coords[ci++],
1379                                                      endy = coords[ci++],
1380                                                      0);
1381                     curx = endx;
1382                     cury = endy;
1383                     break;
1384                 case PathIterator.SEG_CLOSE:
1385                     if (cury != movy) {
1386                         crossings +=
1387                             Curve.pointCrossingsForLine(px, py,
1388                                                         curx, cury,
1389                                                         movx, movy);
1390                     }
1391                     curx = movx;
1392                     cury = movy;
1393                     break;
1394                 }
1395             }
1396             if (cury != movy) {
1397                 crossings +=
1398                     Curve.pointCrossingsForLine(px, py,
1399                                                 curx, cury,
1400                                                 movx, movy);
1401             }
1402             return crossings;
1403         }
1404 
1405         int rectCrossings(double rxmin, double rymin,
1406                           double rxmax, double rymax)
1407         {
1408             if (numTypes == 0) {
1409                 return 0;
1410             }
1411             double[] coords = doubleCoords;
1412             double curx, cury, movx, movy, endx, endy;
1413             curx = movx = coords[0];
1414             cury = movy = coords[1];
1415             int crossings = 0;
1416             int ci = 2;
1417             for (int i = 1;
1418                  crossings != Curve.RECT_INTERSECTS && i < numTypes;
1419                  i++)
1420             {
1421                 switch (pointTypes[i]) {
1422                 case PathIterator.SEG_MOVETO:
1423                     if (curx != movx || cury != movy) {
1424                         crossings =
1425                             Curve.rectCrossingsForLine(crossings,
1426                                                        rxmin, rymin,
1427                                                        rxmax, rymax,
1428                                                        curx, cury,
1429                                                        movx, movy);
1430                     }
1431                     // Count should always be a multiple of 2 here.
1432                     // assert((crossings & 1) != 0);
1433                     movx = curx = coords[ci++];
1434                     movy = cury = coords[ci++];
1435                     break;
1436                 case PathIterator.SEG_LINETO:
1437                     endx = coords[ci++];
1438                     endy = coords[ci++];
1439                     crossings =
1440                         Curve.rectCrossingsForLine(crossings,
1441                                                    rxmin, rymin,
1442                                                    rxmax, rymax,
1443                                                    curx, cury,
1444                                                    endx, endy);
1445                     curx = endx;
1446                     cury = endy;
1447                     break;
1448                 case PathIterator.SEG_QUADTO:
1449                     crossings =
1450                         Curve.rectCrossingsForQuad(crossings,
1451                                                    rxmin, rymin,
1452                                                    rxmax, rymax,
1453                                                    curx, cury,
1454                                                    coords[ci++],
1455                                                    coords[ci++],
1456                                                    endx = coords[ci++],
1457                                                    endy = coords[ci++],
1458                                                    0);
1459                     curx = endx;
1460                     cury = endy;
1461                     break;
1462                 case PathIterator.SEG_CUBICTO:
1463                     crossings =
1464                         Curve.rectCrossingsForCubic(crossings,
1465                                                     rxmin, rymin,
1466                                                     rxmax, rymax,
1467                                                     curx, cury,
1468                                                     coords[ci++],
1469                                                     coords[ci++],
1470                                                     coords[ci++],
1471                                                     coords[ci++],
1472                                                     endx = coords[ci++],
1473                                                     endy = coords[ci++],
1474                                                     0);
1475                     curx = endx;
1476                     cury = endy;
1477                     break;
1478                 case PathIterator.SEG_CLOSE:
1479                     if (curx != movx || cury != movy) {
1480                         crossings =
1481                             Curve.rectCrossingsForLine(crossings,
1482                                                        rxmin, rymin,
1483                                                        rxmax, rymax,
1484                                                        curx, cury,
1485                                                        movx, movy);
1486                     }
1487                     curx = movx;
1488                     cury = movy;
1489                     // Count should always be a multiple of 2 here.
1490                     // assert((crossings & 1) != 0);
1491                     break;
1492                 }
1493             }
1494             if (crossings != Curve.RECT_INTERSECTS &&
1495                 (curx != movx || cury != movy))
1496             {
1497                 crossings =
1498                     Curve.rectCrossingsForLine(crossings,
1499                                                rxmin, rymin,
1500                                                rxmax, rymax,
1501                                                curx, cury,
1502                                                movx, movy);
1503             }
1504             // Count should always be a multiple of 2 here.
1505             // assert((crossings & 1) != 0);
1506             return crossings;
1507         }
1508 
1509         /**
1510          * {@inheritDoc}
1511          * @since 1.6
1512          */
1513         public final void append(PathIterator pi, boolean connect) {
1514             double[] coords = new double[6];
1515             while (!pi.isDone()) {
1516                 switch (pi.currentSegment(coords)) {
1517                 case SEG_MOVETO:
1518                     if (!connect || numTypes < 1 || numCoords < 1) {
1519                         moveTo(coords[0], coords[1]);
1520                         break;
1521                     }
1522                     if (pointTypes[numTypes - 1] != SEG_CLOSE &&
1523                         doubleCoords[numCoords-2] == coords[0] &&
1524                         doubleCoords[numCoords-1] == coords[1])
1525                     {
1526                         // Collapse out initial moveto/lineto
1527                         break;
1528                     }
1529                     lineTo(coords[0], coords[1]);
1530                     break;
1531                 case SEG_LINETO:
1532                     lineTo(coords[0], coords[1]);
1533                     break;
1534                 case SEG_QUADTO:
1535                     quadTo(coords[0], coords[1],
1536                            coords[2], coords[3]);
1537                     break;
1538                 case SEG_CUBICTO:
1539                     curveTo(coords[0], coords[1],
1540                             coords[2], coords[3],
1541                             coords[4], coords[5]);
1542                     break;
1543                 case SEG_CLOSE:
1544                     closePath();
1545                     break;
1546                 }
1547                 pi.next();
1548                 connect = false;
1549             }
1550         }
1551 
1552         /**
1553          * {@inheritDoc}
1554          * @since 1.6
1555          */
1556         public final void transform(AffineTransform at) {
1557             at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2);
1558         }
1559 
1560         /**
1561          * {@inheritDoc}
1562          * @since 1.6
1563          */
1564         public final synchronized Rectangle2D getBounds2D() {
1565             double x1, y1, x2, y2;
1566             int i = numCoords;
1567             if (i > 0) {
1568                 y1 = y2 = doubleCoords[--i];
1569                 x1 = x2 = doubleCoords[--i];
1570                 while (i > 0) {
1571                     double y = doubleCoords[--i];
1572                     double x = doubleCoords[--i];
1573                     if (x < x1) x1 = x;
1574                     if (y < y1) y1 = y;
1575                     if (x > x2) x2 = x;
1576                     if (y > y2) y2 = y;
1577                 }
1578             } else {
1579                 x1 = y1 = x2 = y2 = 0.0;
1580             }
1581             return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
1582         }
1583 
1584         /**
1585          * {@inheritDoc}
1586          * <p>
1587          * The iterator for this class is not multi-threaded safe,
1588          * which means that the {@code Path2D} class does not
1589          * guarantee that modifications to the geometry of this
1590          * {@code Path2D} object do not affect any iterations of
1591          * that geometry that are already in process.
1592          *
1593          * @param at an {@code AffineTransform}
1594          * @return a new {@code PathIterator} that iterates along the boundary
1595          *         of this {@code Shape} and provides access to the geometry
1596          *         of this {@code Shape}'s outline
1597          * @since 1.6
1598          */
1599         public final PathIterator getPathIterator(AffineTransform at) {
1600             if (at == null) {
1601                 return new CopyIterator(this);
1602             } else {
1603                 return new TxIterator(this, at);
1604             }
1605         }
1606 
1607         /**
1608          * Creates a new object of the same class as this object.
1609          *
1610          * @return     a clone of this instance.
1611          * @exception  OutOfMemoryError    if there is not enough memory.
1612          * @see        java.lang.Cloneable
1613          * @since      1.6
1614          */
1615         public final Object clone() {
1616             // Note: It would be nice to have this return Path2D
1617             // but one of our subclasses (GeneralPath) needs to
1618             // offer "public Object clone()" for backwards
1619             // compatibility so we cannot restrict it further.
1620             // REMIND: Can we do both somehow?
1621             return new Path2D.Double(this);
1622         }
1623 
1624         /*
1625          * JDK 1.6 serialVersionUID
1626          */
1627         private static final long serialVersionUID = 1826762518450014216L;
1628 
1629         /**
1630          * Writes the default serializable fields to the
1631          * {@code ObjectOutputStream} followed by an explicit
1632          * serialization of the path segments stored in this
1633          * path.
1634          *
1635          * @serialData
1636          * <ol>
1637          * <li>The default serializable fields.
1638          * There are no default serializable fields as of 1.6.
1639          * <li>followed by
1640          * a byte indicating the storage type of the original object
1641          * as a hint (SERIAL_STORAGE_DBL_ARRAY)
1642          * <li>followed by
1643          * an integer indicating the number of path segments to follow (NP)
1644          * or -1 to indicate an unknown number of path segments follows
1645          * <li>followed by
1646          * an integer indicating the total number of coordinates to follow (NC)
1647          * or -1 to indicate an unknown number of coordinates follows
1648          * (NC should always be even since coordinates always appear in pairs
1649          *  representing an x,y pair)
1650          * <li>followed by
1651          * a byte indicating the winding rule
1652          * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
1653          *  {@link #WIND_NON_ZERO WIND_NON_ZERO})
1654          * <li>followed by
1655          * {@code NP} (or unlimited if {@code NP < 0}) sets of values consisting of
1656          * a single byte indicating a path segment type
1657          * followed by one or more pairs of float or double
1658          * values representing the coordinates of the path segment
1659          * <li>followed by
1660          * a byte indicating the end of the path (SERIAL_PATH_END).
1661          * </ol>
1662          * <p>
1663          * The following byte value constants are used in the serialized form
1664          * of {@code Path2D} objects:
1665          * <table class="striped">
1666          * <caption>Constants</caption>
1667          * <thead>
1668          * <tr>
1669          * <th>Constant Name</th>
1670          * <th>Byte Value</th>
1671          * <th>Followed by</th>
1672          * <th>Description</th>
1673          * </tr>
1674          * </thead>
1675          * <tbody>
1676          * <tr>
1677          * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
1678          * <td>0x30</td>
1679          * <td></td>
1680          * <td>A hint that the original {@code Path2D} object stored
1681          * the coordinates in a Java array of floats.</td>
1682          * </tr>
1683          * <tr>
1684          * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
1685          * <td>0x31</td>
1686          * <td></td>
1687          * <td>A hint that the original {@code Path2D} object stored
1688          * the coordinates in a Java array of doubles.</td>
1689          * </tr>
1690          * <tr>
1691          * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
1692          * <td>0x40</td>
1693          * <td>2 floats</td>
1694          * <td>A {@link #moveTo moveTo} path segment follows.</td>
1695          * </tr>
1696          * <tr>
1697          * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
1698          * <td>0x41</td>
1699          * <td>2 floats</td>
1700          * <td>A {@link #lineTo lineTo} path segment follows.</td>
1701          * </tr>
1702          * <tr>
1703          * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
1704          * <td>0x42</td>
1705          * <td>4 floats</td>
1706          * <td>A {@link #quadTo quadTo} path segment follows.</td>
1707          * </tr>
1708          * <tr>
1709          * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
1710          * <td>0x43</td>
1711          * <td>6 floats</td>
1712          * <td>A {@link #curveTo curveTo} path segment follows.</td>
1713          * </tr>
1714          * <tr>
1715          * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
1716          * <td>0x50</td>
1717          * <td>2 doubles</td>
1718          * <td>A {@link #moveTo moveTo} path segment follows.</td>
1719          * </tr>
1720          * <tr>
1721          * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
1722          * <td>0x51</td>
1723          * <td>2 doubles</td>
1724          * <td>A {@link #lineTo lineTo} path segment follows.</td>
1725          * </tr>
1726          * <tr>
1727          * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
1728          * <td>0x52</td>
1729          * <td>4 doubles</td>
1730          * <td>A {@link #curveTo curveTo} path segment follows.</td>
1731          * </tr>
1732          * <tr>
1733          * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
1734          * <td>0x53</td>
1735          * <td>6 doubles</td>
1736          * <td>A {@link #curveTo curveTo} path segment follows.</td>
1737          * </tr>
1738          * <tr>
1739          * <td>{@code SERIAL_SEG_CLOSE}</td>
1740          * <td>0x60</td>
1741          * <td></td>
1742          * <td>A {@link #closePath closePath} path segment.</td>
1743          * </tr>
1744          * <tr>
1745          * <td>{@code SERIAL_PATH_END}</td>
1746          * <td>0x61</td>
1747          * <td></td>
1748          * <td>There are no more path segments following.</td>
1749          * </tbody>
1750          * </table>
1751          *
1752          * @since 1.6
1753          */
1754         private void writeObject(java.io.ObjectOutputStream s)
1755             throws java.io.IOException
1756         {
1757             super.writeObject(s, true);
1758         }
1759 
1760         /**
1761          * Reads the default serializable fields from the
1762          * {@code ObjectInputStream} followed by an explicit
1763          * serialization of the path segments stored in this
1764          * path.
1765          * <p>
1766          * There are no default serializable fields as of 1.6.
1767          * <p>
1768          * The serial data for this object is described in the
1769          * writeObject method.
1770          *
1771          * @since 1.6
1772          */
1773         private void readObject(java.io.ObjectInputStream s)
1774             throws java.lang.ClassNotFoundException, java.io.IOException
1775         {
1776             super.readObject(s, true);
1777         }
1778 
1779         static class CopyIterator extends Path2D.Iterator {
1780             double[] doubleCoords;
1781 
1782             CopyIterator(Path2D.Double p2dd) {
1783                 super(p2dd);
1784                 this.doubleCoords = p2dd.doubleCoords;
1785             }
1786 
1787             public int currentSegment(float[] coords) {
1788                 int type = path.pointTypes[typeIdx];
1789                 int numCoords = curvecoords[type];
1790                 if (numCoords > 0) {
1791                     for (int i = 0; i < numCoords; i++) {
1792                         coords[i] = (float) doubleCoords[pointIdx + i];
1793                     }
1794                 }
1795                 return type;
1796             }
1797 
1798             public int currentSegment(double[] coords) {
1799                 int type = path.pointTypes[typeIdx];
1800                 int numCoords = curvecoords[type];
1801                 if (numCoords > 0) {
1802                     System.arraycopy(doubleCoords, pointIdx,
1803                                      coords, 0, numCoords);
1804                 }
1805                 return type;
1806             }
1807         }
1808 
1809         static class TxIterator extends Path2D.Iterator {
1810             double[] doubleCoords;
1811             AffineTransform affine;
1812 
1813             TxIterator(Path2D.Double p2dd, AffineTransform at) {
1814                 super(p2dd);
1815                 this.doubleCoords = p2dd.doubleCoords;
1816                 this.affine = at;
1817             }
1818 
1819             public int currentSegment(float[] coords) {
1820                 int type = path.pointTypes[typeIdx];
1821                 int numCoords = curvecoords[type];
1822                 if (numCoords > 0) {
1823                     affine.transform(doubleCoords, pointIdx,
1824                                      coords, 0, numCoords / 2);
1825                 }
1826                 return type;
1827             }
1828 
1829             public int currentSegment(double[] coords) {
1830                 int type = path.pointTypes[typeIdx];
1831                 int numCoords = curvecoords[type];
1832                 if (numCoords > 0) {
1833                     affine.transform(doubleCoords, pointIdx,
1834                                      coords, 0, numCoords / 2);
1835                 }
1836                 return type;
1837             }
1838         }
1839     }
1840 
1841     /**
1842      * Adds a point to the path by moving to the specified
1843      * coordinates specified in double precision.
1844      *
1845      * @param x the specified X coordinate
1846      * @param y the specified Y coordinate
1847      * @since 1.6
1848      */
1849     public abstract void moveTo(double x, double y);
1850 
1851     /**
1852      * Adds a point to the path by drawing a straight line from the
1853      * current coordinates to the new specified coordinates
1854      * specified in double precision.
1855      *
1856      * @param x the specified X coordinate
1857      * @param y the specified Y coordinate
1858      * @since 1.6
1859      */
1860     public abstract void lineTo(double x, double y);
1861 
1862     /**
1863      * Adds a curved segment, defined by two new points, to the path by
1864      * drawing a Quadratic curve that intersects both the current
1865      * coordinates and the specified coordinates {@code (x2,y2)},
1866      * using the specified point {@code (x1,y1)} as a quadratic
1867      * parametric control point.
1868      * All coordinates are specified in double precision.
1869      *
1870      * @param x1 the X coordinate of the quadratic control point
1871      * @param y1 the Y coordinate of the quadratic control point
1872      * @param x2 the X coordinate of the final end point
1873      * @param y2 the Y coordinate of the final end point
1874      * @since 1.6
1875      */
1876     public abstract void quadTo(double x1, double y1,
1877                                 double x2, double y2);
1878 
1879     /**
1880      * Adds a curved segment, defined by three new points, to the path by
1881      * drawing a B&eacute;zier curve that intersects both the current
1882      * coordinates and the specified coordinates {@code (x3,y3)},
1883      * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
1884      * B&eacute;zier control points.
1885      * All coordinates are specified in double precision.
1886      *
1887      * @param x1 the X coordinate of the first B&eacute;zier control point
1888      * @param y1 the Y coordinate of the first B&eacute;zier control point
1889      * @param x2 the X coordinate of the second B&eacute;zier control point
1890      * @param y2 the Y coordinate of the second B&eacute;zier control point
1891      * @param x3 the X coordinate of the final end point
1892      * @param y3 the Y coordinate of the final end point
1893      * @since 1.6
1894      */
1895     public abstract void curveTo(double x1, double y1,
1896                                  double x2, double y2,
1897                                  double x3, double y3);
1898 
1899     /**
1900      * Closes the current subpath by drawing a straight line back to
1901      * the coordinates of the last {@code moveTo}.  If the path is already
1902      * closed then this method has no effect.
1903      *
1904      * @since 1.6
1905      */
1906     public final synchronized void closePath() {
1907         if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
1908             needRoom(true, 0);
1909             pointTypes[numTypes++] = SEG_CLOSE;
1910         }
1911     }
1912 
1913     /**
1914      * Appends the geometry of the specified {@code Shape} object to the
1915      * path, possibly connecting the new geometry to the existing path
1916      * segments with a line segment.
1917      * If the {@code connect} parameter is {@code true} and the
1918      * path is not empty then any initial {@code moveTo} in the
1919      * geometry of the appended {@code Shape}
1920      * is turned into a {@code lineTo} segment.
1921      * If the destination coordinates of such a connecting {@code lineTo}
1922      * segment match the ending coordinates of a currently open
1923      * subpath then the segment is omitted as superfluous.
1924      * The winding rule of the specified {@code Shape} is ignored
1925      * and the appended geometry is governed by the winding
1926      * rule specified for this path.
1927      *
1928      * @param s the {@code Shape} whose geometry is appended
1929      *          to this path
1930      * @param connect a boolean to control whether or not to turn an initial
1931      *                {@code moveTo} segment into a {@code lineTo} segment
1932      *                to connect the new geometry to the existing path
1933      * @since 1.6
1934      */
1935     public final void append(Shape s, boolean connect) {
1936         append(s.getPathIterator(null), connect);
1937     }
1938 
1939     /**
1940      * Appends the geometry of the specified
1941      * {@link PathIterator} object
1942      * to the path, possibly connecting the new geometry to the existing
1943      * path segments with a line segment.
1944      * If the {@code connect} parameter is {@code true} and the
1945      * path is not empty then any initial {@code moveTo} in the
1946      * geometry of the appended {@code Shape} is turned into a
1947      * {@code lineTo} segment.
1948      * If the destination coordinates of such a connecting {@code lineTo}
1949      * segment match the ending coordinates of a currently open
1950      * subpath then the segment is omitted as superfluous.
1951      * The winding rule of the specified {@code Shape} is ignored
1952      * and the appended geometry is governed by the winding
1953      * rule specified for this path.
1954      *
1955      * @param pi the {@code PathIterator} whose geometry is appended to
1956      *           this path
1957      * @param connect a boolean to control whether or not to turn an initial
1958      *                {@code moveTo} segment into a {@code lineTo} segment
1959      *                to connect the new geometry to the existing path
1960      * @since 1.6
1961      */
1962     public abstract void append(PathIterator pi, boolean connect);
1963 
1964     /**
1965      * Returns the fill style winding rule.
1966      *
1967      * @return an integer representing the current winding rule.
1968      * @see #WIND_EVEN_ODD
1969      * @see #WIND_NON_ZERO
1970      * @see #setWindingRule
1971      * @since 1.6
1972      */
1973     public final synchronized int getWindingRule() {
1974         return windingRule;
1975     }
1976 
1977     /**
1978      * Sets the winding rule for this path to the specified value.
1979      *
1980      * @param rule an integer representing the specified
1981      *             winding rule
1982      * @exception IllegalArgumentException if
1983      *          {@code rule} is not either
1984      *          {@link #WIND_EVEN_ODD} or
1985      *          {@link #WIND_NON_ZERO}
1986      * @see #getWindingRule
1987      * @since 1.6
1988      */
1989     public final void setWindingRule(int rule) {
1990         if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
1991             throw new IllegalArgumentException("winding rule must be "+
1992                                                "WIND_EVEN_ODD or "+
1993                                                "WIND_NON_ZERO");
1994         }
1995         windingRule = rule;
1996     }
1997 
1998     /**
1999      * Returns the coordinates most recently added to the end of the path
2000      * as a {@link Point2D} object.
2001      *
2002      * @return a {@code Point2D} object containing the ending coordinates of
2003      *         the path or {@code null} if there are no points in the path.
2004      * @since 1.6
2005      */
2006     public final synchronized Point2D getCurrentPoint() {
2007         int index = numCoords;
2008         if (numTypes < 1 || index < 1) {
2009             return null;
2010         }
2011         if (pointTypes[numTypes - 1] == SEG_CLOSE) {
2012         loop:
2013             for (int i = numTypes - 2; i > 0; i--) {
2014                 switch (pointTypes[i]) {
2015                 case SEG_MOVETO:
2016                     break loop;
2017                 case SEG_LINETO:
2018                     index -= 2;
2019                     break;
2020                 case SEG_QUADTO:
2021                     index -= 4;
2022                     break;
2023                 case SEG_CUBICTO:
2024                     index -= 6;
2025                     break;
2026                 case SEG_CLOSE:
2027                     break;
2028                 }
2029             }
2030         }
2031         return getPoint(index - 2);
2032     }
2033 
2034     /**
2035      * Resets the path to empty.  The append position is set back to the
2036      * beginning of the path and all coordinates and point types are
2037      * forgotten.
2038      *
2039      * @since 1.6
2040      */
2041     public final synchronized void reset() {
2042         numTypes = numCoords = 0;
2043     }
2044 
2045     /**
2046      * Transforms the geometry of this path using the specified
2047      * {@link AffineTransform}.
2048      * The geometry is transformed in place, which permanently changes the
2049      * boundary defined by this object.
2050      *
2051      * @param at the {@code AffineTransform} used to transform the area
2052      * @since 1.6
2053      */
2054     public abstract void transform(AffineTransform at);
2055 
2056     /**
2057      * Returns a new {@code Shape} representing a transformed version
2058      * of this {@code Path2D}.
2059      * Note that the exact type and coordinate precision of the return
2060      * value is not specified for this method.
2061      * The method will return a Shape that contains no less precision
2062      * for the transformed geometry than this {@code Path2D} currently
2063      * maintains, but it may contain no more precision either.
2064      * If the tradeoff of precision vs. storage size in the result is
2065      * important then the convenience constructors in the
2066      * {@link Path2D.Float#Float(Shape, AffineTransform) Path2D.Float}
2067      * and
2068      * {@link Path2D.Double#Double(Shape, AffineTransform) Path2D.Double}
2069      * subclasses should be used to make the choice explicit.
2070      *
2071      * @param at the {@code AffineTransform} used to transform a
2072      *           new {@code Shape}.
2073      * @return a new {@code Shape}, transformed with the specified
2074      *         {@code AffineTransform}.
2075      * @since 1.6
2076      */
2077     public final synchronized Shape createTransformedShape(AffineTransform at) {
2078         Path2D p2d = (Path2D) clone();
2079         if (at != null) {
2080             p2d.transform(at);
2081         }
2082         return p2d;
2083     }
2084 
2085     /**
2086      * {@inheritDoc}
2087      * @since 1.6
2088      */
2089     public final Rectangle getBounds() {
2090         return getBounds2D().getBounds();
2091     }
2092 
2093     /**
2094      * Tests if the specified coordinates are inside the closed
2095      * boundary of the specified {@link PathIterator}.
2096      * <p>
2097      * This method provides a basic facility for implementors of
2098      * the {@link Shape} interface to implement support for the
2099      * {@link Shape#contains(double, double)} method.
2100      *
2101      * @param pi the specified {@code PathIterator}
2102      * @param x the specified X coordinate
2103      * @param y the specified Y coordinate
2104      * @return {@code true} if the specified coordinates are inside the
2105      *         specified {@code PathIterator}; {@code false} otherwise
2106      * @since 1.6
2107      */
2108     public static boolean contains(PathIterator pi, double x, double y) {
2109         if (x * 0.0 + y * 0.0 == 0.0) {
2110             /* N * 0.0 is 0.0 only if N is finite.
2111              * Here we know that both x and y are finite.
2112              */
2113             int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
2114             int cross = Curve.pointCrossingsForPath(pi, x, y);
2115             return ((cross & mask) != 0);
2116         } else {
2117             /* Either x or y was infinite or NaN.
2118              * A NaN always produces a negative response to any test
2119              * and Infinity values cannot be "inside" any path so
2120              * they should return false as well.
2121              */
2122             return false;
2123         }
2124     }
2125 
2126     /**
2127      * Tests if the specified {@link Point2D} is inside the closed
2128      * boundary of the specified {@link PathIterator}.
2129      * <p>
2130      * This method provides a basic facility for implementors of
2131      * the {@link Shape} interface to implement support for the
2132      * {@link Shape#contains(Point2D)} method.
2133      *
2134      * @param pi the specified {@code PathIterator}
2135      * @param p the specified {@code Point2D}
2136      * @return {@code true} if the specified coordinates are inside the
2137      *         specified {@code PathIterator}; {@code false} otherwise
2138      * @since 1.6
2139      */
2140     public static boolean contains(PathIterator pi, Point2D p) {
2141         return contains(pi, p.getX(), p.getY());
2142     }
2143 
2144     /**
2145      * {@inheritDoc}
2146      * @since 1.6
2147      */
2148     public final boolean contains(double x, double y) {
2149         if (x * 0.0 + y * 0.0 == 0.0) {
2150             /* N * 0.0 is 0.0 only if N is finite.
2151              * Here we know that both x and y are finite.
2152              */
2153             if (numTypes < 2) {
2154                 return false;
2155             }
2156             int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
2157             return ((pointCrossings(x, y) & mask) != 0);
2158         } else {
2159             /* Either x or y was infinite or NaN.
2160              * A NaN always produces a negative response to any test
2161              * and Infinity values cannot be "inside" any path so
2162              * they should return false as well.
2163              */
2164             return false;
2165         }
2166     }
2167 
2168     /**
2169      * {@inheritDoc}
2170      * @since 1.6
2171      */
2172     public final boolean contains(Point2D p) {
2173         return contains(p.getX(), p.getY());
2174     }
2175 
2176     /**
2177      * Tests if the specified rectangular area is entirely inside the
2178      * closed boundary of the specified {@link PathIterator}.
2179      * <p>
2180      * This method provides a basic facility for implementors of
2181      * the {@link Shape} interface to implement support for the
2182      * {@link Shape#contains(double, double, double, double)} method.
2183      * <p>
2184      * This method object may conservatively return false in
2185      * cases where the specified rectangular area intersects a
2186      * segment of the path, but that segment does not represent a
2187      * boundary between the interior and exterior of the path.
2188      * Such segments could lie entirely within the interior of the
2189      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2190      * winding rule or if the segments are retraced in the reverse
2191      * direction such that the two sets of segments cancel each
2192      * other out without any exterior area falling between them.
2193      * To determine whether segments represent true boundaries of
2194      * the interior of the path would require extensive calculations
2195      * involving all of the segments of the path and the winding
2196      * rule and are thus beyond the scope of this implementation.
2197      *
2198      * @param pi the specified {@code PathIterator}
2199      * @param x the specified X coordinate
2200      * @param y the specified Y coordinate
2201      * @param w the width of the specified rectangular area
2202      * @param h the height of the specified rectangular area
2203      * @return {@code true} if the specified {@code PathIterator} contains
2204      *         the specified rectangular area; {@code false} otherwise.
2205      * @since 1.6
2206      */
2207     public static boolean contains(PathIterator pi,
2208                                    double x, double y, double w, double h)
2209     {
2210         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2211             /* [xy]+[wh] is NaN if any of those values are NaN,
2212              * or if adding the two together would produce NaN
2213              * by virtue of adding opposing Infinte values.
2214              * Since we need to add them below, their sum must
2215              * not be NaN.
2216              * We return false because NaN always produces a
2217              * negative response to tests
2218              */
2219             return false;
2220         }
2221         if (w <= 0 || h <= 0) {
2222             return false;
2223         }
2224         int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2225         int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h);
2226         return (crossings != Curve.RECT_INTERSECTS &&
2227                 (crossings & mask) != 0);
2228     }
2229 
2230     /**
2231      * Tests if the specified {@link Rectangle2D} is entirely inside the
2232      * closed boundary of the specified {@link PathIterator}.
2233      * <p>
2234      * This method provides a basic facility for implementors of
2235      * the {@link Shape} interface to implement support for the
2236      * {@link Shape#contains(Rectangle2D)} method.
2237      * <p>
2238      * This method object may conservatively return false in
2239      * cases where the specified rectangular area intersects a
2240      * segment of the path, but that segment does not represent a
2241      * boundary between the interior and exterior of the path.
2242      * Such segments could lie entirely within the interior of the
2243      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2244      * winding rule or if the segments are retraced in the reverse
2245      * direction such that the two sets of segments cancel each
2246      * other out without any exterior area falling between them.
2247      * To determine whether segments represent true boundaries of
2248      * the interior of the path would require extensive calculations
2249      * involving all of the segments of the path and the winding
2250      * rule and are thus beyond the scope of this implementation.
2251      *
2252      * @param pi the specified {@code PathIterator}
2253      * @param r a specified {@code Rectangle2D}
2254      * @return {@code true} if the specified {@code PathIterator} contains
2255      *         the specified {@code Rectangle2D}; {@code false} otherwise.
2256      * @since 1.6
2257      */
2258     public static boolean contains(PathIterator pi, Rectangle2D r) {
2259         return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
2260     }
2261 
2262     /**
2263      * {@inheritDoc}
2264      * <p>
2265      * This method object may conservatively return false in
2266      * cases where the specified rectangular area intersects a
2267      * segment of the path, but that segment does not represent a
2268      * boundary between the interior and exterior of the path.
2269      * Such segments could lie entirely within the interior of the
2270      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2271      * winding rule or if the segments are retraced in the reverse
2272      * direction such that the two sets of segments cancel each
2273      * other out without any exterior area falling between them.
2274      * To determine whether segments represent true boundaries of
2275      * the interior of the path would require extensive calculations
2276      * involving all of the segments of the path and the winding
2277      * rule and are thus beyond the scope of this implementation.
2278      *
2279      * @since 1.6
2280      */
2281     public final boolean contains(double x, double y, double w, double h) {
2282         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2283             /* [xy]+[wh] is NaN if any of those values are NaN,
2284              * or if adding the two together would produce NaN
2285              * by virtue of adding opposing Infinte values.
2286              * Since we need to add them below, their sum must
2287              * not be NaN.
2288              * We return false because NaN always produces a
2289              * negative response to tests
2290              */
2291             return false;
2292         }
2293         if (w <= 0 || h <= 0) {
2294             return false;
2295         }
2296         int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2297         int crossings = rectCrossings(x, y, x+w, y+h);
2298         return (crossings != Curve.RECT_INTERSECTS &&
2299                 (crossings & mask) != 0);
2300     }
2301 
2302     /**
2303      * {@inheritDoc}
2304      * <p>
2305      * This method object may conservatively return false in
2306      * cases where the specified rectangular area intersects a
2307      * segment of the path, but that segment does not represent a
2308      * boundary between the interior and exterior of the path.
2309      * Such segments could lie entirely within the interior of the
2310      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2311      * winding rule or if the segments are retraced in the reverse
2312      * direction such that the two sets of segments cancel each
2313      * other out without any exterior area falling between them.
2314      * To determine whether segments represent true boundaries of
2315      * the interior of the path would require extensive calculations
2316      * involving all of the segments of the path and the winding
2317      * rule and are thus beyond the scope of this implementation.
2318      *
2319      * @since 1.6
2320      */
2321     public final boolean contains(Rectangle2D r) {
2322         return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2323     }
2324 
2325     /**
2326      * Tests if the interior of the specified {@link PathIterator}
2327      * intersects the interior of a specified set of rectangular
2328      * coordinates.
2329      * <p>
2330      * This method provides a basic facility for implementors of
2331      * the {@link Shape} interface to implement support for the
2332      * {@link Shape#intersects(double, double, double, double)} method.
2333      * <p>
2334      * This method object may conservatively return true in
2335      * cases where the specified rectangular area intersects a
2336      * segment of the path, but that segment does not represent a
2337      * boundary between the interior and exterior of the path.
2338      * Such a case may occur if some set of segments of the
2339      * path are retraced in the reverse direction such that the
2340      * two sets of segments cancel each other out without any
2341      * interior area between them.
2342      * To determine whether segments represent true boundaries of
2343      * the interior of the path would require extensive calculations
2344      * involving all of the segments of the path and the winding
2345      * rule and are thus beyond the scope of this implementation.
2346      *
2347      * @param pi the specified {@code PathIterator}
2348      * @param x the specified X coordinate
2349      * @param y the specified Y coordinate
2350      * @param w the width of the specified rectangular coordinates
2351      * @param h the height of the specified rectangular coordinates
2352      * @return {@code true} if the specified {@code PathIterator} and
2353      *         the interior of the specified set of rectangular
2354      *         coordinates intersect each other; {@code false} otherwise.
2355      * @since 1.6
2356      */
2357     public static boolean intersects(PathIterator pi,
2358                                      double x, double y, double w, double h)
2359     {
2360         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2361             /* [xy]+[wh] is NaN if any of those values are NaN,
2362              * or if adding the two together would produce NaN
2363              * by virtue of adding opposing Infinte values.
2364              * Since we need to add them below, their sum must
2365              * not be NaN.
2366              * We return false because NaN always produces a
2367              * negative response to tests
2368              */
2369             return false;
2370         }
2371         if (w <= 0 || h <= 0) {
2372             return false;
2373         }
2374         int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2375         int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h);
2376         return (crossings == Curve.RECT_INTERSECTS ||
2377                 (crossings & mask) != 0);
2378     }
2379 
2380     /**
2381      * Tests if the interior of the specified {@link PathIterator}
2382      * intersects the interior of a specified {@link Rectangle2D}.
2383      * <p>
2384      * This method provides a basic facility for implementors of
2385      * the {@link Shape} interface to implement support for the
2386      * {@link Shape#intersects(Rectangle2D)} method.
2387      * <p>
2388      * This method object may conservatively return true in
2389      * cases where the specified rectangular area intersects a
2390      * segment of the path, but that segment does not represent a
2391      * boundary between the interior and exterior of the path.
2392      * Such a case may occur if some set of segments of the
2393      * path are retraced in the reverse direction such that the
2394      * two sets of segments cancel each other out without any
2395      * interior area between them.
2396      * To determine whether segments represent true boundaries of
2397      * the interior of the path would require extensive calculations
2398      * involving all of the segments of the path and the winding
2399      * rule and are thus beyond the scope of this implementation.
2400      *
2401      * @param pi the specified {@code PathIterator}
2402      * @param r the specified {@code Rectangle2D}
2403      * @return {@code true} if the specified {@code PathIterator} and
2404      *         the interior of the specified {@code Rectangle2D}
2405      *         intersect each other; {@code false} otherwise.
2406      * @since 1.6
2407      */
2408     public static boolean intersects(PathIterator pi, Rectangle2D r) {
2409         return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
2410     }
2411 
2412     /**
2413      * {@inheritDoc}
2414      * <p>
2415      * This method object may conservatively return true in
2416      * cases where the specified rectangular area intersects a
2417      * segment of the path, but that segment does not represent a
2418      * boundary between the interior and exterior of the path.
2419      * Such a case may occur if some set of segments of the
2420      * path are retraced in the reverse direction such that the
2421      * two sets of segments cancel each other out without any
2422      * interior area between them.
2423      * To determine whether segments represent true boundaries of
2424      * the interior of the path would require extensive calculations
2425      * involving all of the segments of the path and the winding
2426      * rule and are thus beyond the scope of this implementation.
2427      *
2428      * @since 1.6
2429      */
2430     public final boolean intersects(double x, double y, double w, double h) {
2431         if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2432             /* [xy]+[wh] is NaN if any of those values are NaN,
2433              * or if adding the two together would produce NaN
2434              * by virtue of adding opposing Infinte values.
2435              * Since we need to add them below, their sum must
2436              * not be NaN.
2437              * We return false because NaN always produces a
2438              * negative response to tests
2439              */
2440             return false;
2441         }
2442         if (w <= 0 || h <= 0) {
2443             return false;
2444         }
2445         int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2446         int crossings = rectCrossings(x, y, x+w, y+h);
2447         return (crossings == Curve.RECT_INTERSECTS ||
2448                 (crossings & mask) != 0);
2449     }
2450 
2451     /**
2452      * {@inheritDoc}
2453      * <p>
2454      * This method object may conservatively return true in
2455      * cases where the specified rectangular area intersects a
2456      * segment of the path, but that segment does not represent a
2457      * boundary between the interior and exterior of the path.
2458      * Such a case may occur if some set of segments of the
2459      * path are retraced in the reverse direction such that the
2460      * two sets of segments cancel each other out without any
2461      * interior area between them.
2462      * To determine whether segments represent true boundaries of
2463      * the interior of the path would require extensive calculations
2464      * involving all of the segments of the path and the winding
2465      * rule and are thus beyond the scope of this implementation.
2466      *
2467      * @since 1.6
2468      */
2469     public final boolean intersects(Rectangle2D r) {
2470         return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2471     }
2472 
2473     /**
2474      * {@inheritDoc}
2475      * <p>
2476      * The iterator for this class is not multi-threaded safe,
2477      * which means that this {@code Path2D} class does not
2478      * guarantee that modifications to the geometry of this
2479      * {@code Path2D} object do not affect any iterations of
2480      * that geometry that are already in process.
2481      *
2482      * @since 1.6
2483      */
2484     public final PathIterator getPathIterator(AffineTransform at,
2485                                               double flatness)
2486     {
2487         return new FlatteningPathIterator(getPathIterator(at), flatness);
2488     }
2489 
2490     /**
2491      * Creates a new object of the same class as this object.
2492      *
2493      * @return     a clone of this instance.
2494      * @exception  OutOfMemoryError            if there is not enough memory.
2495      * @see        java.lang.Cloneable
2496      * @since      1.6
2497      */
2498     public abstract Object clone();
2499         // Note: It would be nice to have this return Path2D
2500         // but one of our subclasses (GeneralPath) needs to
2501         // offer "public Object clone()" for backwards
2502         // compatibility so we cannot restrict it further.
2503         // REMIND: Can we do both somehow?
2504 
2505     /**
2506      * Trims the capacity of this Path2D instance to its current
2507      * size. An application can use this operation to minimize the
2508      * storage of a path.
2509      *
2510      * @since 10
2511      */
2512     public abstract void trimToSize();
2513 
2514     /*
2515      * Support fields and methods for serializing the subclasses.
2516      */
2517     private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
2518     private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;
2519 
2520     private static final byte SERIAL_SEG_FLT_MOVETO    = 0x40;
2521     private static final byte SERIAL_SEG_FLT_LINETO    = 0x41;
2522     private static final byte SERIAL_SEG_FLT_QUADTO    = 0x42;
2523     private static final byte SERIAL_SEG_FLT_CUBICTO   = 0x43;
2524 
2525     private static final byte SERIAL_SEG_DBL_MOVETO    = 0x50;
2526     private static final byte SERIAL_SEG_DBL_LINETO    = 0x51;
2527     private static final byte SERIAL_SEG_DBL_QUADTO    = 0x52;
2528     private static final byte SERIAL_SEG_DBL_CUBICTO   = 0x53;
2529 
2530     private static final byte SERIAL_SEG_CLOSE         = 0x60;
2531     private static final byte SERIAL_PATH_END          = 0x61;
2532 
2533     final void writeObject(java.io.ObjectOutputStream s, boolean isdbl)
2534         throws java.io.IOException
2535     {
2536         s.defaultWriteObject();
2537 
2538         float[] fCoords;
2539         double[] dCoords;
2540 
2541         if (isdbl) {
2542             dCoords = ((Path2D.Double) this).doubleCoords;
2543             fCoords = null;
2544         } else {
2545             fCoords = ((Path2D.Float) this).floatCoords;
2546             dCoords = null;
2547         }
2548 
2549         int numTypes = this.numTypes;
2550 
2551         s.writeByte(isdbl
2552                     ? SERIAL_STORAGE_DBL_ARRAY
2553                     : SERIAL_STORAGE_FLT_ARRAY);
2554         s.writeInt(numTypes);
2555         s.writeInt(numCoords);
2556         s.writeByte((byte) windingRule);
2557 
2558         int cindex = 0;
2559         for (int i = 0; i < numTypes; i++) {
2560             int npoints;
2561             byte serialtype;
2562             switch (pointTypes[i]) {
2563             case SEG_MOVETO:
2564                 npoints = 1;
2565                 serialtype = (isdbl
2566                               ? SERIAL_SEG_DBL_MOVETO
2567                               : SERIAL_SEG_FLT_MOVETO);
2568                 break;
2569             case SEG_LINETO:
2570                 npoints = 1;
2571                 serialtype = (isdbl
2572                               ? SERIAL_SEG_DBL_LINETO
2573                               : SERIAL_SEG_FLT_LINETO);
2574                 break;
2575             case SEG_QUADTO:
2576                 npoints = 2;
2577                 serialtype = (isdbl
2578                               ? SERIAL_SEG_DBL_QUADTO
2579                               : SERIAL_SEG_FLT_QUADTO);
2580                 break;
2581             case SEG_CUBICTO:
2582                 npoints = 3;
2583                 serialtype = (isdbl
2584                               ? SERIAL_SEG_DBL_CUBICTO
2585                               : SERIAL_SEG_FLT_CUBICTO);
2586                 break;
2587             case SEG_CLOSE:
2588                 npoints = 0;
2589                 serialtype = SERIAL_SEG_CLOSE;
2590                 break;
2591 
2592             default:
2593                 // Should never happen
2594                 throw new InternalError("unrecognized path type");
2595             }
2596             s.writeByte(serialtype);
2597             while (--npoints >= 0) {
2598                 if (isdbl) {
2599                     s.writeDouble(dCoords[cindex++]);
2600                     s.writeDouble(dCoords[cindex++]);
2601                 } else {
2602                     s.writeFloat(fCoords[cindex++]);
2603                     s.writeFloat(fCoords[cindex++]);
2604                 }
2605             }
2606         }
2607         s.writeByte(SERIAL_PATH_END);
2608     }
2609 
2610     final void readObject(java.io.ObjectInputStream s, boolean storedbl)
2611         throws java.lang.ClassNotFoundException, java.io.IOException
2612     {
2613         s.defaultReadObject();
2614 
2615         // The subclass calls this method with the storage type that
2616         // they want us to use (storedbl) so we ignore the storage
2617         // method hint from the stream.
2618         s.readByte();
2619         int nT = s.readInt();
2620         int nC = s.readInt();
2621         try {
2622             setWindingRule(s.readByte());
2623         } catch (IllegalArgumentException iae) {
2624             throw new java.io.InvalidObjectException(iae.getMessage());
2625         }
2626 
2627         // Accept the size from the stream only if it is less than INIT_SIZE
2628         // otherwise the size will be based on the real data in the stream
2629         pointTypes = new byte[(nT < 0 || nT > INIT_SIZE) ? INIT_SIZE : nT];
2630         final int initX2 = INIT_SIZE * 2;
2631         if (nC < 0 || nC > initX2) {
2632             nC = initX2;
2633         }
2634         if (storedbl) {
2635             ((Path2D.Double) this).doubleCoords = new double[nC];
2636         } else {
2637             ((Path2D.Float) this).floatCoords = new float[nC];
2638         }
2639 
2640     PATHDONE:
2641         for (int i = 0; nT < 0 || i < nT; i++) {
2642             boolean isdbl;
2643             int npoints;
2644             byte segtype;
2645 
2646             byte serialtype = s.readByte();
2647             switch (serialtype) {
2648             case SERIAL_SEG_FLT_MOVETO:
2649                 isdbl = false;
2650                 npoints = 1;
2651                 segtype = SEG_MOVETO;
2652                 break;
2653             case SERIAL_SEG_FLT_LINETO:
2654                 isdbl = false;
2655                 npoints = 1;
2656                 segtype = SEG_LINETO;
2657                 break;
2658             case SERIAL_SEG_FLT_QUADTO:
2659                 isdbl = false;
2660                 npoints = 2;
2661                 segtype = SEG_QUADTO;
2662                 break;
2663             case SERIAL_SEG_FLT_CUBICTO:
2664                 isdbl = false;
2665                 npoints = 3;
2666                 segtype = SEG_CUBICTO;
2667                 break;
2668 
2669             case SERIAL_SEG_DBL_MOVETO:
2670                 isdbl = true;
2671                 npoints = 1;
2672                 segtype = SEG_MOVETO;
2673                 break;
2674             case SERIAL_SEG_DBL_LINETO:
2675                 isdbl = true;
2676                 npoints = 1;
2677                 segtype = SEG_LINETO;
2678                 break;
2679             case SERIAL_SEG_DBL_QUADTO:
2680                 isdbl = true;
2681                 npoints = 2;
2682                 segtype = SEG_QUADTO;
2683                 break;
2684             case SERIAL_SEG_DBL_CUBICTO:
2685                 isdbl = true;
2686                 npoints = 3;
2687                 segtype = SEG_CUBICTO;
2688                 break;
2689 
2690             case SERIAL_SEG_CLOSE:
2691                 isdbl = false;
2692                 npoints = 0;
2693                 segtype = SEG_CLOSE;
2694                 break;
2695 
2696             case SERIAL_PATH_END:
2697                 if (nT < 0) {
2698                     break PATHDONE;
2699                 }
2700                 throw new StreamCorruptedException("unexpected PATH_END");
2701 
2702             default:
2703                 throw new StreamCorruptedException("unrecognized path type");
2704             }
2705             needRoom(segtype != SEG_MOVETO, npoints * 2);
2706             if (isdbl) {
2707                 while (--npoints >= 0) {
2708                     append(s.readDouble(), s.readDouble());
2709                 }
2710             } else {
2711                 while (--npoints >= 0) {
2712                     append(s.readFloat(), s.readFloat());
2713                 }
2714             }
2715             pointTypes[numTypes++] = segtype;
2716         }
2717         if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
2718             throw new StreamCorruptedException("missing PATH_END");
2719         }
2720     }
2721 
2722     abstract static class Iterator implements PathIterator {
2723         int typeIdx;
2724         int pointIdx;
2725         Path2D path;
2726 
2727         static final int[] curvecoords = {2, 2, 4, 6, 0};
2728 
2729         Iterator(Path2D path) {
2730             this.path = path;
2731         }
2732 
2733         public int getWindingRule() {
2734             return path.getWindingRule();
2735         }
2736 
2737         public boolean isDone() {
2738             return (typeIdx >= path.numTypes);
2739         }
2740 
2741         public void next() {
2742             int type = path.pointTypes[typeIdx++];
2743             pointIdx += curvecoords[type];
2744         }
2745     }
2746 }