< prev index next >

src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java

Print this page


   1 /*
   2  * Copyright (c) 2007, 2016, 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.marlin;
  27 
  28 import java.util.Arrays;
  29 import static java.lang.Math.ulp;
  30 import static java.lang.Math.sqrt;
  31 
  32 import sun.awt.geom.PathConsumer2D;
  33 import sun.java2d.marlin.Curve.BreakPtrIterator;
  34 
  35 
  36 // TODO: some of the arithmetic here is too verbose and prone to hard to
  37 // debug typos. We should consider making a small Point/Vector class that
  38 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
  39 final class Stroker implements PathConsumer2D, MarlinConst {
  40 
  41     private static final int MOVE_TO = 0;
  42     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
  43     private static final int CLOSE = 2;
  44 
  45     /**
  46      * Constant value for join style.
  47      */
  48     public static final int JOIN_MITER = 0;
  49 
  50     /**
  51      * Constant value for join style.
  52      */
  53     public static final int JOIN_ROUND = 1;
  54 


  58     public static final int JOIN_BEVEL = 2;
  59 
  60     /**
  61      * Constant value for end cap style.
  62      */
  63     public static final int CAP_BUTT = 0;
  64 
  65     /**
  66      * Constant value for end cap style.
  67      */
  68     public static final int CAP_ROUND = 1;
  69 
  70     /**
  71      * Constant value for end cap style.
  72      */
  73     public static final int CAP_SQUARE = 2;
  74 
  75     // pisces used to use fixed point arithmetic with 16 decimal digits. I
  76     // didn't want to change the values of the constant below when I converted
  77     // it to floating point, so that's why the divisions by 2^16 are there.
  78     private static final float ROUND_JOIN_THRESHOLD = 1000/65536f;
  79 
  80     private static final float C = 0.5522847498307933f;
  81 
  82     private static final int MAX_N_CURVES = 11;
  83 
  84     private PathConsumer2D out;
  85 
  86     private int capStyle;
  87     private int joinStyle;
  88 
  89     private float lineWidth2;
  90     private float invHalfLineWidth2Sq;
  91 
  92     private final float[] offset0 = new float[2];
  93     private final float[] offset1 = new float[2];
  94     private final float[] offset2 = new float[2];
  95     private final float[] miter = new float[2];
  96     private float miterLimitSq;
  97 
  98     private int prev;
  99 
 100     // The starting point of the path, and the slope there.
 101     private float sx0, sy0, sdx, sdy;
 102     // the current point and the slope there.
 103     private float cx0, cy0, cdx, cdy; // c stands for current
 104     // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
 105     // first and last points on the left parallel path. Since this path is
 106     // parallel, it's slope at any point is parallel to the slope of the
 107     // original path (thought they may have different directions), so these
 108     // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
 109     // would be error prone and hard to read, so we keep these anyway.
 110     private float smx, smy, cmx, cmy;
 111 
 112     private final PolyStack reverse;
 113 
 114     // This is where the curve to be processed is put. We give it
 115     // enough room to store 2 curves: one for the current subdivision, the
 116     // other for the rest of the curve.
 117     private final float[] middle = new float[2 * 8];
 118     private final float[] lp = new float[8];
 119     private final float[] rp = new float[8];
 120     private final float[] subdivTs = new float[MAX_N_CURVES - 1];
 121 
 122     // per-thread renderer context
 123     final RendererContext rdrCtx;
 124 
 125     // dirty curve
 126     final Curve curve;
 127 
 128     /**
 129      * Constructs a <code>Stroker</code>.
 130      * @param rdrCtx per-thread renderer context
 131      */
 132     Stroker(final RendererContext rdrCtx) {
 133         this.rdrCtx = rdrCtx;
 134 
 135         this.reverse = new PolyStack(rdrCtx);
 136         this.curve = rdrCtx.curve;
 137     }


 141      *
 142      * @param pc2d an output <code>PathConsumer2D</code>.
 143      * @param lineWidth the desired line width in pixels
 144      * @param capStyle the desired end cap style, one of
 145      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
 146      * <code>CAP_SQUARE</code>.
 147      * @param joinStyle the desired line join style, one of
 148      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
 149      * <code>JOIN_BEVEL</code>.
 150      * @param miterLimit the desired miter limit
 151      * @return this instance
 152      */
 153     Stroker init(PathConsumer2D pc2d,
 154               float lineWidth,
 155               int capStyle,
 156               int joinStyle,
 157               float miterLimit)
 158     {
 159         this.out = pc2d;
 160 
 161         this.lineWidth2 = lineWidth / 2f;
 162         this.invHalfLineWidth2Sq = 1f / (2f * lineWidth2 * lineWidth2);
 163         this.capStyle = capStyle;
 164         this.joinStyle = joinStyle;
 165 
 166         float limit = miterLimit * lineWidth2;
 167         this.miterLimitSq = limit * limit;
 168 
 169         this.prev = CLOSE;
 170 
 171         rdrCtx.stroking = 1;
 172 
 173         return this; // fluent API
 174     }
 175 
 176     /**
 177      * Disposes this stroker:
 178      * clean up before reusing this instance
 179      */
 180     void dispose() {
 181         reverse.dispose();
 182 
 183         if (DO_CLEAN_DIRTY) {
 184             // Force zero-fill dirty arrays:
 185             Arrays.fill(offset0, 0f);
 186             Arrays.fill(offset1, 0f);
 187             Arrays.fill(offset2, 0f);
 188             Arrays.fill(miter, 0f);
 189             Arrays.fill(middle, 0f);
 190             Arrays.fill(lp, 0f);
 191             Arrays.fill(rp, 0f);
 192             Arrays.fill(subdivTs, 0f);
 193         }
 194     }
 195 
 196     private static void computeOffset(final float lx, final float ly,
 197                                       final float w, final float[] m)
 198     {
 199         float len = lx*lx + ly*ly;
 200         if (len == 0f) {
 201             m[0] = 0f;
 202             m[1] = 0f;
 203         } else {
 204             len = (float) sqrt(len);
 205             m[0] =  (ly * w) / len;
 206             m[1] = -(lx * w) / len;
 207         }
 208     }
 209 
 210     // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are
 211     // clockwise (if dx1,dy1 needs to be rotated clockwise to close
 212     // the smallest angle between it and dx2,dy2).
 213     // This is equivalent to detecting whether a point q is on the right side
 214     // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and
 215     // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
 216     // clockwise order.
 217     // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
 218     private static boolean isCW(final float dx1, final float dy1,
 219                                 final float dx2, final float dy2)
 220     {
 221         return dx1 * dy2 <= dy1 * dx2;
 222     }
 223 
 224     private void drawRoundJoin(float x, float y,
 225                                float omx, float omy, float mx, float my,
 226                                boolean rev,
 227                                float threshold)
 228     {
 229         if ((omx == 0f && omy == 0f) || (mx == 0f && my == 0f)) {
 230             return;
 231         }
 232 
 233         float domx = omx - mx;
 234         float domy = omy - my;
 235         float len = domx*domx + domy*domy;
 236         if (len < threshold) {
 237             return;
 238         }
 239 
 240         if (rev) {
 241             omx = -omx;
 242             omy = -omy;
 243             mx  = -mx;
 244             my  = -my;
 245         }
 246         drawRoundJoin(x, y, omx, omy, mx, my, rev);
 247     }
 248 
 249     private void drawRoundJoin(float cx, float cy,
 250                                float omx, float omy,
 251                                float mx, float my,
 252                                boolean rev)
 253     {
 254         // The sign of the dot product of mx,my and omx,omy is equal to the
 255         // the sign of the cosine of ext
 256         // (ext is the angle between omx,omy and mx,my).
 257         final float cosext = omx * mx + omy * my;
 258         // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
 259         // need 1 curve to approximate the circle section that joins omx,omy
 260         // and mx,my.
 261         final int numCurves = (cosext >= 0f) ? 1 : 2;
 262 
 263         switch (numCurves) {
 264         case 1:
 265             drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
 266             break;
 267         case 2:
 268             // we need to split the arc into 2 arcs spanning the same angle.
 269             // The point we want will be one of the 2 intersections of the
 270             // perpendicular bisector of the chord (omx,omy)->(mx,my) and the
 271             // circle. We could find this by scaling the vector
 272             // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies
 273             // on the circle), but that can have numerical problems when the angle
 274             // between omx,omy and mx,my is close to 180 degrees. So we compute a
 275             // normal of (omx,omy)-(mx,my). This will be the direction of the
 276             // perpendicular bisector. To get one of the intersections, we just scale
 277             // this vector that its length is lineWidth2 (this works because the
 278             // perpendicular bisector goes through the origin). This scaling doesn't
 279             // have numerical problems because we know that lineWidth2 divided by
 280             // this normal's length is at least 0.5 and at most sqrt(2)/2 (because
 281             // we know the angle of the arc is > 90 degrees).
 282             float nx = my - omy, ny = omx - mx;
 283             float nlen = (float) sqrt(nx*nx + ny*ny);
 284             float scale = lineWidth2/nlen;
 285             float mmx = nx * scale, mmy = ny * scale;
 286 
 287             // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
 288             // computed the wrong intersection so we get the other one.
 289             // The test above is equivalent to if (rev).
 290             if (rev) {
 291                 mmx = -mmx;
 292                 mmy = -mmy;
 293             }
 294             drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
 295             drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
 296             break;
 297         default:
 298         }
 299     }
 300 
 301     // the input arc defined by omx,omy and mx,my must span <= 90 degrees.
 302     private void drawBezApproxForArc(final float cx, final float cy,
 303                                      final float omx, final float omy,
 304                                      final float mx, final float my,
 305                                      boolean rev)
 306     {
 307         final float cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq;
 308 
 309         // check round off errors producing cos(ext) > 1 and a NaN below
 310         // cos(ext) == 1 implies colinear segments and an empty join anyway
 311         if (cosext2 >= 0.5f) {
 312             // just return to avoid generating a flat curve:
 313             return;
 314         }
 315 
 316         // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc
 317         // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that
 318         // define the bezier curve we're computing.
 319         // It is computed using the constraints that P1-P0 and P3-P2 are parallel
 320         // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
 321         float cv = (float) ((4.0 / 3.0) * sqrt(0.5 - cosext2) /
 322                             (1.0 + sqrt(cosext2 + 0.5)));
 323         // if clockwise, we need to negate cv.
 324         if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
 325             cv = -cv;
 326         }
 327         final float x1 = cx + omx;
 328         final float y1 = cy + omy;
 329         final float x2 = x1 - cv * omy;
 330         final float y2 = y1 + cv * omx;
 331 
 332         final float x4 = cx + mx;
 333         final float y4 = cy + my;
 334         final float x3 = x4 + cv * my;
 335         final float y3 = y4 - cv * mx;
 336 
 337         emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
 338     }
 339 
 340     private void drawRoundCap(float cx, float cy, float mx, float my) {
 341         final float Cmx = C * mx;
 342         final float Cmy = C * my;
 343         emitCurveTo(cx + mx - Cmy, cy + my + Cmx,
 344                     cx - my + Cmx, cy + mx + Cmy,
 345                     cx - my,       cy + mx);
 346         emitCurveTo(cx - my - Cmx, cy + mx - Cmy,
 347                     cx - mx - Cmy, cy - my + Cmx,
 348                     cx - mx,       cy - my);
 349     }
 350 
 351     // Put the intersection point of the lines (x0, y0) -> (x1, y1)
 352     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1].
 353     // If the lines are parallel, it will put a non finite number in m.
 354     private static void computeIntersection(final float x0, final float y0,
 355                                             final float x1, final float y1,
 356                                             final float x0p, final float y0p,
 357                                             final float x1p, final float y1p,
 358                                             final float[] m, int off)
 359     {
 360         float x10 = x1 - x0;
 361         float y10 = y1 - y0;
 362         float x10p = x1p - x0p;
 363         float y10p = y1p - y0p;
 364 









 365         float den = x10*y10p - x10p*y10;
 366         float t = x10p*(y0-y0p) - y10p*(x0-x0p);
 367         t /= den;
 368         m[off++] = x0 + t*x10;
 369         m[off]   = y0 + t*y10;
 370     }
 371 


































 372     private void drawMiter(final float pdx, final float pdy,
 373                            final float x0, final float y0,
 374                            final float dx, final float dy,
 375                            float omx, float omy, float mx, float my,
 376                            boolean rev)
 377     {
 378         if ((mx == omx && my == omy) ||
 379             (pdx == 0f && pdy == 0f) ||
 380             (dx == 0f && dy == 0f))
 381         {
 382             return;
 383         }
 384 
 385         if (rev) {
 386             omx = -omx;
 387             omy = -omy;
 388             mx  = -mx;
 389             my  = -my;
 390         }
 391 
 392         computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
 393                             (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
 394                             miter, 0);
 395 
 396         final float miterX = miter[0];
 397         final float miterY = miter[1];
 398         float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
 399 
 400         // If the lines are parallel, lenSq will be either NaN or +inf
 401         // (actually, I'm not sure if the latter is possible. The important
 402         // thing is that -inf is not possible, because lenSq is a square).
 403         // For both of those values, the comparison below will fail and
 404         // no miter will be drawn, which is correct.
 405         if (lenSq < miterLimitSq) {
 406             emitLineTo(miterX, miterY, rev);
 407         }
 408     }
 409 
 410     @Override
 411     public void moveTo(float x0, float y0) {
 412         if (prev == DRAWING_OP_TO) {
 413             finish();
 414         }
 415         this.sx0 = this.cx0 = x0;
 416         this.sy0 = this.cy0 = y0;
 417         this.cdx = this.sdx = 1f;
 418         this.cdy = this.sdy = 0f;
 419         this.prev = MOVE_TO;
 420     }
 421 
 422     @Override
 423     public void lineTo(float x1, float y1) {
 424         float dx = x1 - cx0;
 425         float dy = y1 - cy0;
 426         if (dx == 0f && dy == 0f) {
 427             dx = 1f;
 428         }
 429         computeOffset(dx, dy, lineWidth2, offset0);
 430         final float mx = offset0[0];
 431         final float my = offset0[1];
 432 
 433         drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
 434 
 435         emitLineTo(cx0 + mx, cy0 + my);
 436         emitLineTo( x1 + mx,  y1 + my);
 437 
 438         emitLineToRev(cx0 - mx, cy0 - my);
 439         emitLineToRev( x1 - mx,  y1 - my);
 440 
 441         this.cmx = mx;
 442         this.cmy = my;
 443         this.cdx = dx;
 444         this.cdy = dy;
 445         this.cx0 = x1;
 446         this.cy0 = y1;
 447         this.prev = DRAWING_OP_TO;
 448     }
 449 
 450     @Override
 451     public void closePath() {
 452         if (prev != DRAWING_OP_TO) {
 453             if (prev == CLOSE) {
 454                 return;
 455             }
 456             emitMoveTo(cx0, cy0 - lineWidth2);
 457             this.cmx = this.smx = 0f;
 458             this.cmy = this.smy = -lineWidth2;
 459             this.cdx = this.sdx = 1f;
 460             this.cdy = this.sdy = 0f;
 461             finish();
 462             return;
 463         }
 464 
 465         if (cx0 != sx0 || cy0 != sy0) {
 466             lineTo(sx0, sy0);
 467         }
 468 
 469         drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
 470 
 471         emitLineTo(sx0 + smx, sy0 + smy);
 472 
 473         emitMoveTo(sx0 - smx, sy0 - smy);
 474         emitReverse();
 475 
 476         this.prev = CLOSE;
 477         emitClose();
 478     }
 479 
 480     private void emitReverse() {


 623                                 float x2, float y2,
 624                                 float[] left, float[] right) {
 625         computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
 626         final float mx = offset0[0];
 627         final float my = offset0[1];
 628         left[0] = x1 + mx;
 629         left[1] = y1 + my;
 630         left[2] = x2 + mx;
 631         left[3] = y2 + my;
 632         right[0] = x1 - mx;
 633         right[1] = y1 - my;
 634         right[2] = x2 - mx;
 635         right[3] = y2 - my;
 636     }
 637 
 638     private int computeOffsetCubic(float[] pts, final int off,
 639                                    float[] leftOff, float[] rightOff)
 640     {
 641         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
 642         // vanishes, which creates problems with computeOffset. Usually
 643         // this happens when this stroker object is trying to winden
 644         // a curve with a cusp. What happens is that curveTo splits
 645         // the input curve at the cusp, and passes it to this function.
 646         // because of inaccuracies in the splitting, we consider points
 647         // equal if they're very close to each other.
 648         final float x1 = pts[off + 0], y1 = pts[off + 1];
 649         final float x2 = pts[off + 2], y2 = pts[off + 3];
 650         final float x3 = pts[off + 4], y3 = pts[off + 5];
 651         final float x4 = pts[off + 6], y4 = pts[off + 7];
 652 
 653         float dx4 = x4 - x3;
 654         float dy4 = y4 - y3;
 655         float dx1 = x2 - x1;
 656         float dy1 = y2 - y1;
 657 
 658         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
 659         // in which case ignore if p1 == p2
 660         final boolean p1eqp2 = within(x1,y1,x2,y2, 6f * ulp(y2));
 661         final boolean p3eqp4 = within(x3,y3,x4,y4, 6f * ulp(y4));
 662         if (p1eqp2 && p3eqp4) {
 663             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
 664             return 4;
 665         } else if (p1eqp2) {
 666             dx1 = x3 - x1;
 667             dy1 = y3 - y1;
 668         } else if (p3eqp4) {
 669             dx4 = x4 - x2;
 670             dy4 = y4 - y2;
 671         }
 672 
 673         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
 674         float dotsq = (dx1 * dx4 + dy1 * dy4);
 675         dotsq *= dotsq;
 676         float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
 677         if (Helpers.within(dotsq, l1sq * l4sq, 4f * ulp(dotsq))) {
 678             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
 679             return 4;
 680         }
 681 
 682 //      What we're trying to do in this function is to approximate an ideal
 683 //      offset curve (call it I) of the input curve B using a bezier curve Bp.
 684 //      The constraints I use to get the equations are:
 685 //
 686 //      1. The computed curve Bp should go through I(0) and I(1). These are
 687 //      x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find
 688 //      4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p).
 689 //
 690 //      2. Bp should have slope equal in absolute value to I at the endpoints. So,
 691 //      (by the way, the operator || in the comments below means "aligned with".
 692 //      It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that
 693 //      vectors I'(0) and Bp'(0) are aligned, which is the same as saying
 694 //      that the tangent lines of I and Bp at 0 are parallel. Mathematically
 695 //      this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some
 696 //      nonzero constant.)
 697 //      I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and


 709 //          (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to
 710 //          (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3
 711 //      We can substitute (1) and (2) from above into (4) and we get:
 712 //          (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p
 713 //      which is equivalent to
 714 //          (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p)
 715 //
 716 //      The right side of this is a 2D vector, and we know I(0.5), which gives us
 717 //      Bp(0.5), which gives us the value of the right side.
 718 //      The left side is just a matrix vector multiplication in disguise. It is
 719 //
 720 //      [x2-x1, x4-x3][c1]
 721 //      [y2-y1, y4-y3][c2]
 722 //      which, is equal to
 723 //      [dx1, dx4][c1]
 724 //      [dy1, dy4][c2]
 725 //      At this point we are left with a simple linear system and we solve it by
 726 //      getting the inverse of the matrix above. Then we use [c1,c2] to compute
 727 //      p2p and p3p.
 728 
 729         float x = (x1 + 3f * (x2 + x3) + x4) / 8f;
 730         float y = (y1 + 3f * (y2 + y3) + y4) / 8f;
 731         // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
 732         // c*B'(0.5) for some constant c.
 733         float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
 734 
 735         // this computes the offsets at t=0, 0.5, 1, using the property that
 736         // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
 737         // the (dx/dt, dy/dt) vectors at the endpoints.
 738         computeOffset(dx1, dy1, lineWidth2, offset0);
 739         computeOffset(dxm, dym, lineWidth2, offset1);
 740         computeOffset(dx4, dy4, lineWidth2, offset2);
 741         float x1p = x1 + offset0[0]; // start
 742         float y1p = y1 + offset0[1]; // point
 743         float xi  = x  + offset1[0]; // interpolation
 744         float yi  = y  + offset1[1]; // point
 745         float x4p = x4 + offset2[0]; // end
 746         float y4p = y4 + offset2[1]; // point
 747 
 748         float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4));
 749 
 750         float two_pi_m_p1_m_p4x = 2f * xi - x1p - x4p;
 751         float two_pi_m_p1_m_p4y = 2f * yi - y1p - y4p;
 752         float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
 753         float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 754 
 755         float x2p, y2p, x3p, y3p;
 756         x2p = x1p + c1*dx1;
 757         y2p = y1p + c1*dy1;
 758         x3p = x4p + c2*dx4;
 759         y3p = y4p + c2*dy4;
 760 
 761         leftOff[0] = x1p; leftOff[1] = y1p;
 762         leftOff[2] = x2p; leftOff[3] = y2p;
 763         leftOff[4] = x3p; leftOff[5] = y3p;
 764         leftOff[6] = x4p; leftOff[7] = y4p;
 765 
 766         x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
 767         xi = xi - 2f * offset1[0]; yi = yi - 2f * offset1[1];
 768         x4p = x4 - offset2[0]; y4p = y4 - offset2[1];
 769 
 770         two_pi_m_p1_m_p4x = 2f * xi - x1p - x4p;
 771         two_pi_m_p1_m_p4y = 2f * yi - y1p - y4p;
 772         c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
 773         c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 774 
 775         x2p = x1p + c1*dx1;
 776         y2p = y1p + c1*dy1;
 777         x3p = x4p + c2*dx4;
 778         y3p = y4p + c2*dy4;
 779 
 780         rightOff[0] = x1p; rightOff[1] = y1p;
 781         rightOff[2] = x2p; rightOff[3] = y2p;
 782         rightOff[4] = x3p; rightOff[5] = y3p;
 783         rightOff[6] = x4p; rightOff[7] = y4p;
 784         return 8;
 785     }
 786 


 787     // return the kind of curve in the right and left arrays.
 788     private int computeOffsetQuad(float[] pts, final int off,
 789                                   float[] leftOff, float[] rightOff)
 790     {
 791         final float x1 = pts[off + 0], y1 = pts[off + 1];
 792         final float x2 = pts[off + 2], y2 = pts[off + 3];
 793         final float x3 = pts[off + 4], y3 = pts[off + 5];
 794 
 795         final float dx3 = x3 - x2;
 796         final float dy3 = y3 - y2;
 797         final float dx1 = x2 - x1;
 798         final float dy1 = y2 - y1;
 799 
 800         // this computes the offsets at t = 0, 1
 801         computeOffset(dx1, dy1, lineWidth2, offset0);
 802         computeOffset(dx3, dy3, lineWidth2, offset1);




 803 
 804         leftOff[0]  = x1 + offset0[0]; leftOff[1]  = y1 + offset0[1];
 805         leftOff[4]  = x3 + offset1[0]; leftOff[5]  = y3 + offset1[1];
 806         rightOff[0] = x1 - offset0[0]; rightOff[1] = y1 - offset0[1];
 807         rightOff[4] = x3 - offset1[0]; rightOff[5] = y3 - offset1[1];
 808 
 809         float x1p = leftOff[0]; // start
 810         float y1p = leftOff[1]; // point
 811         float x3p = leftOff[4]; // end
 812         float y3p = leftOff[5]; // point
 813 
 814         // Corner cases:
 815         // 1. If the two control vectors are parallel, we'll end up with NaN's
 816         //    in leftOff (and rightOff in the body of the if below), so we'll
 817         //    do getLineOffsets, which is right.
 818         // 2. If the first or second two points are equal, then (dx1,dy1)==(0,0)
 819         //    or (dx3,dy3)==(0,0), so (x1p, y1p)==(x1p+dx1, y1p+dy1)
 820         //    or (x3p, y3p)==(x3p-dx3, y3p-dy3), which means that
 821         //    computeIntersection will put NaN's in leftOff and right off, and
 822         //    we will do getLineOffsets, which is right.
 823         computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
 824         float cx = leftOff[2];
 825         float cy = leftOff[3];
 826 
 827         if (!(isFinite(cx) && isFinite(cy))) {
 828             // maybe the right path is not degenerate.
 829             x1p = rightOff[0];
 830             y1p = rightOff[1];
 831             x3p = rightOff[4];
 832             y3p = rightOff[5];
 833             computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
 834             cx = rightOff[2];
 835             cy = rightOff[3];
 836             if (!(isFinite(cx) && isFinite(cy))) {
 837                 // both are degenerate. This curve is a line.
 838                 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
 839                 return 4;
 840             }
 841             // {left,right}Off[0,1,4,5] are already set to the correct values.
 842             leftOff[2] = 2f * x2 - cx;
 843             leftOff[3] = 2f * y2 - cy;
 844             return 6;
 845         }
 846 
 847         // rightOff[2,3] = (x2,y2) - ((left_x2, left_y2) - (x2, y2))
 848         // == 2*(x2, y2) - (left_x2, left_y2)
 849         rightOff[2] = 2f * x2 - cx;
 850         rightOff[3] = 2f * y2 - cy;
 851         return 6;
 852     }
 853 
 854     private static boolean isFinite(float x) {
 855         return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY);
 856     }
 857 
 858     // If this class is compiled with ecj, then Hotspot crashes when OSR
 859     // compiling this function. See bugs 7004570 and 6675699
 860     // TODO: until those are fixed, we should work around that by
 861     // manually inlining this into curveTo and quadTo.
 862 /******************************* WORKAROUND **********************************
 863     private void somethingTo(final int type) {
 864         // need these so we can update the state at the end of this method
 865         final float xf = middle[type-2], yf = middle[type-1];
 866         float dxs = middle[2] - middle[0];
 867         float dys = middle[3] - middle[1];
 868         float dxf = middle[type - 2] - middle[type - 4];
 869         float dyf = middle[type - 1] - middle[type - 3];
 870         switch(type) {
 871         case 6:
 872             if ((dxs == 0f && dys == 0f) ||
 873                 (dxf == 0f && dyf == 0f)) {
 874                dxs = dxf = middle[4] - middle[0];
 875                dys = dyf = middle[5] - middle[1];
 876             }
 877             break;
 878         case 8:
 879             boolean p1eqp2 = (dxs == 0f && dys == 0f);
 880             boolean p3eqp4 = (dxf == 0f && dyf == 0f);
 881             if (p1eqp2) {
 882                 dxs = middle[4] - middle[0];
 883                 dys = middle[5] - middle[1];
 884                 if (dxs == 0f && dys == 0f) {
 885                     dxs = middle[6] - middle[0];
 886                     dys = middle[7] - middle[1];
 887                 }
 888             }
 889             if (p3eqp4) {
 890                 dxf = middle[6] - middle[2];
 891                 dyf = middle[7] - middle[3];
 892                 if (dxf == 0f && dyf == 0f) {
 893                     dxf = middle[6] - middle[0];
 894                     dyf = middle[7] - middle[1];
 895                 }
 896             }
 897         }
 898         if (dxs == 0f && dys == 0f) {
 899             // this happens iff the "curve" is just a point
 900             lineTo(middle[0], middle[1]);
 901             return;
 902         }
 903         // if these vectors are too small, normalize them, to avoid future
 904         // precision problems.
 905         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
 906             float len = (float) sqrt(dxs*dxs + dys*dys);
 907             dxs /= len;
 908             dys /= len;
 909         }
 910         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
 911             float len = (float) sqrt(dxf*dxf + dyf*dyf);
 912             dxf /= len;
 913             dyf /= len;
 914         }
 915 
 916         computeOffset(dxs, dys, lineWidth2, offset0);
 917         final float mx = offset0[0];
 918         final float my = offset0[1];
 919         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
 920 
 921         int nSplits = findSubdivPoints(curve, middle, subdivTs, type, lineWidth2);
 922 
 923         int kind = 0;
 924         BreakPtrIterator it = curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
 925         while(it.hasNext()) {
 926             int curCurveOff = it.next();
 927 
 928             switch (type) {
 929             case 8:
 930                 kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
 931                 break;
 932             case 6:
 933                 kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
 934                 break;
 935             }
 936             emitLineTo(lp[0], lp[1]);
 937             switch(kind) {
 938             case 8:
 939                 emitCurveTo(lp[2], lp[3], lp[4], lp[5], lp[6], lp[7]);
 940                 emitCurveToRev(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5]);
 941                 break;
 942             case 6:
 943                 emitQuadTo(lp[2], lp[3], lp[4], lp[5]);
 944                 emitQuadToRev(rp[0], rp[1], rp[2], rp[3]);
 945                 break;
 946             case 4:
 947                 emitLineTo(lp[2], lp[3]);
 948                 emitLineTo(rp[0], rp[1], true);
 949                 break;
 950             }
 951             emitLineTo(rp[kind - 2], rp[kind - 1], true);
 952         }
 953 
 954         this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
 955         this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
 956         this.cdx = dxf;
 957         this.cdy = dyf;
 958         this.cx0 = xf;
 959         this.cy0 = yf;
 960         this.prev = DRAWING_OP_TO;
 961     }
 962 ****************************** END WORKAROUND *******************************/
 963 
 964     // finds values of t where the curve in pts should be subdivided in order
 965     // to get good offset curves a distance of w away from the middle curve.
 966     // Stores the points in ts, and returns how many of them there were.
 967     private static int findSubdivPoints(final Curve c, float[] pts, float[] ts,
 968                                         final int type, final float w)
 969     {
 970         final float x12 = pts[2] - pts[0];
 971         final float y12 = pts[3] - pts[1];
 972         // if the curve is already parallel to either axis we gain nothing
 973         // from rotating it.
 974         if (y12 != 0f && x12 != 0f) {
 975             // we rotate it so that the first vector in the control polygon is
 976             // parallel to the x-axis. This will ensure that rotated quarter
 977             // circles won't be subdivided.
 978             final float hypot = (float) sqrt(x12 * x12 + y12 * y12);
 979             final float cos = x12 / hypot;
 980             final float sin = y12 / hypot;
 981             final float x1 = cos * pts[0] + sin * pts[1];
 982             final float y1 = cos * pts[1] - sin * pts[0];
 983             final float x2 = cos * pts[2] + sin * pts[3];
 984             final float y2 = cos * pts[3] - sin * pts[2];
 985             final float x3 = cos * pts[4] + sin * pts[5];
 986             final float y3 = cos * pts[5] - sin * pts[4];
 987 
 988             switch(type) {
 989             case 8:
 990                 final float x4 = cos * pts[6] + sin * pts[7];
 991                 final float y4 = cos * pts[7] - sin * pts[6];
 992                 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
 993                 break;
 994             case 6:
 995                 c.set(x1, y1, x2, y2, x3, y3);
 996                 break;
 997             default:
 998             }


1014         // now we must subdivide at points where one of the offset curves will have
1015         // a cusp. This happens at ts where the radius of curvature is equal to w.
1016         ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
1017 
1018         ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
1019         Helpers.isort(ts, 0, ret);
1020         return ret;
1021     }
1022 
1023     @Override public void curveTo(float x1, float y1,
1024                                   float x2, float y2,
1025                                   float x3, float y3)
1026     {
1027         final float[] mid = middle;
1028 
1029         mid[0] = cx0; mid[1] = cy0;
1030         mid[2] = x1;  mid[3] = y1;
1031         mid[4] = x2;  mid[5] = y2;
1032         mid[6] = x3;  mid[7] = y3;
1033 
1034         // inlined version of somethingTo(8);
1035         // See the TODO on somethingTo
1036 
1037         // need these so we can update the state at the end of this method
1038         final float xf = mid[6], yf = mid[7];
1039         float dxs = mid[2] - mid[0];
1040         float dys = mid[3] - mid[1];
1041         float dxf = mid[6] - mid[4];
1042         float dyf = mid[7] - mid[5];
1043 
1044         boolean p1eqp2 = (dxs == 0f && dys == 0f);
1045         boolean p3eqp4 = (dxf == 0f && dyf == 0f);
1046         if (p1eqp2) {
1047             dxs = mid[4] - mid[0];
1048             dys = mid[5] - mid[1];
1049             if (dxs == 0f && dys == 0f) {
1050                 dxs = mid[6] - mid[0];
1051                 dys = mid[7] - mid[1];
1052             }
1053         }
1054         if (p3eqp4) {
1055             dxf = mid[6] - mid[2];
1056             dyf = mid[7] - mid[3];
1057             if (dxf == 0f && dyf == 0f) {
1058                 dxf = mid[6] - mid[0];
1059                 dyf = mid[7] - mid[1];
1060             }
1061         }
1062         if (dxs == 0f && dys == 0f) {
1063             // this happens if the "curve" is just a point
1064             lineTo(mid[0], mid[1]);
1065             return;
1066         }
1067 
1068         // if these vectors are too small, normalize them, to avoid future
1069         // precision problems.
1070         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
1071             float len = (float) sqrt(dxs*dxs + dys*dys);
1072             dxs /= len;
1073             dys /= len;
1074         }
1075         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
1076             float len = (float) sqrt(dxf*dxf + dyf*dyf);
1077             dxf /= len;
1078             dyf /= len;
1079         }
1080 
1081         computeOffset(dxs, dys, lineWidth2, offset0);
1082         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
1083 
1084         int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);








