src/share/classes/sun/java2d/pisces/Stroker.java

Print this page




  96 
  97     int[] offset = new int[2];
  98     int[] reverse = new int[100];
  99     int[] miter = new int[2];
 100     long miterLimitSq;
 101 
 102     int prev;
 103     int rindex;
 104     boolean started;
 105     boolean lineToOrigin;
 106     boolean joinToOrigin;
 107 
 108     int sx0, sy0, sx1, sy1, x0, y0, x1, y1;
 109     int mx0, my0, mx1, my1, omx, omy;
 110     int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0;
 111 
 112     double m00_2_m01_2;
 113     double m10_2_m11_2;
 114     double m00_m10_m01_m11;
 115 




 116     /**
 117      * Empty constructor.  <code>setOutput</code> and
 118      * <code>setParameters</code> must be called prior to calling any
 119      * other methods.
 120      */
 121     public Stroker() {}
 122 
 123     /**
 124      * Constructs a <code>Stroker</code>.
 125      *
 126      * @param output an output <code>LineSink</code>.
 127      * @param lineWidth the desired line width in pixels, in S15.16
 128      * format.
 129      * @param capStyle the desired end cap style, one of
 130      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
 131      * <code>CAP_SQUARE</code>.
 132      * @param joinStyle the desired line join style, one of
 133      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
 134      * <code>JOIN_BEVEL</code>.
 135      * @param miterLimit the desired miter limit, in S15.16 format.


 210         }
 211 
 212         this.numPenSegments = (int)(3.14159f*lineWidth/65536.0f);
 213         if (pen_dx == null || pen_dx.length < numPenSegments) {
 214             this.pen_dx = new int[numPenSegments];
 215             this.pen_dy = new int[numPenSegments];
 216             this.penIncluded = new boolean[numPenSegments];
 217             this.join = new int[2*numPenSegments];
 218         }
 219 
 220         for (int i = 0; i < numPenSegments; i++) {
 221             double r = lineWidth/2.0;
 222             double theta = (double)i*2.0*Math.PI/numPenSegments;
 223 
 224             double cos = Math.cos(theta);
 225             double sin = Math.sin(theta);
 226             pen_dx[i] = (int)(r*(dm00*cos + dm01*sin));
 227             pen_dy[i] = (int)(r*(dm10*cos + dm11*sin));
 228         }
 229 



 230         prev = CLOSE;
 231         rindex = 0;
 232         started = false;
 233         lineToOrigin = false;
 234     }
 235 
 236     private void computeOffset(int x0, int y0, int x1, int y1, int[] m) {
 237         long lx = (long)x1 - (long)x0;
 238         long ly = (long)y1 - (long)y0;
 239 
 240         int dx, dy;
 241         if (m00 > 0 && m00 == m11 && m01 == 0 & m10 == 0) {
 242             long ilen = PiscesMath.hypot(lx, ly);
 243             if (ilen == 0) {
 244                 dx = dy = 0;
 245             } else {
 246                 dx = (int)( (ly*scaledLineWidth2)/ilen);
 247                 dy = (int)(-(lx*scaledLineWidth2)/ilen);
 248             }
 249         } else {


 259             double ddy = dly*m00_m10_m01_m11 - dlx*m10_2_m11_2;
 260             dx = (int)(ddx*div);
 261             dy = (int)(ddy*div);
 262         }
 263 
 264         m[0] = dx;
 265         m[1] = dy;
 266     }
 267 
 268     private void ensureCapacity(int newrindex) {
 269         if (reverse.length < newrindex) {
 270             int[] tmp = new int[Math.max(newrindex, 6*reverse.length/5)];
 271             System.arraycopy(reverse, 0, tmp, 0, rindex);
 272             this.reverse = tmp;
 273         }
 274     }
 275 
 276     private boolean isCCW(int x0, int y0,
 277                           int x1, int y1,
 278                           int x2, int y2) {
 279         int dx0 = x1 - x0;
 280         int dy0 = y1 - y0;
 281         int dx1 = x2 - x1;
 282         int dy1 = y2 - y1;
 283         return (long)dx0*dy1 < (long)dy0*dx1;

 284     }
 285 
 286     private boolean side(int x, int y, int x0, int y0, int x1, int y1) {
 287         long lx = x;
 288         long ly = y;
 289         long lx0 = x0;
 290         long ly0 = y0;
 291         long lx1 = x1;
 292         long ly1 = y1;
 293 
 294         return (ly0 - ly1)*lx + (lx1 - lx0)*ly + (lx0*ly1 - lx1*ly0) > 0;
 295     }
 296 
 297     private int computeRoundJoin(int cx, int cy,
 298                                  int xa, int ya,
 299                                  int xb, int yb,
 300                                  int side,
 301                                  boolean flip,
 302                                  int[] join) {
 303         int px, py;


 455             mx = -mx;
 456             my = -my;
 457         }
 458 
 459         computeMiter(px0 + omx, py0 + omy, x0 + omx, y0 + omy,
 460                      x0 + mx, y0 + my, x1 + mx, y1 + my,
 461                      miter);
 462 
 463         // Compute miter length in untransformed coordinates
 464         long dx = (long)miter[0] - x0;
 465         long dy = (long)miter[1] - y0;
 466         long a = (dy*m00 - dx*m10) >> 16;
 467         long b = (dy*m01 - dx*m11) >> 16;
 468         long lenSq = a*a + b*b;
 469 
 470         if (lenSq < miterLimitSq) {
 471             emitLineTo(miter[0], miter[1], rev);
 472         }
 473     }
 474 


















 475 
 476     public void moveTo(int x0, int y0) {
 477         // System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
 478 







 479         if (lineToOrigin) {
 480             // not closing the path, do the previous lineTo
 481             lineToImpl(sx0, sy0, joinToOrigin);
 482             lineToOrigin = false;
 483         }
 484 
 485         if (prev == LINE_TO) {
 486             finish();
 487         }
 488 
 489         this.sx0 = this.x0 = x0;
 490         this.sy0 = this.y0 = y0;
 491         this.rindex = 0;
 492         this.started = false;
 493         this.joinSegment = false;
 494         this.prev = MOVE_TO;
 495     }
 496 
 497     boolean joinSegment = false;
 498 


 513             // not closing the path, do the previous lineTo
 514             lineToImpl(sx0, sy0, joinToOrigin);
 515             lineToOrigin = false;
 516         } else if (x1 == x0 && y1 == y0) {
 517             return;
 518         } else if (x1 == sx0 && y1 == sy0) {
 519             lineToOrigin = true;
 520             joinToOrigin = joinSegment;
 521             joinSegment = false;
 522             return;
 523         }
 524 
 525         lineToImpl(x1, y1, joinSegment);
 526         joinSegment = false;
 527     }
 528 
 529     private void lineToImpl(int x1, int y1, boolean joinSegment) {
 530         computeOffset(x0, y0, x1, y1, offset);
 531         int mx = offset[0];
 532         int my = offset[1];










 533 
 534         if (!started) {
 535             emitMoveTo(x0 + mx, y0 + my);
 536             this.sx1 = x1;
 537             this.sy1 = y1;
 538             this.mx0 = mx;
 539             this.my0 = my;
 540             started = true;
 541         } else {
 542             boolean ccw = isCCW(px0, py0, x0, y0, x1, y1);
 543             if (joinSegment) {
 544                 if (joinStyle == JOIN_MITER) {
 545                     drawMiter(px0, py0, x0, y0, x1, y1, omx, omy, mx, my,
 546                               ccw);
 547                 } else if (joinStyle == JOIN_ROUND) {
 548                     drawRoundJoin(x0, y0,
 549                                   omx, omy,
 550                                   mx, my, 0, false, ccw,
 551                                   ROUND_JOIN_THRESHOLD);
 552                 }




  96 
  97     int[] offset = new int[2];
  98     int[] reverse = new int[100];
  99     int[] miter = new int[2];
 100     long miterLimitSq;
 101 
 102     int prev;
 103     int rindex;
 104     boolean started;
 105     boolean lineToOrigin;
 106     boolean joinToOrigin;
 107 
 108     int sx0, sy0, sx1, sy1, x0, y0, x1, y1;
 109     int mx0, my0, mx1, my1, omx, omy;
 110     int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0;
 111 
 112     double m00_2_m01_2;
 113     double m10_2_m11_2;
 114     double m00_m10_m01_m11;
 115 
 116     // An upper bound on the width the the transformed line and the
 117     // untransformed line.
 118     int wub;
 119 
 120     /**
 121      * Empty constructor.  <code>setOutput</code> and
 122      * <code>setParameters</code> must be called prior to calling any
 123      * other methods.
 124      */
 125     public Stroker() {}
 126 
 127     /**
 128      * Constructs a <code>Stroker</code>.
 129      *
 130      * @param output an output <code>LineSink</code>.
 131      * @param lineWidth the desired line width in pixels, in S15.16
 132      * format.
 133      * @param capStyle the desired end cap style, one of
 134      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
 135      * <code>CAP_SQUARE</code>.
 136      * @param joinStyle the desired line join style, one of
 137      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
 138      * <code>JOIN_BEVEL</code>.
 139      * @param miterLimit the desired miter limit, in S15.16 format.


 214         }
 215 
 216         this.numPenSegments = (int)(3.14159f*lineWidth/65536.0f);
 217         if (pen_dx == null || pen_dx.length < numPenSegments) {
 218             this.pen_dx = new int[numPenSegments];
 219             this.pen_dy = new int[numPenSegments];
 220             this.penIncluded = new boolean[numPenSegments];
 221             this.join = new int[2*numPenSegments];
 222         }
 223 
 224         for (int i = 0; i < numPenSegments; i++) {
 225             double r = lineWidth/2.0;
 226             double theta = (double)i*2.0*Math.PI/numPenSegments;
 227 
 228             double cos = Math.cos(theta);
 229             double sin = Math.sin(theta);
 230             pen_dx[i] = (int)(r*(dm00*cos + dm01*sin));
 231             pen_dy[i] = (int)(r*(dm10*cos + dm11*sin));
 232         }
 233 
 234         wub = (int)((lineWidth * PiscesMath.hypot((long)m01+m00, (long)m10+m11)) >> 16); 
 235         wub = (wub > lineWidth) ? wub : lineWidth;
 236 
 237         prev = CLOSE;
 238         rindex = 0;
 239         started = false;
 240         lineToOrigin = false;
 241     }
 242 
 243     private void computeOffset(int x0, int y0, int x1, int y1, int[] m) {
 244         long lx = (long)x1 - (long)x0;
 245         long ly = (long)y1 - (long)y0;
 246 
 247         int dx, dy;
 248         if (m00 > 0 && m00 == m11 && m01 == 0 & m10 == 0) {
 249             long ilen = PiscesMath.hypot(lx, ly);
 250             if (ilen == 0) {
 251                 dx = dy = 0;
 252             } else {
 253                 dx = (int)( (ly*scaledLineWidth2)/ilen);
 254                 dy = (int)(-(lx*scaledLineWidth2)/ilen);
 255             }
 256         } else {


 266             double ddy = dly*m00_m10_m01_m11 - dlx*m10_2_m11_2;
 267             dx = (int)(ddx*div);
 268             dy = (int)(ddy*div);
 269         }
 270 
 271         m[0] = dx;
 272         m[1] = dy;
 273     }
 274 
 275     private void ensureCapacity(int newrindex) {
 276         if (reverse.length < newrindex) {
 277             int[] tmp = new int[Math.max(newrindex, 6*reverse.length/5)];
 278             System.arraycopy(reverse, 0, tmp, 0, rindex);
 279             this.reverse = tmp;
 280         }
 281     }
 282 
 283     private boolean isCCW(int x0, int y0,
 284                           int x1, int y1,
 285                           int x2, int y2) {
 286         // These need to be longs to avoid overflows.
 287         long dx0 = (long)x1 - x0;
 288         long dy0 = (long)y1 - y0;
 289         long dx1 = (long)x2 - x1;
 290         long dy1 = (long)y2 - y1;
 291         return dx0*dy1 < dy0*dx1;
 292     }
 293 
 294     private boolean side(int x, int y, int x0, int y0, int x1, int y1) {
 295         long lx = x;
 296         long ly = y;
 297         long lx0 = x0;
 298         long ly0 = y0;
 299         long lx1 = x1;
 300         long ly1 = y1;
 301 
 302         return (ly0 - ly1)*lx + (lx1 - lx0)*ly + (lx0*ly1 - lx1*ly0) > 0;
 303     }
 304 
 305     private int computeRoundJoin(int cx, int cy,
 306                                  int xa, int ya,
 307                                  int xb, int yb,
 308                                  int side,
 309                                  boolean flip,
 310                                  int[] join) {
 311         int px, py;


 463             mx = -mx;
 464             my = -my;
 465         }
 466 
 467         computeMiter(px0 + omx, py0 + omy, x0 + omx, y0 + omy,
 468                      x0 + mx, y0 + my, x1 + mx, y1 + my,
 469                      miter);
 470 
 471         // Compute miter length in untransformed coordinates
 472         long dx = (long)miter[0] - x0;
 473         long dy = (long)miter[1] - y0;
 474         long a = (dy*m00 - dx*m10) >> 16;
 475         long b = (dy*m01 - dx*m11) >> 16;
 476         long lenSq = a*a + b*b;
 477 
 478         if (lenSq < miterLimitSq) {
 479             emitLineTo(miter[0], miter[1], rev);
 480         }
 481     }
 482 
 483     private static void makeOverflowSafe(int[] coords, int wub) {
 484         int x = coords[0]; 
 485         int y = coords[1];
 486         if ((long)x + wub != x + wub) {
 487             x -= wub; 
 488         }
 489         if ((long)y + wub != y + wub) {
 490             y -= wub;
 491         }
 492         if ((long)x - wub != x - wub) {
 493             x += wub; 
 494         }
 495         if ((long)y - wub != y - wub) {
 496             y += wub;
 497         }
 498         coords[0] = x;
 499         coords[1] = y;
 500     }
 501 
 502     public void moveTo(int x0, int y0) {
 503         // System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
 504 
 505         // We can't move too close to the borders set by Integer.MAX_VALUE or 
 506         // MIN_VALUE, because when we try to draw an endcap, or even just adding
 507         // offsets to (x0,y0), overflows might happen.
 508         int[] coords = {x0, y0};
 509         makeOverflowSafe(coords, wub);
 510         x0 = coords[0]; y0 = coords[1];
 511 
 512         if (lineToOrigin) {
 513             // not closing the path, do the previous lineTo
 514             lineToImpl(sx0, sy0, joinToOrigin);
 515             lineToOrigin = false;
 516         }
 517 
 518         if (prev == LINE_TO) {
 519             finish();
 520         }
 521 
 522         this.sx0 = this.x0 = x0;
 523         this.sy0 = this.y0 = y0;
 524         this.rindex = 0;
 525         this.started = false;
 526         this.joinSegment = false;
 527         this.prev = MOVE_TO;
 528     }
 529 
 530     boolean joinSegment = false;
 531 


 546             // not closing the path, do the previous lineTo
 547             lineToImpl(sx0, sy0, joinToOrigin);
 548             lineToOrigin = false;
 549         } else if (x1 == x0 && y1 == y0) {
 550             return;
 551         } else if (x1 == sx0 && y1 == sy0) {
 552             lineToOrigin = true;
 553             joinToOrigin = joinSegment;
 554             joinSegment = false;
 555             return;
 556         }
 557 
 558         lineToImpl(x1, y1, joinSegment);
 559         joinSegment = false;
 560     }
 561 
 562     private void lineToImpl(int x1, int y1, boolean joinSegment) {
 563         computeOffset(x0, y0, x1, y1, offset);
 564         int mx = offset[0];
 565         int my = offset[1];
 566 
 567         // Now we want to make sure that none of the operations involving line
 568         // drawing will overflow. These include adding pen points to (x1,y1),
 569         // drawing a square end cap, and adding/subtracting mx and my to x1 
 570         // and y1 (in the calls to emitLineTo). However, it does not protect
 571         // against possible overflows when drawing miters.
 572         // TODO: protect against possible overflows when drawing miters.
 573         int[] coords = {x1, y1};
 574         makeOverflowSafe(coords, wub);
 575         x1 = coords[0]; y1 = coords[1];
 576 
 577         if (!started) {
 578             emitMoveTo(x0 + mx, y0 + my);
 579             this.sx1 = x1;
 580             this.sy1 = y1;
 581             this.mx0 = mx;
 582             this.my0 = my;
 583             started = true;
 584         } else {
 585             boolean ccw = isCCW(px0, py0, x0, y0, x1, y1);
 586             if (joinSegment) {
 587                 if (joinStyle == JOIN_MITER) {
 588                     drawMiter(px0, py0, x0, y0, x1, y1, omx, omy, mx, my,
 589                               ccw);
 590                 } else if (joinStyle == JOIN_ROUND) {
 591                     drawRoundJoin(x0, y0,
 592                                   omx, omy,
 593                                   mx, my, 0, false, ccw,
 594                                   ROUND_JOIN_THRESHOLD);
 595                 }