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