1085 
1086         final float[] l = lp;
1087         final float[] r = rp;
1088 
1089         int kind = 0;
1090         BreakPtrIterator it = curve.breakPtsAtTs(mid, 8, subdivTs, nSplits);
1091         while(it.hasNext()) {
1092             int curCurveOff = it.next();
1093 
1094             kind = computeOffsetCubic(mid, curCurveOff, l, r);
1095             emitLineTo(l[0], l[1]);
1096 
1097             switch(kind) {
1098             case 8:
1099                 emitCurveTo(l[2], l[3], l[4], l[5], l[6], l[7]);
1100                 emitCurveToRev(r[0], r[1], r[2], r[3], r[4], r[5]);
1101                 break;
1102             case 4:
1103                 emitLineTo(l[2], l[3]);
1104                 emitLineToRev(r[0], r[1]);
1105                 break;
1106             default:
1107             }
1108             emitLineToRev(r[kind - 2], r[kind - 1]);
1109         }
1110 
1111         this.cmx = (l[kind - 2] - r[kind - 2]) / 2f;
1112         this.cmy = (l[kind - 1] - r[kind - 1]) / 2f;
1113         this.cdx = dxf;
1114         this.cdy = dyf;
1115         this.cx0 = xf;
1116         this.cy0 = yf;
1117         this.prev = DRAWING_OP_TO;
1118     }
1119 
1120     @Override public void quadTo(float x1, float y1, float x2, float y2) {
1121         final float[] mid = middle;
1122 
1123         mid[0] = cx0; mid[1] = cy0;
1124         mid[2] = x1;  mid[3] = y1;
1125         mid[4] = x2;  mid[5] = y2;
1126 
1127         // inlined version of somethingTo(8);
1128         // See the TODO on somethingTo
1129 
1130         // need these so we can update the state at the end of this method
1131         final float xf = mid[4], yf = mid[5];
1132         float dxs = mid[2] - mid[0];
1133         float dys = mid[3] - mid[1];
1134         float dxf = mid[4] - mid[2];
1135         float dyf = mid[5] - mid[3];
1136         if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
1137             dxs = dxf = mid[4] - mid[0];
1138             dys = dyf = mid[5] - mid[1];
1139         }
1140         if (dxs == 0f && dys == 0f) {
1141             // this happens if the "curve" is just a point
1142             lineTo(mid[0], mid[1]);
1143             return;
1144         }
1145         // if these vectors are too small, normalize them, to avoid future
1146         // precision problems.
1147         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
1148             float len = (float) sqrt(dxs*dxs + dys*dys);
1149             dxs /= len;
1150             dys /= len;
1151         }
1152         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
1153             float len = (float) sqrt(dxf*dxf + dyf*dyf);
1154             dxf /= len;
1155             dyf /= len;
1156         }
1157 
1158         computeOffset(dxs, dys, lineWidth2, offset0);
1159         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
1160 
1161         int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
1162 








