1 /*
   2  * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.pisces;
  27 
  28 import java.util.Arrays;
  29 import java.util.Iterator;
  30 import static java.lang.Math.ulp;
  31 import static java.lang.Math.sqrt;
  32 
  33 import sun.awt.geom.PathConsumer2D;
  34 
  35 // TODO: some of the arithmetic here is too verbose and prone to hard to
  36 // debug typos. We should consider making a small Point/Vector class that
  37 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
  38 final class Stroker implements PathConsumer2D {
  39 
  40     private static final int MOVE_TO = 0;
  41     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
  42     private static final int CLOSE = 2;
  43 
  44     /**
  45      * Constant value for join style.
  46      */
  47     public static final int JOIN_MITER = 0;
  48 
  49     /**
  50      * Constant value for join style.
  51      */
  52     public static final int JOIN_ROUND = 1;
  53 
  54     /**
  55      * Constant value for join style.
  56      */
  57     public static final int JOIN_BEVEL = 2;
  58 
  59     /**
  60      * Constant value for end cap style.
  61      */
  62     public static final int CAP_BUTT = 0;
  63 
  64     /**
  65      * Constant value for end cap style.
  66      */
  67     public static final int CAP_ROUND = 1;
  68 
  69     /**
  70      * Constant value for end cap style.
  71      */
  72     public static final int CAP_SQUARE = 2;
  73 
  74     private final PathConsumer2D out;
  75 
  76     private final int capStyle;
  77     private final int joinStyle;
  78 
  79     private final float lineWidth2;
  80 
  81     private final float[][] offset = new float[3][2];
  82     private final float[] miter = new float[2];
  83     private final float miterLimitSq;
  84 
  85     private int prev;
  86 
  87     // The starting point of the path, and the slope there.
  88     private float sx0, sy0, sdx, sdy;
  89     // the current point and the slope there.
  90     private float cx0, cy0, cdx, cdy; // c stands for current
  91     // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
  92     // first and last points on the left parallel path. Since this path is
  93     // parallel, it's slope at any point is parallel to the slope of the
  94     // original path (thought they may have different directions), so these
  95     // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
  96     // would be error prone and hard to read, so we keep these anyway.
  97     private float smx, smy, cmx, cmy;
  98 
  99     private final PolyStack reverse = new PolyStack();
 100 
 101     /**
 102      * Constructs a {@code Stroker}.
 103      *
 104      * @param pc2d an output {@code PathConsumer2D}.
 105      * @param lineWidth the desired line width in pixels
 106      * @param capStyle the desired end cap style, one of
 107      * {@code CAP_BUTT}, {@code CAP_ROUND} or
 108      * {@code CAP_SQUARE}.
 109      * @param joinStyle the desired line join style, one of
 110      * {@code JOIN_MITER}, {@code JOIN_ROUND} or
 111      * {@code JOIN_BEVEL}.
 112      * @param miterLimit the desired miter limit
 113      */
 114     public Stroker(PathConsumer2D pc2d,
 115                    float lineWidth,
 116                    int capStyle,
 117                    int joinStyle,
 118                    float miterLimit)
 119     {
 120         this.out = pc2d;
 121 
 122         this.lineWidth2 = lineWidth / 2;
 123         this.capStyle = capStyle;
 124         this.joinStyle = joinStyle;
 125 
 126         float limit = miterLimit * lineWidth2;
 127         this.miterLimitSq = limit*limit;
 128 
 129         this.prev = CLOSE;
 130     }
 131 
 132     private static void computeOffset(final float lx, final float ly,
 133                                       final float w, final float[] m)
 134     {
 135         final float len = (float) sqrt(lx*lx + ly*ly);
 136         if (len == 0) {
 137             m[0] = m[1] = 0;
 138         } else {
 139             m[0] = (ly * w)/len;
 140             m[1] = -(lx * w)/len;
 141         }
 142     }
 143 
 144     // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are
 145     // clockwise (if dx1,dy1 needs to be rotated clockwise to close
 146     // the smallest angle between it and dx2,dy2).
 147     // This is equivalent to detecting whether a point q is on the right side
 148     // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and
 149     // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
 150     // clockwise order.
 151     // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
 152     private static boolean isCW(final float dx1, final float dy1,
 153                                 final float dx2, final float dy2)
 154     {
 155         return dx1 * dy2 <= dy1 * dx2;
 156     }
 157 
 158     // pisces used to use fixed point arithmetic with 16 decimal digits. I
 159     // didn't want to change the values of the constant below when I converted
 160     // it to floating point, so that's why the divisions by 2^16 are there.
 161     private static final float ROUND_JOIN_THRESHOLD = 1000/65536f;
 162 
 163     private void drawRoundJoin(float x, float y,
 164                                float omx, float omy, float mx, float my,
 165                                boolean rev,
 166                                float threshold)
 167     {
 168         if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) {
 169             return;
 170         }
 171 
 172         float domx = omx - mx;
 173         float domy = omy - my;
 174         float len = domx*domx + domy*domy;
 175         if (len < threshold) {
 176             return;
 177         }
 178 
 179         if (rev) {
 180             omx = -omx;
 181             omy = -omy;
 182             mx = -mx;
 183             my = -my;
 184         }
 185         drawRoundJoin(x, y, omx, omy, mx, my, rev);
 186     }
 187 
 188     private void drawRoundJoin(float cx, float cy,
 189                                float omx, float omy,
 190                                float mx, float my,
 191                                boolean rev)
 192     {
 193         // The sign of the dot product of mx,my and omx,omy is equal to the
 194         // the sign of the cosine of ext
 195         // (ext is the angle between omx,omy and mx,my).
 196         final float cosext = omx * mx + omy * my;
 197         // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
 198         // need 1 curve to approximate the circle section that joins omx,omy
 199         // and mx,my.
 200         final int numCurves = (cosext >= 0f) ? 1 : 2;
 201 
 202         switch (numCurves) {
 203         case 1:
 204             drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
 205             break;
 206         case 2:
 207             // we need to split the arc into 2 arcs spanning the same angle.
 208             // The point we want will be one of the 2 intersections of the
 209             // perpendicular bisector of the chord (omx,omy)->(mx,my) and the
 210             // circle. We could find this by scaling the vector
 211             // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies
 212             // on the circle), but that can have numerical problems when the angle
 213             // between omx,omy and mx,my is close to 180 degrees. So we compute a
 214             // normal of (omx,omy)-(mx,my). This will be the direction of the
 215             // perpendicular bisector. To get one of the intersections, we just scale
 216             // this vector that its length is lineWidth2 (this works because the
 217             // perpendicular bisector goes through the origin). This scaling doesn't
 218             // have numerical problems because we know that lineWidth2 divided by
 219             // this normal's length is at least 0.5 and at most sqrt(2)/2 (because
 220             // we know the angle of the arc is > 90 degrees).
 221             float nx = my - omy, ny = omx - mx;
 222             float nlen = (float) sqrt(nx*nx + ny*ny);
 223             float scale = lineWidth2/nlen;
 224             float mmx = nx * scale, mmy = ny * scale;
 225 
 226             // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
 227             // computed the wrong intersection so we get the other one.
 228             // The test above is equivalent to if (rev).
 229             if (rev) {
 230                 mmx = -mmx;
 231                 mmy = -mmy;
 232             }
 233             drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
 234             drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
 235             break;
 236         }
 237     }
 238 
 239     // the input arc defined by omx,omy and mx,my must span <= 90 degrees.
 240     private void drawBezApproxForArc(final float cx, final float cy,
 241                                      final float omx, final float omy,
 242                                      final float mx, final float my,
 243                                      boolean rev)
 244     {
 245         final float cosext2 = (omx * mx + omy * my) / (2f * lineWidth2 * lineWidth2);
 246 
 247         // check round off errors producing cos(ext) > 1 and a NaN below
 248         // cos(ext) == 1 implies colinear segments and an empty join anyway
 249         if (cosext2 >= 0.5f) {
 250             // just return to avoid generating a flat curve:
 251             return;
 252         }
 253 
 254         // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc
 255         // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that
 256         // define the bezier curve we're computing.
 257         // It is computed using the constraints that P1-P0 and P3-P2 are parallel
 258         // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
 259         float cv = (float) ((4.0 / 3.0) * sqrt(0.5 - cosext2) /
 260                             (1.0 + sqrt(cosext2 + 0.5)));
 261         // if clockwise, we need to negate cv.
 262         if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
 263             cv = -cv;
 264         }
 265         final float x1 = cx + omx;
 266         final float y1 = cy + omy;
 267         final float x2 = x1 - cv * omy;
 268         final float y2 = y1 + cv * omx;
 269 
 270         final float x4 = cx + mx;
 271         final float y4 = cy + my;
 272         final float x3 = x4 + cv * my;
 273         final float y3 = y4 - cv * mx;
 274 
 275         emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
 276     }
 277 
 278     private void drawRoundCap(float cx, float cy, float mx, float my) {
 279         final float C = 0.5522847498307933f;
 280         // the first and second arguments of the following two calls
 281         // are really will be ignored by emitCurveTo (because of the false),
 282         // but we put them in anyway, as opposed to just giving it 4 zeroes,
 283         // because it's just 4 additions and it's not good to rely on this
 284         // sort of assumption (right now it's true, but that may change).
 285         emitCurveTo(cx+mx,      cy+my,
 286                     cx+mx-C*my, cy+my+C*mx,
 287                     cx-my+C*mx, cy+mx+C*my,
 288                     cx-my,      cy+mx,
 289                     false);
 290         emitCurveTo(cx-my,      cy+mx,
 291                     cx-my-C*mx, cy+mx-C*my,
 292                     cx-mx-C*my, cy-my+C*mx,
 293                     cx-mx,      cy-my,
 294                     false);
 295     }
 296 
 297     // Put the intersection point of the lines (x0, y0) -> (x1, y1)
 298     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1].
 299     // If the lines are parallel, it will put a non finite number in m.
 300     private void computeIntersection(final float x0, final float y0,
 301                                      final float x1, final float y1,
 302                                      final float x0p, final float y0p,
 303                                      final float x1p, final float y1p,
 304                                      final float[] m, int off)
 305     {
 306         float x10 = x1 - x0;
 307         float y10 = y1 - y0;
 308         float x10p = x1p - x0p;
 309         float y10p = y1p - y0p;
 310 
 311         float den = x10*y10p - x10p*y10;
 312         float t = x10p*(y0-y0p) - y10p*(x0-x0p);
 313         t /= den;
 314         m[off++] = x0 + t*x10;
 315         m[off] = y0 + t*y10;
 316     }
 317 
 318     private void drawMiter(final float pdx, final float pdy,
 319                            final float x0, final float y0,
 320                            final float dx, final float dy,
 321                            float omx, float omy, float mx, float my,
 322                            boolean rev)
 323     {
 324         if ((mx == omx && my == omy) ||
 325             (pdx == 0 && pdy == 0) ||
 326             (dx == 0 && dy == 0))
 327         {
 328             return;
 329         }
 330 
 331         if (rev) {
 332             omx = -omx;
 333             omy = -omy;
 334             mx = -mx;
 335             my = -my;
 336         }
 337 
 338         computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
 339                             (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
 340                             miter, 0);
 341 
 342         float lenSq = (miter[0]-x0)*(miter[0]-x0) + (miter[1]-y0)*(miter[1]-y0);
 343 
 344         // If the lines are parallel, lenSq will be either NaN or +inf
 345         // (actually, I'm not sure if the latter is possible. The important
 346         // thing is that -inf is not possible, because lenSq is a square).
 347         // For both of those values, the comparison below will fail and
 348         // no miter will be drawn, which is correct.
 349         if (lenSq < miterLimitSq) {
 350             emitLineTo(miter[0], miter[1], rev);
 351         }
 352     }
 353 
 354     public void moveTo(float x0, float y0) {
 355         if (prev == DRAWING_OP_TO) {
 356             finish();
 357         }
 358         this.sx0 = this.cx0 = x0;
 359         this.sy0 = this.cy0 = y0;
 360         this.cdx = this.sdx = 1;
 361         this.cdy = this.sdy = 0;
 362         this.prev = MOVE_TO;
 363     }
 364 
 365     public void lineTo(float x1, float y1) {
 366         float dx = x1 - cx0;
 367         float dy = y1 - cy0;
 368         if (dx == 0f && dy == 0f) {
 369             dx = 1;
 370         }
 371         computeOffset(dx, dy, lineWidth2, offset[0]);
 372         float mx = offset[0][0];
 373         float my = offset[0][1];
 374 
 375         drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
 376 
 377         emitLineTo(cx0 + mx, cy0 + my);
 378         emitLineTo(x1 + mx, y1 + my);
 379 
 380         emitLineTo(cx0 - mx, cy0 - my, true);
 381         emitLineTo(x1 - mx, y1 - my, true);
 382 
 383         this.cmx = mx;
 384         this.cmy = my;
 385         this.cdx = dx;
 386         this.cdy = dy;
 387         this.cx0 = x1;
 388         this.cy0 = y1;
 389         this.prev = DRAWING_OP_TO;
 390     }
 391 
 392     public void closePath() {
 393         if (prev != DRAWING_OP_TO) {
 394             if (prev == CLOSE) {
 395                 return;
 396             }
 397             emitMoveTo(cx0, cy0 - lineWidth2);
 398             this.cmx = this.smx = 0;
 399             this.cmy = this.smy = -lineWidth2;
 400             this.cdx = this.sdx = 1;
 401             this.cdy = this.sdy = 0;
 402             finish();
 403             return;
 404         }
 405 
 406         if (cx0 != sx0 || cy0 != sy0) {
 407             lineTo(sx0, sy0);
 408         }
 409 
 410         drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
 411 
 412         emitLineTo(sx0 + smx, sy0 + smy);
 413 
 414         emitMoveTo(sx0 - smx, sy0 - smy);
 415         emitReverse();
 416 
 417         this.prev = CLOSE;
 418         emitClose();
 419     }
 420 
 421     private void emitReverse() {
 422         while(!reverse.isEmpty()) {
 423             reverse.pop(out);
 424         }
 425     }
 426 
 427     public void pathDone() {
 428         if (prev == DRAWING_OP_TO) {
 429             finish();
 430         }
 431 
 432         out.pathDone();
 433         // this shouldn't matter since this object won't be used
 434         // after the call to this method.
 435         this.prev = CLOSE;
 436     }
 437 
 438     private void finish() {
 439         if (capStyle == CAP_ROUND) {
 440             drawRoundCap(cx0, cy0, cmx, cmy);
 441         } else if (capStyle == CAP_SQUARE) {
 442             emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
 443             emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
 444         }
 445 
 446         emitReverse();
 447 
 448         if (capStyle == CAP_ROUND) {
 449             drawRoundCap(sx0, sy0, -smx, -smy);
 450         } else if (capStyle == CAP_SQUARE) {
 451             emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
 452             emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
 453         }
 454 
 455         emitClose();
 456     }
 457 
 458     private void emitMoveTo(final float x0, final float y0) {
 459         out.moveTo(x0, y0);
 460     }
 461 
 462     private void emitLineTo(final float x1, final float y1) {
 463         out.lineTo(x1, y1);
 464     }
 465 
 466     private void emitLineTo(final float x1, final float y1,
 467                             final boolean rev)
 468     {
 469         if (rev) {
 470             reverse.pushLine(x1, y1);
 471         } else {
 472             emitLineTo(x1, y1);
 473         }
 474     }
 475 
 476     private void emitQuadTo(final float x0, final float y0,
 477                             final float x1, final float y1,
 478                             final float x2, final float y2, final boolean rev)
 479     {
 480         if (rev) {
 481             reverse.pushQuad(x0, y0, x1, y1);
 482         } else {
 483             out.quadTo(x1, y1, x2, y2);
 484         }
 485     }
 486 
 487     private void emitCurveTo(final float x0, final float y0,
 488                              final float x1, final float y1,
 489                              final float x2, final float y2,
 490                              final float x3, final float y3, final boolean rev)
 491     {
 492         if (rev) {
 493             reverse.pushCubic(x0, y0, x1, y1, x2, y2);
 494         } else {
 495             out.curveTo(x1, y1, x2, y2, x3, y3);
 496         }
 497     }
 498 
 499     private void emitClose() {
 500         out.closePath();
 501     }
 502 
 503     private void drawJoin(float pdx, float pdy,
 504                           float x0, float y0,
 505                           float dx, float dy,
 506                           float omx, float omy,
 507                           float mx, float my)
 508     {
 509         if (prev != DRAWING_OP_TO) {
 510             emitMoveTo(x0 + mx, y0 + my);
 511             this.sdx = dx;
 512             this.sdy = dy;
 513             this.smx = mx;
 514             this.smy = my;
 515         } else {
 516             boolean cw = isCW(pdx, pdy, dx, dy);
 517             if (joinStyle == JOIN_MITER) {
 518                 drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
 519             } else if (joinStyle == JOIN_ROUND) {
 520                 drawRoundJoin(x0, y0,
 521                               omx, omy,
 522                               mx, my, cw,
 523                               ROUND_JOIN_THRESHOLD);
 524             }
 525             emitLineTo(x0, y0, !cw);
 526         }
 527         prev = DRAWING_OP_TO;
 528     }
 529 
 530     private static boolean within(final float x1, final float y1,
 531                                   final float x2, final float y2,
 532                                   final float ERR)
 533     {
 534         assert ERR > 0 : "";
 535         // compare taxicab distance. ERR will always be small, so using
 536         // true distance won't give much benefit
 537         return (Helpers.within(x1, x2, ERR) &&  // we want to avoid calling Math.abs
 538                 Helpers.within(y1, y2, ERR)); // this is just as good.
 539     }
 540 
 541     private void getLineOffsets(float x1, float y1,
 542                                 float x2, float y2,
 543                                 float[] left, float[] right) {
 544         computeOffset(x2 - x1, y2 - y1, lineWidth2, offset[0]);
 545         left[0] = x1 + offset[0][0];
 546         left[1] = y1 + offset[0][1];
 547         left[2] = x2 + offset[0][0];
 548         left[3] = y2 + offset[0][1];
 549         right[0] = x1 - offset[0][0];
 550         right[1] = y1 - offset[0][1];
 551         right[2] = x2 - offset[0][0];
 552         right[3] = y2 - offset[0][1];
 553     }
 554 
 555     private int computeOffsetCubic(float[] pts, final int off,
 556                                    float[] leftOff, float[] rightOff)
 557     {
 558         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
 559         // vanishes, which creates problems with computeOffset. Usually
 560         // this happens when this stroker object is trying to winden
 561         // a curve with a cusp. What happens is that curveTo splits
 562         // the input curve at the cusp, and passes it to this function.
 563         // because of inaccuracies in the splitting, we consider points
 564         // equal if they're very close to each other.
 565         final float x1 = pts[off + 0], y1 = pts[off + 1];
 566         final float x2 = pts[off + 2], y2 = pts[off + 3];
 567         final float x3 = pts[off + 4], y3 = pts[off + 5];
 568         final float x4 = pts[off + 6], y4 = pts[off + 7];
 569 
 570         float dx4 = x4 - x3;
 571         float dy4 = y4 - y3;
 572         float dx1 = x2 - x1;
 573         float dy1 = y2 - y1;
 574 
 575         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
 576         // in which case ignore if p1 == p2
 577         final boolean p1eqp2 = within(x1,y1,x2,y2, 6 * ulp(y2));
 578         final boolean p3eqp4 = within(x3,y3,x4,y4, 6 * ulp(y4));
 579         if (p1eqp2 && p3eqp4) {
 580             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
 581             return 4;
 582         } else if (p1eqp2) {
 583             dx1 = x3 - x1;
 584             dy1 = y3 - y1;
 585         } else if (p3eqp4) {
 586             dx4 = x4 - x2;
 587             dy4 = y4 - y2;
 588         }
 589 
 590         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
 591         float dotsq = (dx1 * dx4 + dy1 * dy4);
 592         dotsq = dotsq * dotsq;
 593         float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
 594         if (Helpers.within(dotsq, l1sq * l4sq, 4 * ulp(dotsq))) {
 595             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
 596             return 4;
 597         }
 598 
 599 //      What we're trying to do in this function is to approximate an ideal
 600 //      offset curve (call it I) of the input curve B using a bezier curve Bp.
 601 //      The constraints I use to get the equations are:
 602 //
 603 //      1. The computed curve Bp should go through I(0) and I(1). These are
 604 //      x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find
 605 //      4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p).
 606 //
 607 //      2. Bp should have slope equal in absolute value to I at the endpoints. So,
 608 //      (by the way, the operator || in the comments below means "aligned with".
 609 //      It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that
 610 //      vectors I'(0) and Bp'(0) are aligned, which is the same as saying
 611 //      that the tangent lines of I and Bp at 0 are parallel. Mathematically
 612 //      this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some
 613 //      nonzero constant.)
 614 //      I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and
 615 //      I'(1) || B'(1); therefore, Bp'(0) || B'(0) and Bp'(1) || B'(1).
 616 //      We know that Bp'(0) || (p2p-p1p) and Bp'(1) || (p4p-p3p) and the same
 617 //      is true for any bezier curve; therefore, we get the equations
 618 //          (1) p2p = c1 * (p2-p1) + p1p
 619 //          (2) p3p = c2 * (p4-p3) + p4p
 620 //      We know p1p, p4p, p2, p1, p3, and p4; therefore, this reduces the number
 621 //      of unknowns from 4 to 2 (i.e. just c1 and c2).
 622 //      To eliminate these 2 unknowns we use the following constraint:
 623 //
 624 //      3. Bp(0.5) == I(0.5). Bp(0.5)=(x,y) and I(0.5)=(xi,yi), and I should note
 625 //      that I(0.5) is *the only* reason for computing dxm,dym. This gives us
 626 //          (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to
 627 //          (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3
 628 //      We can substitute (1) and (2) from above into (4) and we get:
 629 //          (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p
 630 //      which is equivalent to
 631 //          (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p)
 632 //
 633 //      The right side of this is a 2D vector, and we know I(0.5), which gives us
 634 //      Bp(0.5), which gives us the value of the right side.
 635 //      The left side is just a matrix vector multiplication in disguise. It is
 636 //
 637 //      [x2-x1, x4-x3][c1]
 638 //      [y2-y1, y4-y3][c2]
 639 //      which, is equal to
 640 //      [dx1, dx4][c1]
 641 //      [dy1, dy4][c2]
 642 //      At this point we are left with a simple linear system and we solve it by
 643 //      getting the inverse of the matrix above. Then we use [c1,c2] to compute
 644 //      p2p and p3p.
 645 
 646         float x = 0.125f * (x1 + 3 * (x2 + x3) + x4);
 647         float y = 0.125f * (y1 + 3 * (y2 + y3) + y4);
 648         // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
 649         // c*B'(0.5) for some constant c.
 650         float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
 651 
 652         // this computes the offsets at t=0, 0.5, 1, using the property that
 653         // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
 654         // the (dx/dt, dy/dt) vectors at the endpoints.
 655         computeOffset(dx1, dy1, lineWidth2, offset[0]);
 656         computeOffset(dxm, dym, lineWidth2, offset[1]);
 657         computeOffset(dx4, dy4, lineWidth2, offset[2]);
 658         float x1p = x1 + offset[0][0]; // start
 659         float y1p = y1 + offset[0][1]; // point
 660         float xi  = x + offset[1][0]; // interpolation
 661         float yi  = y + offset[1][1]; // point
 662         float x4p = x4 + offset[2][0]; // end
 663         float y4p = y4 + offset[2][1]; // point
 664 
 665         float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4));
 666 
 667         float two_pi_m_p1_m_p4x = 2*xi - x1p - x4p;
 668         float two_pi_m_p1_m_p4y = 2*yi - y1p - y4p;
 669         float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
 670         float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 671 
 672         float x2p, y2p, x3p, y3p;
 673         x2p = x1p + c1*dx1;
 674         y2p = y1p + c1*dy1;
 675         x3p = x4p + c2*dx4;
 676         y3p = y4p + c2*dy4;
 677 
 678         leftOff[0] = x1p; leftOff[1] = y1p;
 679         leftOff[2] = x2p; leftOff[3] = y2p;
 680         leftOff[4] = x3p; leftOff[5] = y3p;
 681         leftOff[6] = x4p; leftOff[7] = y4p;
 682 
 683         x1p = x1 - offset[0][0]; y1p = y1 - offset[0][1];
 684         xi = xi - 2 * offset[1][0]; yi = yi - 2 * offset[1][1];
 685         x4p = x4 - offset[2][0]; y4p = y4 - offset[2][1];
 686 
 687         two_pi_m_p1_m_p4x = 2*xi - x1p - x4p;
 688         two_pi_m_p1_m_p4y = 2*yi - y1p - y4p;
 689         c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
 690         c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 691 
 692         x2p = x1p + c1*dx1;
 693         y2p = y1p + c1*dy1;
 694         x3p = x4p + c2*dx4;
 695         y3p = y4p + c2*dy4;
 696 
 697         rightOff[0] = x1p; rightOff[1] = y1p;
 698         rightOff[2] = x2p; rightOff[3] = y2p;
 699         rightOff[4] = x3p; rightOff[5] = y3p;
 700         rightOff[6] = x4p; rightOff[7] = y4p;
 701         return 8;
 702     }
 703 
 704     // return the kind of curve in the right and left arrays.
 705     private int computeOffsetQuad(float[] pts, final int off,
 706                                   float[] leftOff, float[] rightOff)
 707     {
 708         final float x1 = pts[off + 0], y1 = pts[off + 1];
 709         final float x2 = pts[off + 2], y2 = pts[off + 3];
 710         final float x3 = pts[off + 4], y3 = pts[off + 5];
 711 
 712         final float dx3 = x3 - x2;
 713         final float dy3 = y3 - y2;
 714         final float dx1 = x2 - x1;
 715         final float dy1 = y2 - y1;
 716 
 717         // this computes the offsets at t = 0, 1
 718         computeOffset(dx1, dy1, lineWidth2, offset[0]);
 719         computeOffset(dx3, dy3, lineWidth2, offset[1]);
 720 
 721         leftOff[0]  = x1 + offset[0][0];  leftOff[1] = y1 + offset[0][1];
 722         leftOff[4]  = x3 + offset[1][0];  leftOff[5] = y3 + offset[1][1];
 723         rightOff[0] = x1 - offset[0][0]; rightOff[1] = y1 - offset[0][1];
 724         rightOff[4] = x3 - offset[1][0]; rightOff[5] = y3 - offset[1][1];
 725 
 726         float x1p = leftOff[0]; // start
 727         float y1p = leftOff[1]; // point
 728         float x3p = leftOff[4]; // end
 729         float y3p = leftOff[5]; // point
 730 
 731         // Corner cases:
 732         // 1. If the two control vectors are parallel, we'll end up with NaN's
 733         //    in leftOff (and rightOff in the body of the if below), so we'll
 734         //    do getLineOffsets, which is right.
 735         // 2. If the first or second two points are equal, then (dx1,dy1)==(0,0)
 736         //    or (dx3,dy3)==(0,0), so (x1p, y1p)==(x1p+dx1, y1p+dy1)
 737         //    or (x3p, y3p)==(x3p-dx3, y3p-dy3), which means that
 738         //    computeIntersection will put NaN's in leftOff and right off, and
 739         //    we will do getLineOffsets, which is right.
 740         computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
 741         float cx = leftOff[2];
 742         float cy = leftOff[3];
 743 
 744         if (!(isFinite(cx) && isFinite(cy))) {
 745             // maybe the right path is not degenerate.
 746             x1p = rightOff[0];
 747             y1p = rightOff[1];
 748             x3p = rightOff[4];
 749             y3p = rightOff[5];
 750             computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
 751             cx = rightOff[2];
 752             cy = rightOff[3];
 753             if (!(isFinite(cx) && isFinite(cy))) {
 754                 // both are degenerate. This curve is a line.
 755                 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
 756                 return 4;
 757             }
 758             // {left,right}Off[0,1,4,5] are already set to the correct values.
 759             leftOff[2] = 2*x2 - cx;
 760             leftOff[3] = 2*y2 - cy;
 761             return 6;
 762         }
 763 
 764         // rightOff[2,3] = (x2,y2) - ((left_x2, left_y2) - (x2, y2))
 765         // == 2*(x2, y2) - (left_x2, left_y2)
 766         rightOff[2] = 2*x2 - cx;
 767         rightOff[3] = 2*y2 - cy;
 768         return 6;
 769     }
 770 
 771     private static boolean isFinite(float x) {
 772         return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY);
 773     }
 774 
 775     // This is where the curve to be processed is put. We give it
 776     // enough room to store 2 curves: one for the current subdivision, the
 777     // other for the rest of the curve.
 778     private float[] middle = new float[2*8];
 779     private float[] lp = new float[8];
 780     private float[] rp = new float[8];
 781     private static final int MAX_N_CURVES = 11;
 782     private float[] subdivTs = new float[MAX_N_CURVES - 1];
 783 
 784     // If this class is compiled with ecj, then Hotspot crashes when OSR
 785     // compiling this function. See bugs 7004570 and 6675699
 786     // TODO: until those are fixed, we should work around that by
 787     // manually inlining this into curveTo and quadTo.
 788 /******************************* WORKAROUND **********************************
 789     private void somethingTo(final int type) {
 790         // need these so we can update the state at the end of this method
 791         final float xf = middle[type-2], yf = middle[type-1];
 792         float dxs = middle[2] - middle[0];
 793         float dys = middle[3] - middle[1];
 794         float dxf = middle[type - 2] - middle[type - 4];
 795         float dyf = middle[type - 1] - middle[type - 3];
 796         switch(type) {
 797         case 6:
 798             if ((dxs == 0f && dys == 0f) ||
 799                 (dxf == 0f && dyf == 0f)) {
 800                dxs = dxf = middle[4] - middle[0];
 801                dys = dyf = middle[5] - middle[1];
 802             }
 803             break;
 804         case 8:
 805             boolean p1eqp2 = (dxs == 0f && dys == 0f);
 806             boolean p3eqp4 = (dxf == 0f && dyf == 0f);
 807             if (p1eqp2) {
 808                 dxs = middle[4] - middle[0];
 809                 dys = middle[5] - middle[1];
 810                 if (dxs == 0f && dys == 0f) {
 811                     dxs = middle[6] - middle[0];
 812                     dys = middle[7] - middle[1];
 813                 }
 814             }
 815             if (p3eqp4) {
 816                 dxf = middle[6] - middle[2];
 817                 dyf = middle[7] - middle[3];
 818                 if (dxf == 0f && dyf == 0f) {
 819                     dxf = middle[6] - middle[0];
 820                     dyf = middle[7] - middle[1];
 821                 }
 822             }
 823         }
 824         if (dxs == 0f && dys == 0f) {
 825             // this happens iff the "curve" is just a point
 826             lineTo(middle[0], middle[1]);
 827             return;
 828         }
 829         // if these vectors are too small, normalize them, to avoid future
 830         // precision problems.
 831         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
 832             float len = (float) sqrt(dxs*dxs + dys*dys);
 833             dxs /= len;
 834             dys /= len;
 835         }
 836         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
 837             float len = (float) sqrt(dxf*dxf + dyf*dyf);
 838             dxf /= len;
 839             dyf /= len;
 840         }
 841 
 842         computeOffset(dxs, dys, lineWidth2, offset[0]);
 843         final float mx = offset[0][0];
 844         final float my = offset[0][1];
 845         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
 846 
 847         int nSplits = findSubdivPoints(middle, subdivTs, type, lineWidth2);
 848 
 849         int kind = 0;
 850         Iterator<Integer> it = Curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
 851         while(it.hasNext()) {
 852             int curCurveOff = it.next();
 853 
 854             switch (type) {
 855             case 8:
 856                 kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
 857                 break;
 858             case 6:
 859                 kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
 860                 break;
 861             }
 862             emitLineTo(lp[0], lp[1]);
 863             switch(kind) {
 864             case 8:
 865                 emitCurveTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], lp[6], lp[7], false);
 866                 emitCurveTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7], true);
 867                 break;
 868             case 6:
 869                 emitQuadTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], false);
 870                 emitQuadTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], true);
 871                 break;
 872             case 4:
 873                 emitLineTo(lp[2], lp[3]);
 874                 emitLineTo(rp[0], rp[1], true);
 875                 break;
 876             }
 877             emitLineTo(rp[kind - 2], rp[kind - 1], true);
 878         }
 879 
 880         this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
 881         this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
 882         this.cdx = dxf;
 883         this.cdy = dyf;
 884         this.cx0 = xf;
 885         this.cy0 = yf;
 886         this.prev = DRAWING_OP_TO;
 887     }
 888 ****************************** END WORKAROUND *******************************/
 889 
 890     // finds values of t where the curve in pts should be subdivided in order
 891     // to get good offset curves a distance of w away from the middle curve.
 892     // Stores the points in ts, and returns how many of them there were.
 893     private static Curve c = new Curve();
 894     private static int findSubdivPoints(float[] pts, float[] ts, final int type, final float w)
 895     {
 896         final float x12 = pts[2] - pts[0];
 897         final float y12 = pts[3] - pts[1];
 898         // if the curve is already parallel to either axis we gain nothing
 899         // from rotating it.
 900         if (y12 != 0f && x12 != 0f) {
 901             // we rotate it so that the first vector in the control polygon is
 902             // parallel to the x-axis. This will ensure that rotated quarter
 903             // circles won't be subdivided.
 904             final float hypot = (float) sqrt(x12 * x12 + y12 * y12);
 905             final float cos = x12 / hypot;
 906             final float sin = y12 / hypot;
 907             final float x1 = cos * pts[0] + sin * pts[1];
 908             final float y1 = cos * pts[1] - sin * pts[0];
 909             final float x2 = cos * pts[2] + sin * pts[3];
 910             final float y2 = cos * pts[3] - sin * pts[2];
 911             final float x3 = cos * pts[4] + sin * pts[5];
 912             final float y3 = cos * pts[5] - sin * pts[4];
 913             switch(type) {
 914             case 8:
 915                 final float x4 = cos * pts[6] + sin * pts[7];
 916                 final float y4 = cos * pts[7] - sin * pts[6];
 917                 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
 918                 break;
 919             case 6:
 920                 c.set(x1, y1, x2, y2, x3, y3);
 921                 break;
 922             }
 923         } else {
 924             c.set(pts, type);
 925         }
 926 
 927         int ret = 0;
 928         // we subdivide at values of t such that the remaining rotated
 929         // curves are monotonic in x and y.
 930         ret += c.dxRoots(ts, ret);
 931         ret += c.dyRoots(ts, ret);
 932         // subdivide at inflection points.
 933         if (type == 8) {
 934             // quadratic curves can't have inflection points
 935             ret += c.infPoints(ts, ret);
 936         }
 937 
 938         // now we must subdivide at points where one of the offset curves will have
 939         // a cusp. This happens at ts where the radius of curvature is equal to w.
 940         ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
 941 
 942         ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
 943         Helpers.isort(ts, 0, ret);
 944         return ret;
 945     }
 946 
 947     @Override public void curveTo(float x1, float y1,
 948                                   float x2, float y2,
 949                                   float x3, float y3)
 950     {
 951         middle[0] = cx0; middle[1] = cy0;
 952         middle[2] = x1;  middle[3] = y1;
 953         middle[4] = x2;  middle[5] = y2;
 954         middle[6] = x3;  middle[7] = y3;
 955 
 956         // inlined version of somethingTo(8);
 957         // See the TODO on somethingTo
 958 
 959         // need these so we can update the state at the end of this method
 960         final float xf = middle[6], yf = middle[7];
 961         float dxs = middle[2] - middle[0];
 962         float dys = middle[3] - middle[1];
 963         float dxf = middle[6] - middle[4];
 964         float dyf = middle[7] - middle[5];
 965 
 966         boolean p1eqp2 = (dxs == 0f && dys == 0f);
 967         boolean p3eqp4 = (dxf == 0f && dyf == 0f);
 968         if (p1eqp2) {
 969             dxs = middle[4] - middle[0];
 970             dys = middle[5] - middle[1];
 971             if (dxs == 0f && dys == 0f) {
 972                 dxs = middle[6] - middle[0];
 973                 dys = middle[7] - middle[1];
 974             }
 975         }
 976         if (p3eqp4) {
 977             dxf = middle[6] - middle[2];
 978             dyf = middle[7] - middle[3];
 979             if (dxf == 0f && dyf == 0f) {
 980                 dxf = middle[6] - middle[0];
 981                 dyf = middle[7] - middle[1];
 982             }
 983         }
 984         if (dxs == 0f && dys == 0f) {
 985             // this happens iff the "curve" is just a point
 986             lineTo(middle[0], middle[1]);
 987             return;
 988         }
 989 
 990         // if these vectors are too small, normalize them, to avoid future
 991         // precision problems.
 992         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
 993             float len = (float) sqrt(dxs*dxs + dys*dys);
 994             dxs /= len;
 995             dys /= len;
 996         }
 997         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
 998             float len = (float) sqrt(dxf*dxf + dyf*dyf);
 999             dxf /= len;
