src/java.desktop/share/classes/sun/java2d/pisces/Stroker.java
branchhttp-client-branch
changeset 55844 dbcbcda0e413
parent 55842 cb8fcde5b5c8
parent 47873 7944849362f3
child 55845 a88515bdd90a
equal deleted inserted replaced
55842:cb8fcde5b5c8 55844:dbcbcda0e413
     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 }