1163         final float[] l = lp;
1164         final float[] r = rp;
1165 
1166         int kind = 0;
1167         BreakPtrIterator it = curve.breakPtsAtTs(mid, 6, subdivTs, nSplits);
1168         while(it.hasNext()) {
1169             int curCurveOff = it.next();
1170 
1171             kind = computeOffsetQuad(mid, curCurveOff, l, r);
1172             emitLineTo(l[0], l[1]);
1173 
1174             switch(kind) {
1175             case 6:
1176                 emitQuadTo(l[2], l[3], l[4], l[5]);
1177                 emitQuadToRev(r[0], r[1], r[2], r[3]);
1178                 break;
1179             case 4:
1180                 emitLineTo(l[2], l[3]);
1181                 emitLineToRev(r[0], r[1]);
1182                 break;
1183             default:
1184             }
1185             emitLineToRev(r[kind - 2], r[kind - 1]);
1186         }
1187 
1188         this.cmx = (l[kind - 2] - r[kind - 2]) / 2f;
1189         this.cmy = (l[kind - 1] - r[kind - 1]) / 2f;
1190         this.cdx = dxf;
1191         this.cdy = dyf;
1192         this.cx0 = xf;
1193         this.cy0 = yf;
1194         this.prev = DRAWING_OP_TO;
1195     }
1196 
1197     @Override public long getNativeConsumer() {
1198         throw new InternalError("Stroker doesn't use a native consumer");
1199     }
1200 
1201     // a stack of polynomial curves where each curve shares endpoints with
1202     // adjacent ones.
1203     static final class PolyStack {
1204         private static final byte TYPE_LINETO  = (byte) 0;
1205         private static final byte TYPE_QUADTO  = (byte) 1;
1206         private static final byte TYPE_CUBICTO = (byte) 2;
1207 
1208         // curves capacity = edges count (4096) = half edges x 2 (coords)
1209         private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT;
1210 
1211         // types capacity = half edges count (2048)
1212         private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT >> 1;
1213 
1214         float[] curves;
1215         int end;
1216         byte[] curveTypes;
1217         int numCurves;
1218 
1219         // per-thread renderer context
1220         final RendererContext rdrCtx;
1221 
1222         // curves ref (dirty)
1223         final FloatArrayCache.Reference curves_ref;
1224         // curveTypes ref (dirty)
1225         final ByteArrayCache.Reference curveTypes_ref;
1226 
1227         // used marks (stats only)
1228         int curveTypesUseMark;
1229         int curvesUseMark;
1230 
1231         /**
1232          * Constructor
1233          * @param rdrCtx per-thread renderer context
1234          */
1235         PolyStack(final RendererContext rdrCtx) {
1236             this.rdrCtx = rdrCtx;
1237 
1238             curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 16K
1239             curves     = curves_ref.initial;
1240 
1241             curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 2K
1242             curveTypes     = curveTypes_ref.initial;
1243             numCurves = 0;
1244             end = 0;
1245 
1246             if (DO_STATS) {
1247                 curveTypesUseMark = 0;
1248                 curvesUseMark = 0;
1249             }
1250         }
1251 
1252         /**
1253          * Disposes this PolyStack:
1254          * clean up before reusing this instance
1255          */
1256         void dispose() {
1257             end = 0;
1258             numCurves = 0;
1259 
1260             if (DO_STATS) {
1261                 rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);


1352                     io.quadTo(_curves[e+0], _curves[e+1],
1353                               _curves[e+2], _curves[e+3]);
1354                     continue;
1355                 case TYPE_CUBICTO:
1356                     e -= 6;
1357                     io.curveTo(_curves[e+0], _curves[e+1],
1358                                _curves[e+2], _curves[e+3],
1359                                _curves[e+4], _curves[e+5]);
1360                     continue;
1361                 default:
1362                 }
1363             }
1364             numCurves = 0;
1365             end = 0;
1366         }
1367 
1368         @Override
1369         public String toString() {
1370             String ret = "";
1371             int nc = numCurves;
1372             int e  = end;
1373             int len;
1374             while (nc != 0) {
1375                 switch(curveTypes[--nc]) {
1376                 case TYPE_LINETO:
1377                     len = 2;
1378                     ret += "line: ";
1379                     break;
1380                 case TYPE_QUADTO:
1381                     len = 4;
1382                     ret += "quad: ";
1383                     break;
1384                 case TYPE_CUBICTO:
1385                     len = 6;
1386                     ret += "cubic: ";
1387                     break;
1388                 default:
1389                     len = 0;
1390                 }
1391                 e -= len;
1392                 ret += Arrays.toString(Arrays.copyOfRange(curves, e, e+len))
1393                                        + "\n";
1394             }
1395             return ret;
1396         }
1397     }
1398 }
   1 /*
   2  * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.marlin;
  27 
  28 import java.util.Arrays;


  29 
  30 import sun.awt.geom.PathConsumer2D;


  31 
  32 // TODO: some of the arithmetic here is too verbose and prone to hard to
  33 // debug typos. We should consider making a small Point/Vector class that
  34 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
  35 final class Stroker implements PathConsumer2D, MarlinConst {
  36 
  37     private static final int MOVE_TO = 0;
  38     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
  39     private static final int CLOSE = 2;
  40 
  41     /**
  42      * Constant value for join style.
  43      */
  44     public static final int JOIN_MITER = 0;
  45 
  46     /**
  47      * Constant value for join style.
  48      */
  49     public static final int JOIN_ROUND = 1;
  50 


  54     public static final int JOIN_BEVEL = 2;
  55 
  56     /**
  57      * Constant value for end cap style.
  58      */
  59     public static final int CAP_BUTT = 0;
  60 
  61     /**
  62      * Constant value for end cap style.
  63      */
  64     public static final int CAP_ROUND = 1;
  65 
  66     /**
  67      * Constant value for end cap style.
  68      */
  69     public static final int CAP_SQUARE = 2;
  70 
  71     // pisces used to use fixed point arithmetic with 16 decimal digits. I
  72     // didn't want to change the values of the constant below when I converted
  73     // it to floating point, so that's why the divisions by 2^16 are there.
  74     private static final float ROUND_JOIN_THRESHOLD = 1000.0f/65536.0f;
  75 
  76     private static final float C = 0.5522847498307933f;
  77 
  78     private static final int MAX_N_CURVES = 11;
  79 
  80     private PathConsumer2D out;
  81 
  82     private int capStyle;
  83     private int joinStyle;
  84 
  85     private float lineWidth2;
  86     private float invHalfLineWidth2Sq;
  87 
  88     private final float[] offset0 = new float[2];
  89     private final float[] offset1 = new float[2];
  90     private final float[] offset2 = new float[2];
  91     private final float[] miter = new float[2];
  92     private float miterLimitSq;
  93 
  94     private int prev;
  95 
  96     // The starting point of the path, and the slope there.
  97     private float sx0, sy0, sdx, sdy;
  98     // the current point and the slope there.
  99     private float cx0, cy0, cdx, cdy; // c stands for current
 100     // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
 101     // first and last points on the left parallel path. Since this path is
 102     // parallel, it's slope at any point is parallel to the slope of the
 103     // original path (thought they may have different directions), so these
 104     // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
 105     // would be error prone and hard to read, so we keep these anyway.
 106     private float smx, smy, cmx, cmy;
 107 
 108     private final PolyStack reverse;
 109 
 110     // This is where the curve to be processed is put. We give it
 111     // enough room to store all curves.
 112     private final float[] middle = new float[MAX_N_CURVES * 6 + 2];

 113     private final float[] lp = new float[8];
 114     private final float[] rp = new float[8];
 115     private final float[] subdivTs = new float[MAX_N_CURVES - 1];
 116 
 117     // per-thread renderer context
 118     final RendererContext rdrCtx;
 119 
 120     // dirty curve
 121     final Curve curve;
 122 
 123     /**
 124      * Constructs a <code>Stroker</code>.
 125      * @param rdrCtx per-thread renderer context
 126      */
 127     Stroker(final RendererContext rdrCtx) {
 128         this.rdrCtx = rdrCtx;
 129 
 130         this.reverse = new PolyStack(rdrCtx);
 131         this.curve = rdrCtx.curve;
 132     }


 136      *
 137      * @param pc2d an output <code>PathConsumer2D</code>.
 138      * @param lineWidth the desired line width in pixels
 139      * @param capStyle the desired end cap style, one of
 140      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
 141      * <code>CAP_SQUARE</code>.
 142      * @param joinStyle the desired line join style, one of
 143      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
 144      * <code>JOIN_BEVEL</code>.
 145      * @param miterLimit the desired miter limit
 146      * @return this instance
 147      */
 148     Stroker init(PathConsumer2D pc2d,
 149               float lineWidth,
 150               int capStyle,
 151               int joinStyle,
 152               float miterLimit)
 153     {
 154         this.out = pc2d;
 155 
 156         this.lineWidth2 = lineWidth / 2.0f;
 157         this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
 158         this.capStyle = capStyle;
 159         this.joinStyle = joinStyle;
 160 
 161         float limit = miterLimit * lineWidth2;
 162         this.miterLimitSq = limit * limit;
 163 
 164         this.prev = CLOSE;
 165 
 166         rdrCtx.stroking = 1;
 167 
 168         return this; // fluent API
 169     }
 170 
 171     /**
 172      * Disposes this stroker:
 173      * clean up before reusing this instance
 174      */
 175     void dispose() {
 176         reverse.dispose();
 177 
 178         if (DO_CLEAN_DIRTY) {
 179             // Force zero-fill dirty arrays:
 180             Arrays.fill(offset0, 0.0f);
 181             Arrays.fill(offset1, 0.0f);
 182             Arrays.fill(offset2, 0.0f);
 183             Arrays.fill(miter, 0.0f);
 184             Arrays.fill(middle, 0.0f);
 185             Arrays.fill(lp, 0.0f);
 186             Arrays.fill(rp, 0.0f);
 187             Arrays.fill(subdivTs, 0.0f);
 188         }
 189     }
 190 
 191     private static void computeOffset(final float lx, final float ly,
 192                                       final float w, final float[] m)
 193     {
 194         float len = lx*lx + ly*ly;
 195         if (len == 0.0f) {
 196             m[0] = 0.0f;
 197             m[1] = 0.0f;
 198         } else {
 199             len = (float) Math.sqrt(len);
 200             m[0] =  (ly * w) / len;
 201             m[1] = -(lx * w) / len;
 202         }
 203     }
 204 
 205     // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are
 206     // clockwise (if dx1,dy1 needs to be rotated clockwise to close
 207     // the smallest angle between it and dx2,dy2).
 208     // This is equivalent to detecting whether a point q is on the right side
 209     // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and
 210     // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
 211     // clockwise order.
 212     // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
 213     private static boolean isCW(final float dx1, final float dy1,
 214                                 final float dx2, final float dy2)
 215     {
 216         return dx1 * dy2 <= dy1 * dx2;
 217     }
 218 
 219     private void drawRoundJoin(float x, float y,
 220                                float omx, float omy, float mx, float my,
 221                                boolean rev,
 222                                float threshold)
 223     {
 224         if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) {
 225             return;
 226         }
 227 
 228         float domx = omx - mx;
 229         float domy = omy - my;
 230         float len = domx*domx + domy*domy;
 231         if (len < threshold) {
 232             return;
 233         }
 234 
 235         if (rev) {
 236             omx = -omx;
 237             omy = -omy;
 238             mx  = -mx;
 239             my  = -my;
 240         }
 241         drawRoundJoin(x, y, omx, omy, mx, my, rev);
 242     }
 243 
 244     private void drawRoundJoin(float cx, float cy,
 245                                float omx, float omy,
 246                                float mx, float my,
 247                                boolean rev)
 248     {
 249         // The sign of the dot product of mx,my and omx,omy is equal to the
 250         // the sign of the cosine of ext
 251         // (ext is the angle between omx,omy and mx,my).
 252         final float cosext = omx * mx + omy * my;
 253         // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
 254         // need 1 curve to approximate the circle section that joins omx,omy
 255         // and mx,my.
 256         final int numCurves = (cosext >= 0.0f) ? 1 : 2;
 257 
 258         switch (numCurves) {
 259         case 1:
 260             drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
 261             break;
 262         case 2:
 263             // we need to split the arc into 2 arcs spanning the same angle.
 264             // The point we want will be one of the 2 intersections of the
 265             // perpendicular bisector of the chord (omx,omy)->(mx,my) and the
 266             // circle. We could find this by scaling the vector
 267             // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies
 268             // on the circle), but that can have numerical problems when the angle
 269             // between omx,omy and mx,my is close to 180 degrees. So we compute a
 270             // normal of (omx,omy)-(mx,my). This will be the direction of the
 271             // perpendicular bisector. To get one of the intersections, we just scale
 272             // this vector that its length is lineWidth2 (this works because the
 273             // perpendicular bisector goes through the origin). This scaling doesn't
 274             // have numerical problems because we know that lineWidth2 divided by
 275             // this normal's length is at least 0.5 and at most sqrt(2)/2 (because
 276             // we know the angle of the arc is > 90 degrees).
 277             float nx = my - omy, ny = omx - mx;
 278             float nlen = (float) Math.sqrt(nx*nx + ny*ny);
 279             float scale = lineWidth2/nlen;
 280             float mmx = nx * scale, mmy = ny * scale;
 281 
 282             // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
 283             // computed the wrong intersection so we get the other one.
 284             // The test above is equivalent to if (rev).
 285             if (rev) {
 286                 mmx = -mmx;
 287                 mmy = -mmy;
 288             }
 289             drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
 290             drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
 291             break;
 292         default:
 293         }
 294     }
 295 
 296     // the input arc defined by omx,omy and mx,my must span <= 90 degrees.
 297     private void drawBezApproxForArc(final float cx, final float cy,
 298                                      final float omx, final float omy,
 299                                      final float mx, final float my,
 300                                      boolean rev)
 301     {
 302         final float cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq;
 303 
 304         // check round off errors producing cos(ext) > 1 and a NaN below
 305         // cos(ext) == 1 implies colinear segments and an empty join anyway
 306         if (cosext2 >= 0.5f) {
 307             // just return to avoid generating a flat curve:
 308             return;
 309         }
 310 
 311         // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc
 312         // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that
 313         // define the bezier curve we're computing.
 314         // It is computed using the constraints that P1-P0 and P3-P2 are parallel
 315         // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
 316         float cv = (float) ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) /
 317                             (1.0d + Math.sqrt(cosext2 + 0.5d)));
 318         // if clockwise, we need to negate cv.
 319         if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
 320             cv = -cv;
 321         }
 322         final float x1 = cx + omx;
 323         final float y1 = cy + omy;
 324         final float x2 = x1 - cv * omy;
 325         final float y2 = y1 + cv * omx;
 326 
 327         final float x4 = cx + mx;
 328         final float y4 = cy + my;
 329         final float x3 = x4 + cv * my;
 330         final float y3 = y4 - cv * mx;
 331 
 332         emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
 333     }
 334 
 335     private void drawRoundCap(float cx, float cy, float mx, float my) {
 336         final float Cmx = C * mx;
 337         final float Cmy = C * my;
 338         emitCurveTo(cx + mx - Cmy, cy + my + Cmx,
 339                     cx - my + Cmx, cy + mx + Cmy,
 340                     cx - my,       cy + mx);
 341         emitCurveTo(cx - my - Cmx, cy + mx - Cmy,
 342                     cx - mx - Cmy, cy - my + Cmx,
 343                     cx - mx,       cy - my);
 344     }
 345 
 346     // Return the intersection point of the lines (x0, y0) -> (x1, y1)
 347     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
 348     private static void computeMiter(final float x0, final float y0,
 349                                      final float x1, final float y1,
 350                                      final float x0p, final float y0p,
 351                                      final float x1p, final float y1p,
 352                                      final float[] m, int off)

 353     {
 354         float x10 = x1 - x0;
 355         float y10 = y1 - y0;
 356         float x10p = x1p - x0p;
 357         float y10p = y1p - y0p;
 358 
 359         // if this is 0, the lines are parallel. If they go in the
 360         // same direction, there is no intersection so m[off] and
 361         // m[off+1] will contain infinity, so no miter will be drawn.
 362         // If they go in the same direction that means that the start of the
 363         // current segment and the end of the previous segment have the same
 364         // tangent, in which case this method won't even be involved in
 365         // miter drawing because it won't be called by drawMiter (because
 366         // (mx == omx && my == omy) will be true, and drawMiter will return
 367         // immediately).
 368         float den = x10*y10p - x10p*y10;
 369         float t = x10p*(y0-y0p) - y10p*(x0-x0p);
 370         t /= den;
 371         m[off++] = x0 + t*x10;
 372         m[off]   = y0 + t*y10;
 373     }
 374 
 375     // Return the intersection point of the lines (x0, y0) -> (x1, y1)
 376     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
 377     private static void safeComputeMiter(final float x0, final float y0,
 378                                          final float x1, final float y1,
 379                                          final float x0p, final float y0p,
 380                                          final float x1p, final float y1p,
 381                                          final float[] m, int off)
 382     {
 383         float x10 = x1 - x0;
 384         float y10 = y1 - y0;
 385         float x10p = x1p - x0p;
 386         float y10p = y1p - y0p;
 387 
 388         // if this is 0, the lines are parallel. If they go in the
 389         // same direction, there is no intersection so m[off] and
 390         // m[off+1] will contain infinity, so no miter will be drawn.
 391         // If they go in the same direction that means that the start of the
 392         // current segment and the end of the previous segment have the same
 393         // tangent, in which case this method won't even be involved in
 394         // miter drawing because it won't be called by drawMiter (because
 395         // (mx == omx && my == omy) will be true, and drawMiter will return
 396         // immediately).
 397         float den = x10*y10p - x10p*y10;
 398         if (den == 0.0f) {
 399             m[off++] = (x0 + x0p) / 2.0f;
 400             m[off]   = (y0 + y0p) / 2.0f;
 401             return;
 402         }
 403         float t = x10p*(y0-y0p) - y10p*(x0-x0p);
 404         t /= den;
 405         m[off++] = x0 + t*x10;
 406         m[off] = y0 + t*y10;
 407     }
 408 
 409     private void drawMiter(final float pdx, final float pdy,
 410                            final float x0, final float y0,
 411                            final float dx, final float dy,
 412                            float omx, float omy, float mx, float my,
 413                            boolean rev)
 414     {
 415         if ((mx == omx && my == omy) ||
 416             (pdx == 0.0f && pdy == 0.0f) ||
 417             (dx == 0.0f && dy == 0.0f))
 418         {
 419             return;
 420         }
 421 
 422         if (rev) {
 423             omx = -omx;
 424             omy = -omy;
 425             mx  = -mx;
 426             my  = -my;
 427         }
 428 
 429         computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
 430                      (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
 431                      miter, 0);
 432 
 433         final float miterX = miter[0];
 434         final float miterY = miter[1];
 435         float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
 436 
 437         // If the lines are parallel, lenSq will be either NaN or +inf
 438         // (actually, I'm not sure if the latter is possible. The important
 439         // thing is that -inf is not possible, because lenSq is a square).
 440         // For both of those values, the comparison below will fail and
 441         // no miter will be drawn, which is correct.
 442         if (lenSq < miterLimitSq) {
 443             emitLineTo(miterX, miterY, rev);
 444         }
 445     }
 446 
 447     @Override
 448     public void moveTo(float x0, float y0) {
 449         if (prev == DRAWING_OP_TO) {
 450             finish();
 451         }
 452         this.sx0 = this.cx0 = x0;
 453         this.sy0 = this.cy0 = y0;
 454         this.cdx = this.sdx = 1.0f;
 455         this.cdy = this.sdy = 0.0f;
 456         this.prev = MOVE_TO;
 457     }
 458 
 459     @Override
 460     public void lineTo(float x1, float y1) {
 461         float dx = x1 - cx0;
 462         float dy = y1 - cy0;
 463         if (dx == 0.0f && dy == 0.0f) {
 464             dx = 1.0f;
 465         }
 466         computeOffset(dx, dy, lineWidth2, offset0);
 467         final float mx = offset0[0];
 468         final float my = offset0[1];
 469 
 470         drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
 471 
 472         emitLineTo(cx0 + mx, cy0 + my);
 473         emitLineTo( x1 + mx,  y1 + my);
 474 
 475         emitLineToRev(cx0 - mx, cy0 - my);
 476         emitLineToRev( x1 - mx,  y1 - my);
 477 
 478         this.cmx = mx;
 479         this.cmy = my;
 480         this.cdx = dx;
 481         this.cdy = dy;
 482         this.cx0 = x1;
 483         this.cy0 = y1;
 484         this.prev = DRAWING_OP_TO;
 485     }
 486 
 487     @Override
 488     public void closePath() {
 489         if (prev != DRAWING_OP_TO) {
 490             if (prev == CLOSE) {
 491                 return;
 492             }
 493             emitMoveTo(cx0, cy0 - lineWidth2);
 494             this.cmx = this.smx = 0.0f;
 495             this.cmy = this.smy = -lineWidth2;
 496             this.cdx = this.sdx = 1.0f;
 497             this.cdy = this.sdy = 0.0f;
 498             finish();
 499             return;
 500         }
 501 
 502         if (cx0 != sx0 || cy0 != sy0) {
 503             lineTo(sx0, sy0);
 504         }
 505 
 506         drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
 507 
 508         emitLineTo(sx0 + smx, sy0 + smy);
 509 
 510         emitMoveTo(sx0 - smx, sy0 - smy);
 511         emitReverse();
 512 
 513         this.prev = CLOSE;
 514         emitClose();
 515     }
 516 
 517     private void emitReverse() {


 660                                 float x2, float y2,
 661                                 float[] left, float[] right) {
 662         computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
 663         final float mx = offset0[0];
 664         final float my = offset0[1];
 665         left[0] = x1 + mx;
 666         left[1] = y1 + my;
 667         left[2] = x2 + mx;
 668         left[3] = y2 + my;
 669         right[0] = x1 - mx;
 670         right[1] = y1 - my;
 671         right[2] = x2 - mx;
 672         right[3] = y2 - my;
 673     }
 674 
 675     private int computeOffsetCubic(float[] pts, final int off,
 676                                    float[] leftOff, float[] rightOff)
 677     {
 678         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
 679         // vanishes, which creates problems with computeOffset. Usually
 680         // this happens when this stroker object is trying to widen
 681         // a curve with a cusp. What happens is that curveTo splits
 682         // the input curve at the cusp, and passes it to this function.
 683         // because of inaccuracies in the splitting, we consider points
 684         // equal if they're very close to each other.
 685         final float x1 = pts[off + 0], y1 = pts[off + 1];
 686         final float x2 = pts[off + 2], y2 = pts[off + 3];
 687         final float x3 = pts[off + 4], y3 = pts[off + 5];
 688         final float x4 = pts[off + 6], y4 = pts[off + 7];
 689 
 690         float dx4 = x4 - x3;
 691         float dy4 = y4 - y3;
 692         float dx1 = x2 - x1;
 693         float dy1 = y2 - y1;
 694 
 695         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
 696         // in which case ignore if p1 == p2
 697         final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0f * Math.ulp(y2));
 698         final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0f * Math.ulp(y4));
 699         if (p1eqp2 && p3eqp4) {
 700             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
 701             return 4;
 702         } else if (p1eqp2) {
 703             dx1 = x3 - x1;
 704             dy1 = y3 - y1;
 705         } else if (p3eqp4) {
 706             dx4 = x4 - x2;
 707             dy4 = y4 - y2;
 708         }
 709 
 710         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
 711         float dotsq = (dx1 * dx4 + dy1 * dy4);
 712         dotsq *= dotsq;
 713         float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
 714         if (Helpers.within(dotsq, l1sq * l4sq, 4.0f * Math.ulp(dotsq))) {
 715             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
 716             return 4;
 717         }
 718 
 719 //      What we're trying to do in this function is to approximate an ideal
 720 //      offset curve (call it I) of the input curve B using a bezier curve Bp.
 721 //      The constraints I use to get the equations are:
 722 //
 723 //      1. The computed curve Bp should go through I(0) and I(1). These are
 724 //      x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find
 725 //      4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p).
 726 //
 727 //      2. Bp should have slope equal in absolute value to I at the endpoints. So,
 728 //      (by the way, the operator || in the comments below means "aligned with".
 729 //      It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that
 730 //      vectors I'(0) and Bp'(0) are aligned, which is the same as saying
 731 //      that the tangent lines of I and Bp at 0 are parallel. Mathematically
 732 //      this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some
 733 //      nonzero constant.)
 734 //      I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and


 746 //          (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to
 747 //          (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3
 748 //      We can substitute (1) and (2) from above into (4) and we get:
 749 //          (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p
 750 //      which is equivalent to
 751 //          (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p)
 752 //
 753 //      The right side of this is a 2D vector, and we know I(0.5), which gives us
 754 //      Bp(0.5), which gives us the value of the right side.
 755 //      The left side is just a matrix vector multiplication in disguise. It is
 756 //
 757 //      [x2-x1, x4-x3][c1]
 758 //      [y2-y1, y4-y3][c2]
 759 //      which, is equal to
 760 //      [dx1, dx4][c1]
 761 //      [dy1, dy4][c2]
 762 //      At this point we are left with a simple linear system and we solve it by
 763 //      getting the inverse of the matrix above. Then we use [c1,c2] to compute
 764 //      p2p and p3p.
 765 
 766         float x = (x1 + 3.0f * (x2 + x3) + x4) / 8.0f;
 767         float y = (y1 + 3.0f * (y2 + y3) + y4) / 8.0f;
 768         // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
 769         // c*B'(0.5) for some constant c.
 770         float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
 771 
 772         // this computes the offsets at t=0, 0.5, 1, using the property that
 773         // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
 774         // the (dx/dt, dy/dt) vectors at the endpoints.
 775         computeOffset(dx1, dy1, lineWidth2, offset0);
 776         computeOffset(dxm, dym, lineWidth2, offset1);
 777         computeOffset(dx4, dy4, lineWidth2, offset2);
 778         float x1p = x1 + offset0[0]; // start
 779         float y1p = y1 + offset0[1]; // point
 780         float xi  = x  + offset1[0]; // interpolation
 781         float yi  = y  + offset1[1]; // point
 782         float x4p = x4 + offset2[0]; // end
 783         float y4p = y4 + offset2[1]; // point
 784 
 785         float invdet43 = 4.0f / (3.0f * (dx1 * dy4 - dy1 * dx4));
 786 
 787         float two_pi_m_p1_m_p4x = 2.0f * xi - x1p - x4p;
 788         float two_pi_m_p1_m_p4y = 2.0f * yi - y1p - y4p;
 789         float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
 790         float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 791 
 792         float x2p, y2p, x3p, y3p;
 793         x2p = x1p + c1*dx1;
 794         y2p = y1p + c1*dy1;
 795         x3p = x4p + c2*dx4;
 796         y3p = y4p + c2*dy4;
 797 
 798         leftOff[0] = x1p; leftOff[1] = y1p;
 799         leftOff[2] = x2p; leftOff[3] = y2p;
 800         leftOff[4] = x3p; leftOff[5] = y3p;
 801         leftOff[6] = x4p; leftOff[7] = y4p;
 802 
 803         x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
 804         xi = xi - 2.0f * offset1[0]; yi = yi - 2.0f * offset1[1];
 805         x4p = x4 - offset2[0]; y4p = y4 - offset2[1];
 806 
 807         two_pi_m_p1_m_p4x = 2.0f * xi - x1p - x4p;
 808         two_pi_m_p1_m_p4y = 2.0f * yi - y1p - y4p;
 809         c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
 810         c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 811 
 812         x2p = x1p + c1*dx1;
 813         y2p = y1p + c1*dy1;
 814         x3p = x4p + c2*dx4;
 815         y3p = y4p + c2*dy4;
 816 
 817         rightOff[0] = x1p; rightOff[1] = y1p;
 818         rightOff[2] = x2p; rightOff[3] = y2p;
 819         rightOff[4] = x3p; rightOff[5] = y3p;
 820         rightOff[6] = x4p; rightOff[7] = y4p;
 821         return 8;
 822     }
 823 
 824     // compute offset curves using bezier spline through t=0.5 (i.e.
 825     // ComputedCurve(0.5) == IdealParallelCurve(0.5))
 826     // return the kind of curve in the right and left arrays.
 827     private int computeOffsetQuad(float[] pts, final int off,
 828                                   float[] leftOff, float[] rightOff)
 829     {
 830         final float x1 = pts[off + 0], y1 = pts[off + 1];
 831         final float x2 = pts[off + 2], y2 = pts[off + 3];
 832         final float x3 = pts[off + 4], y3 = pts[off + 5];
 833 
 834         final float dx3 = x3 - x2;
 835         final float dy3 = y3 - y2;
 836         final float dx1 = x2 - x1;
 837         final float dy1 = y2 - y1;
 838 
 839         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
 840         // vanishes, which creates problems with computeOffset. Usually
 841         // this happens when this stroker object is trying to widen
 842         // a curve with a cusp. What happens is that curveTo splits
 843         // the input curve at the cusp, and passes it to this function.
 844         // because of inaccuracies in the splitting, we consider points
 845         // equal if they're very close to each other.
 846 
 847         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
 848         // in which case ignore.
 849         final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0f * Math.ulp(y2));
 850         final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0f * Math.ulp(y3));
 851         if (p1eqp2 || p2eqp3) {
 852             getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
 853             return 4;


































 854         }
 855 
 856         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
 857         float dotsq = (dx1 * dx3 + dy1 * dy3);
 858         dotsq *= dotsq;
 859         float l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3;
 860         if (Helpers.within(dotsq, l1sq * l3sq, 4.0f * Math.ulp(dotsq))) {
 861             getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
 862             return 4;




























































 863         }
 864 
 865         // this computes the offsets at t=0, 0.5, 1, using the property that
 866         // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
 867         // the (dx/dt, dy/dt) vectors at the endpoints.
 868         computeOffset(dx1, dy1, lineWidth2, offset0);
 869         computeOffset(dx3, dy3, lineWidth2, offset1);






 870 
 871         float x1p = x1 + offset0[0]; // start
 872         float y1p = y1 + offset0[1]; // point
 873         float x3p = x3 + offset1[0]; // end
 874         float y3p = y3 + offset1[1]; // point
 875         safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
 876         leftOff[0] = x1p; leftOff[1] = y1p;
 877         leftOff[4] = x3p; leftOff[5] = y3p;


















 878 
 879         x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
 880         x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
 881         safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
 882         rightOff[0] = x1p; rightOff[1] = y1p;
 883         rightOff[4] = x3p; rightOff[5] = y3p;
 884         return 6;

 885     }

 886 
 887     // finds values of t where the curve in pts should be subdivided in order
 888     // to get good offset curves a distance of w away from the middle curve.
 889     // Stores the points in ts, and returns how many of them there were.
 890     private static int findSubdivPoints(final Curve c, float[] pts, float[] ts,
 891                                         final int type, final float w)
 892     {
 893         final float x12 = pts[2] - pts[0];
 894         final float y12 = pts[3] - pts[1];
 895         // if the curve is already parallel to either axis we gain nothing
 896         // from rotating it.
 897         if (y12 != 0.0f && x12 != 0.0f) {
 898             // we rotate it so that the first vector in the control polygon is
 899             // parallel to the x-axis. This will ensure that rotated quarter
 900             // circles won't be subdivided.
 901             final float hypot = (float) Math.sqrt(x12 * x12 + y12 * y12);
 902             final float cos = x12 / hypot;
 903             final float sin = y12 / hypot;
 904             final float x1 = cos * pts[0] + sin * pts[1];
 905             final float y1 = cos * pts[1] - sin * pts[0];
 906             final float x2 = cos * pts[2] + sin * pts[3];
 907             final float y2 = cos * pts[3] - sin * pts[2];
 908             final float x3 = cos * pts[4] + sin * pts[5];
 909             final float y3 = cos * pts[5] - sin * pts[4];
 910 
 911             switch(type) {
 912             case 8:
 913                 final float x4 = cos * pts[6] + sin * pts[7];
 914                 final float y4 = cos * pts[7] - sin * pts[6];
 915                 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
 916                 break;
 917             case 6:
 918                 c.set(x1, y1, x2, y2, x3, y3);
 919                 break;
 920             default:
 921             }


 937         // now we must subdivide at points where one of the offset curves will have
 938         // a cusp. This happens at ts where the radius of curvature is equal to w.
 939         ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
 940 
 941         ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
 942         Helpers.isort(ts, 0, ret);
 943         return ret;
 944     }
 945 
 946     @Override public void curveTo(float x1, float y1,
 947                                   float x2, float y2,
 948                                   float x3, float y3)
 949     {
 950         final float[] mid = middle;
 951 
 952         mid[0] = cx0; mid[1] = cy0;
 953         mid[2] = x1;  mid[3] = y1;
 954         mid[4] = x2;  mid[5] = y2;
 955         mid[6] = x3;  mid[7] = y3;
 956 



 957         // need these so we can update the state at the end of this method
 958         final float xf = mid[6], yf = mid[7];
 959         float dxs = mid[2] - mid[0];
 960         float dys = mid[3] - mid[1];
 961         float dxf = mid[6] - mid[4];
 962         float dyf = mid[7] - mid[5];
 963 
 964         boolean p1eqp2 = (dxs == 0.0f && dys == 0.0f);
 965         boolean p3eqp4 = (dxf == 0.0f && dyf == 0.0f);
 966         if (p1eqp2) {
 967             dxs = mid[4] - mid[0];
 968             dys = mid[5] - mid[1];
 969             if (dxs == 0.0f && dys == 0.0f) {
 970                 dxs = mid[6] - mid[0];
 971                 dys = mid[7] - mid[1];
 972             }
 973         }
 974         if (p3eqp4) {
 975             dxf = mid[6] - mid[2];
 976             dyf = mid[7] - mid[3];
 977             if (dxf == 0.0f && dyf == 0.0f) {
 978                 dxf = mid[6] - mid[0];
 979                 dyf = mid[7] - mid[1];
 980             }
 981         }
 982         if (dxs == 0.0f && dys == 0.0f) {
 983             // this happens if the "curve" is just a point
 984             lineTo(mid[0], mid[1]);
 985             return;
 986         }
 987 
 988         // if these vectors are too small, normalize them, to avoid future
 989         // precision problems.
 990         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
 991             float len = (float) Math.sqrt(dxs*dxs + dys*dys);
 992             dxs /= len;
 993             dys /= len;
 994         }
 995         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
 996             float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
 997             dxf /= len;
 998             dyf /= len;
 999         }