1000             dyf /= len;
1001         }
1002 
1003         computeOffset(dxs, dys, lineWidth2, offset[0]);
1004         final float mx = offset[0][0];
1005         final float my = offset[0][1];
1006         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
1007 
1008         int nSplits = findSubdivPoints(middle, subdivTs, 8, lineWidth2);
1009 
1010         int kind = 0;
1011         Iterator<Integer> it = Curve.breakPtsAtTs(middle, 8, subdivTs, nSplits);
1012         while(it.hasNext()) {
1013             int curCurveOff = it.next();
1014 
1015             kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
1016             emitLineTo(lp[0], lp[1]);
1017             switch(kind) {
1018             case 8:
1019                 emitCurveTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], lp[6], lp[7], false);
1020                 emitCurveTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7], true);
1021                 break;
1022             case 4:
1023                 emitLineTo(lp[2], lp[3]);
1024                 emitLineTo(rp[0], rp[1], true);
1025                 break;
1026             }
1027             emitLineTo(rp[kind - 2], rp[kind - 1], true);
1028         }
1029 
1030         this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
1031         this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
1032         this.cdx = dxf;
1033         this.cdy = dyf;
1034         this.cx0 = xf;
1035         this.cy0 = yf;
1036         this.prev = DRAWING_OP_TO;
1037     }
1038 
1039     @Override public void quadTo(float x1, float y1, float x2, float y2) {
1040         middle[0] = cx0; middle[1] = cy0;
1041         middle[2] = x1;  middle[3] = y1;
1042         middle[4] = x2;  middle[5] = y2;
1043 
1044         // inlined version of somethingTo(8);
1045         // See the TODO on somethingTo
1046 
1047         // need these so we can update the state at the end of this method
1048         final float xf = middle[4], yf = middle[5];
1049         float dxs = middle[2] - middle[0];
1050         float dys = middle[3] - middle[1];
1051         float dxf = middle[4] - middle[2];
1052         float dyf = middle[5] - middle[3];
1053         if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
1054             dxs = dxf = middle[4] - middle[0];
1055             dys = dyf = middle[5] - middle[1];
1056         }
1057         if (dxs == 0f && dys == 0f) {
1058             // this happens iff the "curve" is just a point
1059             lineTo(middle[0], middle[1]);
1060             return;
1061         }
1062         // if these vectors are too small, normalize them, to avoid future
1063         // precision problems.
1064         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
1065             float len = (float) sqrt(dxs*dxs + dys*dys);
1066             dxs /= len;
1067             dys /= len;
1068         }
1069         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
1070             float len = (float) sqrt(dxf*dxf + dyf*dyf);
1071             dxf /= len;
1072             dyf /= len;
1073         }
1074 
1075         computeOffset(dxs, dys, lineWidth2, offset[0]);
1076         final float mx = offset[0][0];
1077         final float my = offset[0][1];
1078         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
1079 
1080         int nSplits = findSubdivPoints(middle, subdivTs, 6, lineWidth2);
1081 
1082         int kind = 0;
1083         Iterator<Integer> it = Curve.breakPtsAtTs(middle, 6, subdivTs, nSplits);
1084         while(it.hasNext()) {
1085             int curCurveOff = it.next();
1086 
1087             kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
1088             emitLineTo(lp[0], lp[1]);
1089             switch(kind) {
1090             case 6:
1091                 emitQuadTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], false);
1092                 emitQuadTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], true);
1093                 break;
1094             case 4:
1095                 emitLineTo(lp[2], lp[3]);
1096                 emitLineTo(rp[0], rp[1], true);
1097                 break;
1098             }
1099             emitLineTo(rp[kind - 2], rp[kind - 1], true);
1100         }
1101 
1102         this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
1103         this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
1104         this.cdx = dxf;
1105         this.cdy = dyf;
1106         this.cx0 = xf;
1107         this.cy0 = yf;
1108         this.prev = DRAWING_OP_TO;
1109     }
1110 
1111     @Override public long getNativeConsumer() {
1112         throw new InternalError("Stroker doesn't use a native consumer");
1113     }
1114 
1115     // a stack of polynomial curves where each curve shares endpoints with
1116     // adjacent ones.
1117     private static final class PolyStack {
1118         float[] curves;
1119         int end;
1120         int[] curveTypes;
1121         int numCurves;
1122 
1123         private static final int INIT_SIZE = 50;
1124 
1125         PolyStack() {
1126             curves = new float[8 * INIT_SIZE];
1127             curveTypes = new int[INIT_SIZE];
1128             end = 0;
1129             numCurves = 0;
1130         }
1131 
1132         public boolean isEmpty() {
1133             return numCurves == 0;
1134         }
1135 
1136         private void ensureSpace(int n) {
1137             if (end + n >= curves.length) {
1138                 int newSize = (end + n) * 2;
1139                 curves = Arrays.copyOf(curves, newSize);
1140             }
1141             if (numCurves >= curveTypes.length) {
1142                 int newSize = numCurves * 2;
1143                 curveTypes = Arrays.copyOf(curveTypes, newSize);
1144             }
1145         }
1146 
1147         public void pushCubic(float x0, float y0,
1148                               float x1, float y1,
1149                               float x2, float y2)
1150         {
1151             ensureSpace(6);
1152             curveTypes[numCurves++] = 8;
1153             // assert(x0 == lastX && y0 == lastY)
1154 
1155             // we reverse the coordinate order to make popping easier
1156             curves[end++] = x2;    curves[end++] = y2;
1157             curves[end++] = x1;    curves[end++] = y1;
1158             curves[end++] = x0;    curves[end++] = y0;
1159         }
1160 
1161         public void pushQuad(float x0, float y0,
1162                              float x1, float y1)
1163         {
1164             ensureSpace(4);
1165             curveTypes[numCurves++] = 6;
1166             // assert(x0 == lastX && y0 == lastY)
1167             curves[end++] = x1;    curves[end++] = y1;
1168             curves[end++] = x0;    curves[end++] = y0;
1169         }
1170 
1171         public void pushLine(float x, float y) {
1172             ensureSpace(2);
1173             curveTypes[numCurves++] = 4;
1174             // assert(x0 == lastX && y0 == lastY)
1175             curves[end++] = x;    curves[end++] = y;
1176         }
1177 
1178         @SuppressWarnings("unused")
1179         public int pop(float[] pts) {
1180             int ret = curveTypes[numCurves - 1];
1181             numCurves--;
1182             end -= (ret - 2);
1183             System.arraycopy(curves, end, pts, 0, ret - 2);
1184             return ret;
1185         }
1186 
1187         public void pop(PathConsumer2D io) {
1188             numCurves--;
1189             int type = curveTypes[numCurves];
1190             end -= (type - 2);
1191             switch(type) {
1192             case 8:
1193                 io.curveTo(curves[end+0], curves[end+1],
1194                            curves[end+2], curves[end+3],
1195                            curves[end+4], curves[end+5]);
1196                 break;
1197             case 6:
1198                 io.quadTo(curves[end+0], curves[end+1],
1199                            curves[end+2], curves[end+3]);
1200                  break;
1201             case 4:
1202                 io.lineTo(curves[end], curves[end+1]);
1203             }
1204         }
1205 
1206         @Override
1207         public String toString() {
1208             String ret = "";
1209             int nc = numCurves;
1210             int end = this.end;
1211             while (nc > 0) {
1212                 nc--;
1213                 int type = curveTypes[numCurves];
1214                 end -= (type - 2);
1215                 switch(type) {
1216                 case 8:
1217                     ret += "cubic: ";
1218                     break;
1219                 case 6:
1220                     ret += "quad: ";
1221                     break;
1222                 case 4:
1223                     ret += "line: ";
1224                     break;
1225                 }
1226                 ret += Arrays.toString(Arrays.copyOfRange(curves, end, end+type-2)) + "\n";
1227             }
1228             return ret;
1229         }
1230     }
1231 }