25 |
25 |
26 package sun.java2d.marlin; |
26 package sun.java2d.marlin; |
27 |
27 |
28 import java.awt.geom.AffineTransform; |
28 import java.awt.geom.AffineTransform; |
29 import java.awt.geom.Path2D; |
29 import java.awt.geom.Path2D; |
|
30 import sun.java2d.marlin.DHelpers.IndexStack; |
|
31 import sun.java2d.marlin.DHelpers.PolyStack; |
30 |
32 |
31 final class DTransformingPathConsumer2D { |
33 final class DTransformingPathConsumer2D { |
32 |
34 |
33 DTransformingPathConsumer2D() { |
35 private final DRendererContext rdrCtx; |
34 // used by DRendererContext |
36 |
35 } |
37 // recycled ClosedPathDetector instance from detectClosedPath() |
36 |
38 private final ClosedPathDetector cpDetector; |
37 // recycled DPathConsumer2D instance from wrapPath2d() |
39 |
|
40 // recycled PathClipFilter instance from pathClipper() |
|
41 private final PathClipFilter pathClipper; |
|
42 |
|
43 // recycled DPathConsumer2D instance from wrapPath2D() |
38 private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); |
44 private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); |
39 |
|
40 DPathConsumer2D wrapPath2d(Path2D.Double p2d) |
|
41 { |
|
42 return wp_Path2DWrapper.init(p2d); |
|
43 } |
|
44 |
45 |
45 // recycled DPathConsumer2D instances from deltaTransformConsumer() |
46 // recycled DPathConsumer2D instances from deltaTransformConsumer() |
46 private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); |
47 private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); |
47 private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); |
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 } |
48 |
94 |
49 DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, |
95 DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, |
50 AffineTransform at) |
96 AffineTransform at) |
51 { |
97 { |
52 if (at == null) { |
98 if (at == null) { |
53 return out; |
99 return out; |
54 } |
100 } |
55 double mxx = at.getScaleX(); |
101 final double mxx = at.getScaleX(); |
56 double mxy = at.getShearX(); |
102 final double mxy = at.getShearX(); |
57 double myx = at.getShearY(); |
103 final double myx = at.getShearY(); |
58 double myy = at.getScaleY(); |
104 final double myy = at.getScaleY(); |
59 |
105 |
60 if (mxy == 0.0d && myx == 0.0d) { |
106 if (mxy == 0.0d && myx == 0.0d) { |
61 if (mxx == 1.0d && myy == 1.0d) { |
107 if (mxx == 1.0d && myy == 1.0d) { |
62 return out; |
108 return out; |
63 } else { |
109 } else { |
|
110 // Scale only |
|
111 if (rdrCtx.doClip) { |
|
112 // adjust clip rectangle (ymin, ymax, xmin, xmax): |
|
113 adjustClipScale(rdrCtx.clipRect, mxx, myy); |
|
114 } |
64 return dt_DeltaScaleFilter.init(out, mxx, myy); |
115 return dt_DeltaScaleFilter.init(out, mxx, myy); |
65 } |
116 } |
66 } else { |
117 } else { |
|
118 if (rdrCtx.doClip) { |
|
119 // adjust clip rectangle (ymin, ymax, xmin, xmax): |
|
120 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy); |
|
121 } |
67 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy); |
122 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy); |
68 } |
123 } |
69 } |
124 } |
70 |
125 |
71 // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer() |
126 private static void adjustClipOffset(final double[] clipRect) { |
72 private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter(); |
127 clipRect[0] += Renderer.RDR_OFFSET_Y; |
73 private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter(); |
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 } |
74 |
193 |
75 DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, |
194 DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, |
76 AffineTransform at) |
195 AffineTransform at) |
77 { |
196 { |
78 if (at == null) { |
197 if (at == null) { |
272 @Override |
390 @Override |
273 public long getNativeConsumer() { |
391 public long getNativeConsumer() { |
274 throw new InternalError("Not using a native peer"); |
392 throw new InternalError("Not using a native peer"); |
275 } |
393 } |
276 } |
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 } |
277 } |
818 } |