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