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.awt.geom.AffineTransform; 29 import java.awt.geom.Path2D; 30 31 final class DTransformingPathConsumer2D { 32 33 DTransformingPathConsumer2D() { 34 // used by DRendererContext 35 } 36 37 // recycled DPathConsumer2D instance from wrapPath2d() 38 private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); 39 40 DPathConsumer2D wrapPath2d(Path2D.Double p2d) 41 { 42 return wp_Path2DWrapper.init(p2d); 43 } 44 45 // recycled DPathConsumer2D instances from deltaTransformConsumer() 46 private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); 47 private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); 48 49 DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, 50 AffineTransform at) 51 { 52 if (at == null) { 53 return out; 54 } 55 double mxx = at.getScaleX(); 56 double mxy = at.getShearX(); 57 double myx = at.getShearY(); 58 double myy = at.getScaleY(); 59 60 if (mxy == 0.0d && myx == 0.0d) { 61 if (mxx == 1.0d && myy == 1.0d) { 62 return out; 63 } else { 64 return dt_DeltaScaleFilter.init(out, mxx, myy); 65 } 66 } else { 67 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy); 68 } 69 } 70 71 // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer() 72 private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter(); 73 private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter(); 74 75 DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, 76 AffineTransform at) 77 { 78 if (at == null) { 79 return out; 80 } 81 double mxx = at.getScaleX(); 82 double mxy = at.getShearX(); 83 double myx = at.getShearY(); 84 double myy = at.getScaleY(); 85 86 if (mxy == 0.0d && myx == 0.0d) { 87 if (mxx == 1.0d && myy == 1.0d) { 88 return out; 89 } else { 90 return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy); 91 } 92 } else { 93 double det = mxx * myy - mxy * myx; 94 return iv_DeltaTransformFilter.init(out, 95 myy / det, 96 -mxy / det, 97 -myx / det, 98 mxx / det); 99 } 100 } 101 102 103 static final class DeltaScaleFilter implements DPathConsumer2D { 104 private DPathConsumer2D out; 105 private double sx, sy; 106 107 DeltaScaleFilter() {} 108 109 DeltaScaleFilter init(DPathConsumer2D out, 110 double mxx, double myy) 111 { 112 this.out = out; 113 sx = mxx; 114 sy = myy; 115 return this; // fluent API 116 } 117 118 @Override 119 public void moveTo(double x0, double y0) { 120 out.moveTo(x0 * sx, y0 * sy); 121 } 122 250 251 @Override 252 public void closePath() { 253 p2d.closePath(); 254 } 255 256 @Override 257 public void pathDone() {} 258 259 @Override 260 public void curveTo(double x1, double y1, 261 double x2, double y2, 262 double x3, double y3) 263 { 264 p2d.curveTo(x1, y1, x2, y2, x3, y3); 265 } 266 267 @Override 268 public void quadTo(double x1, double y1, double x2, double y2) { 269 p2d.quadTo(x1, y1, x2, y2); 270 } 271 272 @Override 273 public long getNativeConsumer() { 274 throw new InternalError("Not using a native peer"); 275 } 276 } 277 } | 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.awt.geom.AffineTransform; 29 import java.awt.geom.Path2D; 30 import sun.java2d.marlin.DHelpers.IndexStack; 31 import sun.java2d.marlin.DHelpers.PolyStack; 32 33 final class DTransformingPathConsumer2D { 34 35 private final DRendererContext rdrCtx; 36 37 // recycled ClosedPathDetector instance from detectClosedPath() 38 private final ClosedPathDetector cpDetector; 39 40 // recycled PathClipFilter instance from pathClipper() 41 private final PathClipFilter pathClipper; 42 43 // recycled DPathConsumer2D instance from wrapPath2D() 44 private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); 45 46 // recycled DPathConsumer2D instances from deltaTransformConsumer() 47 private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); 48 private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); 49 50 // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer() 51 private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter(); 52 private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter(); 53 54 // recycled PathTracer instances from tracer...() methods 55 private final PathTracer tracerInput = new PathTracer("[Input]"); 56 private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector"); 57 private final PathTracer tracerFiller = new PathTracer("Filler"); 58 private final PathTracer tracerStroker = new PathTracer("Stroker"); 59 60 DTransformingPathConsumer2D(final DRendererContext rdrCtx) { 61 // used by RendererContext 62 this.rdrCtx = rdrCtx; 63 this.cpDetector = new ClosedPathDetector(rdrCtx); 64 this.pathClipper = new PathClipFilter(rdrCtx); 65 } 66 67 DPathConsumer2D wrapPath2D(Path2D.Double p2d) { 68 return wp_Path2DWrapper.init(p2d); 69 } 70 71 DPathConsumer2D traceInput(DPathConsumer2D out) { 72 return tracerInput.init(out); 73 } 74 75 DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) { 76 return tracerCPDetector.init(out); 77 } 78 79 DPathConsumer2D traceFiller(DPathConsumer2D out) { 80 return tracerFiller.init(out); 81 } 82 83 DPathConsumer2D traceStroker(DPathConsumer2D out) { 84 return tracerStroker.init(out); 85 } 86 87 DPathConsumer2D detectClosedPath(DPathConsumer2D out) { 88 return cpDetector.init(out); 89 } 90 91 DPathConsumer2D pathClipper(DPathConsumer2D out) { 92 return pathClipper.init(out); 93 } 94 95 DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, 96 AffineTransform at) 97 { 98 if (at == null) { 99 return out; 100 } 101 final double mxx = at.getScaleX(); 102 final double mxy = at.getShearX(); 103 final double myx = at.getShearY(); 104 final double myy = at.getScaleY(); 105 106 if (mxy == 0.0d && myx == 0.0d) { 107 if (mxx == 1.0d && myy == 1.0d) { 108 return out; 109 } else { 110 // Scale only 111 if (rdrCtx.doClip) { 112 // adjust clip rectangle (ymin, ymax, xmin, xmax): 113 adjustClipScale(rdrCtx.clipRect, mxx, myy); 114 } 115 return dt_DeltaScaleFilter.init(out, mxx, myy); 116 } 117 } else { 118 if (rdrCtx.doClip) { 119 // adjust clip rectangle (ymin, ymax, xmin, xmax): 120 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy); 121 } 122 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy); 123 } 124 } 125 126 private static void adjustClipOffset(final double[] clipRect) { 127 clipRect[0] += Renderer.RDR_OFFSET_Y; 128 clipRect[1] += Renderer.RDR_OFFSET_Y; 129 clipRect[2] += Renderer.RDR_OFFSET_X; 130 clipRect[3] += Renderer.RDR_OFFSET_X; 131 } 132 133 private static void adjustClipScale(final double[] clipRect, 134 final double mxx, final double myy) 135 { 136 adjustClipOffset(clipRect); 137 138 // Adjust the clipping rectangle (iv_DeltaScaleFilter): 139 clipRect[0] /= myy; 140 clipRect[1] /= myy; 141 clipRect[2] /= mxx; 142 clipRect[3] /= mxx; 143 } 144 145 private static void adjustClipInverseDelta(final double[] clipRect, 146 final double mxx, final double mxy, 147 final double myx, final double myy) 148 { 149 adjustClipOffset(clipRect); 150 151 // Adjust the clipping rectangle (iv_DeltaTransformFilter): 152 final double det = mxx * myy - mxy * myx; 153 final double imxx = myy / det; 154 final double imxy = -mxy / det; 155 final double imyx = -myx / det; 156 final double imyy = mxx / det; 157 158 double xmin, xmax, ymin, ymax; 159 double x, y; 160 // xmin, ymin: 161 x = clipRect[2] * imxx + clipRect[0] * imxy; 162 y = clipRect[2] * imyx + clipRect[0] * imyy; 163 164 xmin = xmax = x; 165 ymin = ymax = y; 166 167 // xmax, ymin: 168 x = clipRect[3] * imxx + clipRect[0] * imxy; 169 y = clipRect[3] * imyx + clipRect[0] * imyy; 170 171 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } 172 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } 173 174 // xmin, ymax: 175 x = clipRect[2] * imxx + clipRect[1] * imxy; 176 y = clipRect[2] * imyx + clipRect[1] * imyy; 177 178 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } 179 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } 180 181 // xmax, ymax: 182 x = clipRect[3] * imxx + clipRect[1] * imxy; 183 y = clipRect[3] * imyx + clipRect[1] * imyy; 184 185 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } 186 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } 187 188 clipRect[0] = ymin; 189 clipRect[1] = ymax; 190 clipRect[2] = xmin; 191 clipRect[3] = xmax; 192 } 193 194 DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, 195 AffineTransform at) 196 { 197 if (at == null) { 198 return out; 199 } 200 double mxx = at.getScaleX(); 201 double mxy = at.getShearX(); 202 double myx = at.getShearY(); 203 double myy = at.getScaleY(); 204 205 if (mxy == 0.0d && myx == 0.0d) { 206 if (mxx == 1.0d && myy == 1.0d) { 207 return out; 208 } else { 209 return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy); 210 } 211 } else { 212 final double det = mxx * myy - mxy * myx; 213 return iv_DeltaTransformFilter.init(out, 214 myy / det, 215 -mxy / det, 216 -myx / det, 217 mxx / det); 218 } 219 } 220 221 static final class DeltaScaleFilter implements DPathConsumer2D { 222 private DPathConsumer2D out; 223 private double sx, sy; 224 225 DeltaScaleFilter() {} 226 227 DeltaScaleFilter init(DPathConsumer2D out, 228 double mxx, double myy) 229 { 230 this.out = out; 231 sx = mxx; 232 sy = myy; 233 return this; // fluent API 234 } 235 236 @Override 237 public void moveTo(double x0, double y0) { 238 out.moveTo(x0 * sx, y0 * sy); 239 } 240 368 369 @Override 370 public void closePath() { 371 p2d.closePath(); 372 } 373 374 @Override 375 public void pathDone() {} 376 377 @Override 378 public void curveTo(double x1, double y1, 379 double x2, double y2, 380 double x3, double y3) 381 { 382 p2d.curveTo(x1, y1, x2, y2, x3, y3); 383 } 384 385 @Override 386 public void quadTo(double x1, double y1, double x2, double y2) { 387 p2d.quadTo(x1, y1, x2, y2); 388 } 389 390 @Override 391 public long getNativeConsumer() { 392 throw new InternalError("Not using a native peer"); 393 } 394 } 395 396 static final class ClosedPathDetector implements DPathConsumer2D { 397 398 private final DRendererContext rdrCtx; 399 private final PolyStack stack; 400 401 private DPathConsumer2D out; 402 403 ClosedPathDetector(final DRendererContext rdrCtx) { 404 this.rdrCtx = rdrCtx; 405 this.stack = (rdrCtx.stats != null) ? 406 new PolyStack(rdrCtx, 407 rdrCtx.stats.stat_cpd_polystack_types, 408 rdrCtx.stats.stat_cpd_polystack_curves, 409 rdrCtx.stats.hist_cpd_polystack_curves, 410 rdrCtx.stats.stat_array_cpd_polystack_curves, 411 rdrCtx.stats.stat_array_cpd_polystack_types) 412 : new PolyStack(rdrCtx); 413 } 414 415 ClosedPathDetector init(DPathConsumer2D out) { 416 this.out = out; 417 return this; // fluent API 418 } 419 420 /** 421 * Disposes this instance: 422 * clean up before reusing this instance 423 */ 424 void dispose() { 425 stack.dispose(); 426 } 427 428 @Override 429 public void pathDone() { 430 // previous path is not closed: 431 finish(false); 432 out.pathDone(); 433 434 // TODO: fix possible leak if exception happened 435 // Dispose this instance: 436 dispose(); 437 } 438 439 @Override 440 public void closePath() { 441 // path is closed 442 finish(true); 443 out.closePath(); 444 } 445 446 @Override 447 public void moveTo(double x0, double y0) { 448 // previous path is not closed: 449 finish(false); 450 out.moveTo(x0, y0); 451 } 452 453 private void finish(final boolean closed) { 454 rdrCtx.closedPath = closed; 455 stack.pullAll(out); 456 } 457 458 @Override 459 public void lineTo(double x1, double y1) { 460 stack.pushLine(x1, y1); 461 } 462 463 @Override 464 public void curveTo(double x3, double y3, 465 double x2, double y2, 466 double x1, double y1) 467 { 468 stack.pushCubic(x1, y1, x2, y2, x3, y3); 469 } 470 471 @Override 472 public void quadTo(double x2, double y2, double x1, double y1) { 473 stack.pushQuad(x1, y1, x2, y2); 474 } 475 476 @Override 477 public long getNativeConsumer() { 478 throw new InternalError("Not using a native peer"); 479 } 480 } 481 482 static final class PathClipFilter implements DPathConsumer2D { 483 484 private DPathConsumer2D out; 485 486 // Bounds of the drawing region, at pixel precision. 487 private final double[] clipRect; 488 489 private final double[] corners = new double[8]; 490 private boolean init_corners = false; 491 492 private final IndexStack stack; 493 494 // the current outcode of the current sub path 495 private int cOutCode = 0; 496 497 // the cumulated (and) outcode of the complete path 498 private int gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R; 499 500 private boolean outside = false; 501 502 // The current point OUTSIDE 503 private double cx0, cy0; 504 505 PathClipFilter(final DRendererContext rdrCtx) { 506 this.clipRect = rdrCtx.clipRect; 507 this.stack = (rdrCtx.stats != null) ? 508 new IndexStack(rdrCtx, 509 rdrCtx.stats.stat_pcf_idxstack_indices, 510 rdrCtx.stats.hist_pcf_idxstack_indices, 511 rdrCtx.stats.stat_array_pcf_idxstack_indices) 512 : new IndexStack(rdrCtx); 513 } 514 515 PathClipFilter init(final DPathConsumer2D out) { 516 this.out = out; 517 518 // Adjust the clipping rectangle with the renderer offsets 519 final double rdrOffX = DRenderer.RDR_OFFSET_X; 520 final double rdrOffY = DRenderer.RDR_OFFSET_Y; 521 522 // add a small rounding error: 523 final double margin = 1e-3d; 524 525 final double[] _clipRect = this.clipRect; 526 _clipRect[0] -= margin - rdrOffY; 527 _clipRect[1] += margin + rdrOffY; 528 _clipRect[2] -= margin - rdrOffX; 529 _clipRect[3] += margin + rdrOffX; 530 531 this.init_corners = true; 532 this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R; 533 534 return this; // fluent API 535 } 536 537 /** 538 * Disposes this instance: 539 * clean up before reusing this instance 540 */ 541 void dispose() { 542 stack.dispose(); 543 } 544 545 private void finishPath() { 546 if (outside) { 547 // criteria: inside or totally outside ? 548 if (gOutCode == 0) { 549 finish(); 550 } else { 551 this.outside = false; 552 stack.reset(); 553 } 554 } 555 } 556 557 private void finish() { 558 this.outside = false; 559 560 if (!stack.isEmpty()) { 561 if (init_corners) { 562 init_corners = false; 563 564 final double[] _corners = corners; 565 final double[] _clipRect = clipRect; 566 // Top Left (0): 567 _corners[0] = _clipRect[2]; 568 _corners[1] = _clipRect[0]; 569 // Bottom Left (1): 570 _corners[2] = _clipRect[2]; 571 _corners[3] = _clipRect[1]; 572 // Top right (2): 573 _corners[4] = _clipRect[3]; 574 _corners[5] = _clipRect[0]; 575 // Bottom Right (3): 576 _corners[6] = _clipRect[3]; 577 _corners[7] = _clipRect[1]; 578 } 579 stack.pullAll(corners, out); 580 } 581 out.lineTo(cx0, cy0); 582 } 583 584 @Override 585 public void pathDone() { 586 finishPath(); 587 588 out.pathDone(); 589 590 // TODO: fix possible leak if exception happened 591 // Dispose this instance: 592 dispose(); 593 } 594 595 @Override 596 public void closePath() { 597 finishPath(); 598 599 out.closePath(); 600 } 601 602 @Override 603 public void moveTo(final double x0, final double y0) { 604 finishPath(); 605 606 final int outcode = DHelpers.outcode(x0, y0, clipRect); 607 this.cOutCode = outcode; 608 this.outside = false; 609 out.moveTo(x0, y0); 610 } 611 612 @Override 613 public void lineTo(final double xe, final double ye) { 614 final int outcode0 = this.cOutCode; 615 final int outcode1 = DHelpers.outcode(xe, ye, clipRect); 616 this.cOutCode = outcode1; 617 618 final int sideCode = (outcode0 & outcode1); 619 620 // basic rejection criteria: 621 if (sideCode == 0) { 622 this.gOutCode = 0; 623 } else { 624 this.gOutCode &= sideCode; 625 // keep last point coordinate before entering the clip again: 626 this.outside = true; 627 this.cx0 = xe; 628 this.cy0 = ye; 629 630 clip(sideCode, outcode0, outcode1); 631 return; 632 } 633 if (outside) { 634 finish(); 635 } 636 // clipping disabled: 637 out.lineTo(xe, ye); 638 } 639 640 private void clip(final int sideCode, 641 final int outcode0, 642 final int outcode1) 643 { 644 // corner or cross-boundary on left or right side: 645 if ((outcode0 != outcode1) 646 && ((sideCode & MarlinConst.OUTCODE_MASK_L_R) != 0)) 647 { 648 // combine outcodes: 649 final int mergeCode = (outcode0 | outcode1); 650 final int tbCode = mergeCode & MarlinConst.OUTCODE_MASK_T_B; 651 final int lrCode = mergeCode & MarlinConst.OUTCODE_MASK_L_R; 652 final int off = (lrCode == MarlinConst.OUTCODE_LEFT) ? 0 : 2; 653 654 // add corners to outside stack: 655 switch (tbCode) { 656 case MarlinConst.OUTCODE_TOP: 657 // System.out.println("TOP "+ ((off == 0) ? "LEFT" : "RIGHT")); 658 stack.push(off); // top 659 return; 660 case MarlinConst.OUTCODE_BOTTOM: 661 // System.out.println("BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT")); 662 stack.push(off + 1); // bottom 663 return; 664 default: 665 // both TOP / BOTTOM: 666 if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) { 667 // System.out.println("TOP + BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT")); 668 // top to bottom 669 stack.push(off); // top 670 stack.push(off + 1); // bottom 671 } else { 672 // System.out.println("BOTTOM + TOP "+ ((off == 0) ? "LEFT" : "RIGHT")); 673 // bottom to top 674 stack.push(off + 1); // bottom 675 stack.push(off); // top 676 } 677 } 678 } 679 } 680 681 @Override 682 public void curveTo(final double x1, final double y1, 683 final double x2, final double y2, 684 final double xe, final double ye) 685 { 686 final int outcode0 = this.cOutCode; 687 final int outcode3 = DHelpers.outcode(xe, ye, clipRect); 688 this.cOutCode = outcode3; 689 690 int sideCode = outcode0 & outcode3; 691 692 if (sideCode == 0) { 693 this.gOutCode = 0; 694 } else { 695 sideCode &= DHelpers.outcode(x1, y1, clipRect); 696 sideCode &= DHelpers.outcode(x2, y2, clipRect); 697 this.gOutCode &= sideCode; 698 699 // basic rejection criteria: 700 if (sideCode != 0) { 701 // keep last point coordinate before entering the clip again: 702 this.outside = true; 703 this.cx0 = xe; 704 this.cy0 = ye; 705 706 clip(sideCode, outcode0, outcode3); 707 return; 708 } 709 } 710 if (outside) { 711 finish(); 712 } 713 // clipping disabled: 714 out.curveTo(x1, y1, x2, y2, xe, ye); 715 } 716 717 @Override 718 public void quadTo(final double x1, final double y1, 719 final double xe, final double ye) 720 { 721 final int outcode0 = this.cOutCode; 722 final int outcode2 = DHelpers.outcode(xe, ye, clipRect); 723 this.cOutCode = outcode2; 724 725 int sideCode = outcode0 & outcode2; 726 727 if (sideCode == 0) { 728 this.gOutCode = 0; 729 } else { 730 sideCode &= DHelpers.outcode(x1, y1, clipRect); 731 this.gOutCode &= sideCode; 732 733 // basic rejection criteria: 734 if (sideCode != 0) { 735 // keep last point coordinate before entering the clip again: 736 this.outside = true; 737 this.cx0 = xe; 738 this.cy0 = ye; 739 740 clip(sideCode, outcode0, outcode2); 741 return; 742 } 743 } 744 if (outside) { 745 finish(); 746 } 747 // clipping disabled: 748 out.quadTo(x1, y1, xe, ye); 749 } 750 751 @Override 752 public long getNativeConsumer() { 753 throw new InternalError("Not using a native peer"); 754 } 755 } 756 757 static final class PathTracer implements DPathConsumer2D { 758 private final String prefix; 759 private DPathConsumer2D out; 760 761 PathTracer(String name) { 762 this.prefix = name + ": "; 763 } 764 765 PathTracer init(DPathConsumer2D out) { 766 this.out = out; 767 return this; // fluent API 768 } 769 770 @Override 771 public void moveTo(double x0, double y0) { 772 log("moveTo (" + x0 + ", " + y0 + ')'); 773 out.moveTo(x0, y0); 774 } 775 776 @Override 777 public void lineTo(double x1, double y1) { 778 log("lineTo (" + x1 + ", " + y1 + ')'); 779 out.lineTo(x1, y1); 780 } 781 782 @Override 783 public void curveTo(double x1, double y1, 784 double x2, double y2, 785 double x3, double y3) 786 { 787 log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ')'); 788 out.curveTo(x1, y1, x2, y2, x3, y3); 789 } 790 791 @Override 792 public void quadTo(double x1, double y1, double x2, double y2) { 793 log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ')'); 794 out.quadTo(x1, y1, x2, y2); 795 } 796 797 @Override 798 public void closePath() { 799 log("closePath"); 800 out.closePath(); 801 } 802 803 @Override 804 public void pathDone() { 805 log("pathDone"); 806 out.pathDone(); 807 } 808 809 private void log(final String message) { 810 System.out.println(prefix + message); 811 } 812 813 @Override 814 public long getNativeConsumer() { 815 throw new InternalError("Not using a native peer"); 816 } 817 } 818 } |