1000 
1001         computeOffset(dxs, dys, lineWidth2, offset0);
1002         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
1003 
1004         final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
1005 
1006         float prevT = 0.0f;
1007         for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
1008             final float t = subdivTs[i];
1009             Helpers.subdivideCubicAt((t - prevT) / (1.0f - prevT),
1010                                      mid, off, mid, off, mid, off + 6);
1011             prevT = t;
1012         }
1013 
1014         final float[] l = lp;
1015         final float[] r = rp;
1016 
1017         int kind = 0;
1018         for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
1019             kind = computeOffsetCubic(mid, off, l, r);

1020 

1021             emitLineTo(l[0], l[1]);
1022 
1023             switch(kind) {
1024             case 8:
1025                 emitCurveTo(l[2], l[3], l[4], l[5], l[6], l[7]);
1026                 emitCurveToRev(r[0], r[1], r[2], r[3], r[4], r[5]);
1027                 break;
1028             case 4:
1029                 emitLineTo(l[2], l[3]);
1030                 emitLineToRev(r[0], r[1]);
1031                 break;
1032             default:
1033             }
1034             emitLineToRev(r[kind - 2], r[kind - 1]);
1035         }
1036 
1037         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
1038         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
1039         this.cdx = dxf;
1040         this.cdy = dyf;
1041         this.cx0 = xf;
1042         this.cy0 = yf;
1043         this.prev = DRAWING_OP_TO;
1044     }
1045 
1046     @Override public void quadTo(float x1, float y1, float x2, float y2) {
1047         final float[] mid = middle;
1048 
1049         mid[0] = cx0; mid[1] = cy0;
1050         mid[2] = x1;  mid[3] = y1;
1051         mid[4] = x2;  mid[5] = y2;
1052 



1053         // need these so we can update the state at the end of this method
1054         final float xf = mid[4], yf = mid[5];
1055         float dxs = mid[2] - mid[0];
1056         float dys = mid[3] - mid[1];
1057         float dxf = mid[4] - mid[2];
1058         float dyf = mid[5] - mid[3];
1059         if ((dxs == 0.0f && dys == 0.0f) || (dxf == 0.0f && dyf == 0.0f)) {
1060             dxs = dxf = mid[4] - mid[0];
1061             dys = dyf = mid[5] - mid[1];
1062         }
1063         if (dxs == 0.0f && dys == 0.0f) {
1064             // this happens if the "curve" is just a point
1065             lineTo(mid[0], mid[1]);
1066             return;
1067         }
1068         // if these vectors are too small, normalize them, to avoid future
1069         // precision problems.
1070         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
1071             float len = (float) Math.sqrt(dxs*dxs + dys*dys);
1072             dxs /= len;
1073             dys /= len;
1074         }
1075         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
1076             float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
1077             dxf /= len;
1078             dyf /= len;
1079         }
1080 
1081         computeOffset(dxs, dys, lineWidth2, offset0);
1082         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
1083 
1084         int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
1085 
1086         float prevt = 0.0f;
1087         for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
1088             final float t = subdivTs[i];
1089             Helpers.subdivideQuadAt((t - prevt) / (1.0f - prevt),
1090                                     mid, off, mid, off, mid, off + 4);
1091             prevt = t;
1092         }
1093 
1094         final float[] l = lp;
1095         final float[] r = rp;
1096 
1097         int kind = 0;
1098         for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
1099             kind = computeOffsetQuad(mid, off, l, r);

