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