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 } |