1100 

1101             emitLineTo(l[0], l[1]);
1102 
1103             switch(kind) {
1104             case 6:
1105                 emitQuadTo(l[2], l[3], l[4], l[5]);
1106                 emitQuadToRev(r[0], r[1], r[2], r[3]);
1107                 break;
1108             case 4:
1109                 emitLineTo(l[2], l[3]);
1110                 emitLineToRev(r[0], r[1]);
1111                 break;
1112             default:
1113             }
1114             emitLineToRev(r[kind - 2], r[kind - 1]);
1115         }
1116 
1117         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
1118         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
1119         this.cdx = dxf;
1120         this.cdy = dyf;
1121         this.cx0 = xf;
1122         this.cy0 = yf;
1123         this.prev = DRAWING_OP_TO;
1124     }
1125 
1126     @Override public long getNativeConsumer() {
1127         throw new InternalError("Stroker doesn't use a native consumer");
1128     }
1129 
1130     // a stack of polynomial curves where each curve shares endpoints with
1131     // adjacent ones.
1132     static final class PolyStack {
1133         private static final byte TYPE_LINETO  = (byte) 0;
1134         private static final byte TYPE_QUADTO  = (byte) 1;
1135         private static final byte TYPE_CUBICTO = (byte) 2;
1136 
1137         // curves capacity = edges count (8192) = edges x 2 (coords)
1138         private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
1139 
1140         // types capacity = edges count (4096)
1141         private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
1142 
1143         float[] curves;
1144         int end;
1145         byte[] curveTypes;
1146         int numCurves;
1147 
1148         // per-thread renderer context
1149         final RendererContext rdrCtx;
1150 
1151         // curves ref (dirty)
1152         final FloatArrayCache.Reference curves_ref;
1153         // curveTypes ref (dirty)
1154         final ByteArrayCache.Reference curveTypes_ref;
1155 
1156         // used marks (stats only)
1157         int curveTypesUseMark;
1158         int curvesUseMark;
1159 
1160         /**
1161          * Constructor
1162          * @param rdrCtx per-thread renderer context
1163          */
1164         PolyStack(final RendererContext rdrCtx) {
1165             this.rdrCtx = rdrCtx;
1166 
1167             curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
1168             curves     = curves_ref.initial;
1169 
1170             curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
1171             curveTypes     = curveTypes_ref.initial;
1172             numCurves = 0;
1173             end = 0;
1174 
1175             if (DO_STATS) {
1176                 curveTypesUseMark = 0;
1177                 curvesUseMark = 0;
1178             }
1179         }
1180 
1181         /**
1182          * Disposes this PolyStack:
1183          * clean up before reusing this instance
1184          */
1185         void dispose() {
1186             end = 0;
1187             numCurves = 0;
1188 
1189             if (DO_STATS) {
1190                 rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);


1281                     io.quadTo(_curves[e+0], _curves[e+1],
1282                               _curves[e+2], _curves[e+3]);
1283                     continue;
1284                 case TYPE_CUBICTO:
1285                     e -= 6;
1286                     io.curveTo(_curves[e+0], _curves[e+1],
1287                                _curves[e+2], _curves[e+3],
1288                                _curves[e+4], _curves[e+5]);
1289                     continue;
1290                 default:
1291                 }
1292             }
1293             numCurves = 0;
1294             end = 0;
1295         }
1296 
1297         @Override
1298         public String toString() {
1299             String ret = "";
1300             int nc = numCurves;
1301             int last = end;
1302             int len;
1303             while (nc != 0) {
1304                 switch(curveTypes[--nc]) {
1305                 case TYPE_LINETO:
1306                     len = 2;
1307                     ret += "line: ";
1308                     break;
1309                 case TYPE_QUADTO:
1310                     len = 4;
1311                     ret += "quad: ";
1312                     break;
1313                 case TYPE_CUBICTO:
1314                     len = 6;
1315                     ret += "cubic: ";
1316                     break;
1317                 default:
1318                     len = 0;
1319                 }
1320                 last -= len;
1321                 ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
1322                                        + "\n";
1323             }
1324             return ret;
1325         }
1326     }
1327 }
< prev index next >