001: /*
002: *
003: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025:
026: package com.sun.pisces;
027:
028: public class Stroker extends LineSink {
029:
030: private static final int MOVE_TO = 0;
031: private static final int LINE_TO = 1;
032: private static final int CLOSE = 2;
033:
034: /**
035: * Constant value for join style.
036: */
037: public static final int JOIN_MITER = 0;
038:
039: /**
040: * Constant value for join style.
041: */
042: public static final int JOIN_ROUND = 1;
043:
044: /**
045: * Constant value for join style.
046: */
047: public static final int JOIN_BEVEL = 2;
048:
049: /**
050: * Constant value for end cap style.
051: */
052: public static final int CAP_BUTT = 0;
053:
054: /**
055: * Constant value for end cap style.
056: */
057: public static final int CAP_ROUND = 1;
058:
059: /**
060: * Constant value for end cap style.
061: */
062: public static final int CAP_SQUARE = 2;
063:
064: LineSink output;
065:
066: int lineWidth;
067: int capStyle;
068: int joinStyle;
069: int miterLimit;
070:
071: Transform4 transform;
072: int m00, m01;
073: int m10, m11;
074:
075: int lineWidth2;
076: long scaledLineWidth2;
077:
078: // For any pen offset (pen_dx, pen_dy) that does not depend on
079: // the line orientation, the pen should be transformed so that:
080: //
081: // pen_dx' = m00*pen_dx + m01*pen_dy
082: // pen_dy' = m10*pen_dx + m11*pen_dy
083: //
084: // For a round pen, this means:
085: //
086: // pen_dx(r, theta) = r*cos(theta)
087: // pen_dy(r, theta) = r*sin(theta)
088: //
089: // pen_dx'(r, theta) = r*(m00*cos(theta) + m01*sin(theta))
090: // pen_dy'(r, theta) = r*(m10*cos(theta) + m11*sin(theta))
091: int numPenSegments;
092: int[] pen_dx;
093: int[] pen_dy;
094: boolean[] penIncluded;
095: int[] join;
096:
097: int[] offset = new int[2];
098: int[] reverse = new int[100];
099: 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: /**
125: * Constructs a <code>Stroker</code>.
126: *
127: * @param output an output <code>LineSink</code>.
128: * @param lineWidth the desired line width in pixels, in S15.16
129: * format.
130: * @param capStyle the desired end cap style, one of
131: * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
132: * <code>CAP_SQUARE</code>.
133: * @param joinStyle the desired line join style, one of
134: * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
135: * <code>JOIN_BEVEL</code>.
136: * @param miterLimit the desired miter limit, in S15.16 format.
137: * @param transform a <code>Transform4</code> object indicating
138: * the transform that has been previously applied to all incoming
139: * coordinates. This is required in order to produce consistently
140: * shaped end caps and joins.
141: */
142: public Stroker(LineSink output, int lineWidth, int capStyle,
143: int joinStyle, int miterLimit, Transform4 transform) {
144: setOutput(output);
145: setParameters(lineWidth, capStyle, joinStyle, miterLimit,
146: transform);
147: }
148:
149: /**
150: * Sets the output <code>LineSink</code> of this
151: * <code>Stroker</code>.
152: *
153: * @param output an output <code>LineSink</code>.
154: */
155: public void setOutput(LineSink output) {
156: this .output = output;
157: }
158:
159: /**
160: * Sets the parameters of this <code>Stroker</code>.
161: * @param lineWidth the desired line width in pixels, in S15.16
162: * format.
163: * @param capStyle the desired end cap style, one of
164: * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
165: * <code>CAP_SQUARE</code>.
166: * @param joinStyle the desired line join style, one of
167: * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
168: * <code>JOIN_BEVEL</code>.
169: * @param miterLimit the desired miter limit, in S15.16 format.
170: * @param transform a <code>Transform4</code> object indicating
171: * the transform that has been previously applied to all incoming
172: * coordinates. This is required in order to produce consistently
173: * shaped end caps and joins.
174: */
175: public void setParameters(int lineWidth, int capStyle,
176: int joinStyle, int miterLimit, Transform4 transform) {
177: this .lineWidth = lineWidth;
178: this .lineWidth2 = lineWidth >> 1;
179: this .scaledLineWidth2 = (long) transform.m00 * lineWidth2;
180: this .capStyle = capStyle;
181: this .joinStyle = joinStyle;
182: this .miterLimit = miterLimit;
183:
184: this .transform = transform;
185: this .m00 = transform.m00;
186: this .m01 = transform.m01;
187: this .m10 = transform.m10;
188: this .m11 = transform.m11;
189:
190: this .m00_2_m01_2 = (double) m00 * m00 + (double) m01 * m01;
191: this .m10_2_m11_2 = (double) m10 * m10 + (double) m11 * m11;
192: this .m00_m10_m01_m11 = (double) m00 * m10 + (double) m01 * m11;
193:
194: double dm00 = m00 / 65536.0;
195: double dm01 = m01 / 65536.0;
196: double dm10 = m10 / 65536.0;
197: double dm11 = m11 / 65536.0;
198: double determinant = dm00 * dm11 - dm01 * dm10;
199:
200: if (joinStyle == JOIN_MITER) {
201: double limit = (miterLimit / 65536.0)
202: * (lineWidth2 / 65536.0) * determinant;
203: double limitSq = limit * limit;
204: this .miterLimitSq = (long) (limitSq * 65536.0 * 65536.0);
205: }
206:
207: this .numPenSegments = (int) (3.14159f * lineWidth / 65536.0f);
208: if (pen_dx == null || pen_dx.length < numPenSegments) {
209: this .pen_dx = new int[numPenSegments];
210: this .pen_dy = new int[numPenSegments];
211: this .penIncluded = new boolean[numPenSegments];
212: this .join = new int[2 * numPenSegments];
213: }
214:
215: for (int i = 0; i < numPenSegments; i++) {
216: double r = lineWidth / 2.0;
217: double theta = (double) i * 2.0 * Math.PI / numPenSegments;
218:
219: double cos = Math.cos(theta);
220: double sin = Math.sin(theta);
221: pen_dx[i] = (int) (r * (dm00 * cos + dm01 * sin));
222: pen_dy[i] = (int) (r * (dm10 * cos + dm11 * sin));
223: }
224:
225: prev = CLOSE;
226: rindex = 0;
227: started = false;
228: lineToOrigin = false;
229: }
230:
231: private void computeOffset(int x0, int y0, int x1, int y1, int[] m) {
232: long lx = (long) x1 - (long) x0;
233: long ly = (long) y1 - (long) y0;
234:
235: int dx, dy;
236: if (m00 > 0 && m00 == m11 && m01 == 0 & m10 == 0) {
237: long ilen = PiscesMath.hypot(lx, ly);
238: if (ilen == 0) {
239: dx = dy = 0;
240: } else {
241: dx = (int) ((ly * scaledLineWidth2) / ilen >> 16);
242: dy = (int) (-(lx * scaledLineWidth2) / ilen >> 16);
243: }
244: } else {
245: double dlx = x1 - x0;
246: double dly = y1 - y0;
247: double det = (double) m00 * m11 - (double) m01 * m10;
248: int sdet = (det > 0) ? 1 : -1;
249: double a = dly * m00 - dlx * m10;
250: double b = dly * m01 - dlx * m11;
251: double dh = PiscesMath.hypot(a, b);
252: double div = sdet * lineWidth2 / (65536.0 * dh);
253: double ddx = dly * m00_2_m01_2 - dlx * m00_m10_m01_m11;
254: double ddy = dly * m00_m10_m01_m11 - dlx * m10_2_m11_2;
255: dx = (int) (ddx * div);
256: dy = (int) (ddy * div);
257: }
258:
259: m[0] = dx;
260: m[1] = dy;
261: }
262:
263: private void ensureCapacity(int newrindex) {
264: if (reverse.length < newrindex) {
265: int[] tmp = new int[Math.max(newrindex,
266: 6 * reverse.length / 5)];
267: System.arraycopy(reverse, 0, tmp, 0, rindex);
268: this .reverse = tmp;
269: }
270: }
271:
272: private boolean isCCW(int x0, int y0, int x1, int y1, int x2, int y2) {
273: int dx0 = x1 - x0;
274: int dy0 = y1 - y0;
275: int dx1 = x2 - x1;
276: int dy1 = y2 - y1;
277: return (long) dx0 * dy1 < (long) dy0 * dx1;
278: }
279:
280: private boolean side(int x, int y, int x0, int y0, int x1, int y1) {
281: long lx = x;
282: long ly = y;
283: long lx0 = x0;
284: long ly0 = y0;
285: long lx1 = x1;
286: long ly1 = y1;
287:
288: return (ly0 - ly1) * lx + (lx1 - lx0) * ly
289: + (lx0 * ly1 - lx1 * ly0) > 0;
290: }
291:
292: private int computeRoundJoin(int cx, int cy, int xa, int ya,
293: int xb, int yb, int side, boolean flip, int[] join) {
294: int px, py;
295: int ncoords = 0;
296:
297: boolean centerSide;
298: if (side == 0) {
299: centerSide = side(cx, cy, xa, ya, xb, yb);
300: } else {
301: centerSide = (side == 1) ? true : false;
302: }
303: for (int i = 0; i < numPenSegments; i++) {
304: px = cx + pen_dx[i];
305: py = cy + pen_dy[i];
306:
307: boolean penSide = side(px, py, xa, ya, xb, yb);
308: if (penSide != centerSide) {
309: penIncluded[i] = true;
310: } else {
311: penIncluded[i] = false;
312: }
313: }
314:
315: int start = -1, end = -1;
316: for (int i = 0; i < numPenSegments; i++) {
317: if (penIncluded[i]
318: && !penIncluded[(i + numPenSegments - 1)
319: % numPenSegments]) {
320: start = i;
321: }
322: if (penIncluded[i]
323: && !penIncluded[(i + 1) % numPenSegments]) {
324: end = i;
325: }
326: }
327:
328: if (end < start) {
329: end += numPenSegments;
330: }
331:
332: if (start != -1 && end != -1) {
333: long dxa = cx + pen_dx[start] - xa;
334: long dya = cy + pen_dy[start] - ya;
335: long dxb = cx + pen_dx[start] - xb;
336: long dyb = cy + pen_dy[start] - yb;
337:
338: boolean rev = (dxa * dxa + dya * dya > dxb * dxb + dyb
339: * dyb);
340: int i = rev ? end : start;
341: int incr = rev ? -1 : 1;
342: while (true) {
343: int idx = i % numPenSegments;
344: px = cx + pen_dx[idx];
345: py = cy + pen_dy[idx];
346: join[ncoords++] = px;
347: join[ncoords++] = py;
348: if (i == (rev ? start : end)) {
349: break;
350: }
351: i += incr;
352: }
353: }
354:
355: return ncoords / 2;
356: }
357:
358: private static final long ROUND_JOIN_THRESHOLD = 1000L;
359: private static final long ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000L;
360:
361: private void drawRoundJoin(int x, int y, int omx, int omy, int mx,
362: int my, int side, boolean flip, boolean rev, long threshold) {
363: if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) {
364: return;
365: }
366:
367: long domx = (long) omx - mx;
368: long domy = (long) omy - my;
369: long len = domx * domx + domy * domy;
370: if (len < threshold) {
371: return;
372: }
373:
374: if (rev) {
375: omx = -omx;
376: omy = -omy;
377: mx = -mx;
378: my = -my;
379: }
380:
381: int bx0 = x + omx;
382: int by0 = y + omy;
383: int bx1 = x + mx;
384: int by1 = y + my;
385:
386: int npoints = computeRoundJoin(x, y, bx0, by0, bx1, by1, side,
387: flip, join);
388: for (int i = 0; i < npoints; i++) {
389: emitLineTo(join[2 * i], join[2 * i + 1], rev);
390: }
391: }
392:
393: // Return the intersection point of the lines (ix0, iy0) -> (ix1, iy1)
394: // and (ix0p, iy0p) -> (ix1p, iy1p) in m[0] and m[1]
395: private void computeMiter(int ix0, int iy0, int ix1, int iy1,
396: int ix0p, int iy0p, int ix1p, int iy1p, int[] m) {
397: long x0 = ix0;
398: long y0 = iy0;
399: long x1 = ix1;
400: long y1 = iy1;
401:
402: long x0p = ix0p;
403: long y0p = iy0p;
404: long x1p = ix1p;
405: long y1p = iy1p;
406:
407: long x10 = x1 - x0;
408: long y10 = y1 - y0;
409: long x10p = x1p - x0p;
410: long y10p = y1p - y0p;
411:
412: long den = (x10 * y10p - x10p * y10) >> 16;
413: if (den == 0) {
414: m[0] = ix0;
415: m[1] = iy0;
416: return;
417: }
418:
419: long t = (x1p * (y0 - y0p) - x0 * y10p + x0p * (y1p - y0)) >> 16;
420: m[0] = (int) (x0 + (t * x10) / den);
421: m[1] = (int) (y0 + (t * y10) / den);
422: }
423:
424: private void drawMiter(int px0, int py0, int x0, int y0, int x1,
425: int y1, int omx, int omy, int mx, int my, boolean rev) {
426: if (mx == omx && my == omy) {
427: return;
428: }
429: if (px0 == x0 && py0 == y0) {
430: return;
431: }
432: if (x0 == x1 && y0 == y1) {
433: return;
434: }
435:
436: if (rev) {
437: omx = -omx;
438: omy = -omy;
439: mx = -mx;
440: my = -my;
441: }
442:
443: computeMiter(px0 + omx, py0 + omy, x0 + omx, y0 + omy, x0 + mx,
444: y0 + my, x1 + mx, y1 + my, miter);
445:
446: // Compute miter length in untransformed coordinates
447: long dx = (long) miter[0] - x0;
448: long dy = (long) miter[1] - y0;
449: long a = (dy * m00 - dx * m10) >> 16;
450: long b = (dy * m01 - dx * m11) >> 16;
451: long lenSq = a * a + b * b;
452:
453: if (lenSq < miterLimitSq) {
454: emitLineTo(miter[0], miter[1], rev);
455: }
456: }
457:
458: public void moveTo(int x0, int y0) {
459: // System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
460:
461: if (lineToOrigin) {
462: // not closing the path, do the previous lineTo
463: lineToImpl(sx0, sy0, joinToOrigin);
464: lineToOrigin = false;
465: }
466:
467: if (prev == LINE_TO) {
468: finish();
469: }
470:
471: this .sx0 = this .x0 = x0;
472: this .sy0 = this .y0 = y0;
473: this .rindex = 0;
474: this .started = false;
475: this .joinSegment = false;
476: this .prev = MOVE_TO;
477: }
478:
479: boolean joinSegment = false;
480:
481: public void lineJoin() {
482: // System.out.println("Stroker.lineJoin()");
483: this .joinSegment = true;
484: }
485:
486: public void lineTo(int x1, int y1) {
487: // System.out.println("Stroker.lineTo(" + x1/65536.0 + ", " + y1/65536.0 + ")");
488:
489: if (lineToOrigin) {
490: if (x1 == sx0 && y1 == sy0) {
491: // staying in the starting point
492: return;
493: }
494:
495: // not closing the path, do the previous lineTo
496: lineToImpl(sx0, sy0, joinToOrigin);
497: lineToOrigin = false;
498: } else if (x1 == x0 && y1 == y0) {
499: return;
500: } else if (x1 == sx0 && y1 == sy0) {
501: lineToOrigin = true;
502: joinToOrigin = joinSegment;
503: joinSegment = false;
504: return;
505: }
506:
507: lineToImpl(x1, y1, joinSegment);
508: joinSegment = false;
509: }
510:
511: private void lineToImpl(int x1, int y1, boolean joinSegment) {
512: computeOffset(x0, y0, x1, y1, offset);
513: int mx = offset[0];
514: int my = offset[1];
515:
516: if (!started) {
517: emitMoveTo(x0 + mx, y0 + my);
518: this .sx1 = x1;
519: this .sy1 = y1;
520: this .mx0 = mx;
521: this .my0 = my;
522: started = true;
523: } else {
524: boolean ccw = isCCW(px0, py0, x0, y0, x1, y1);
525: if (joinSegment) {
526: if (joinStyle == JOIN_MITER) {
527: drawMiter(px0, py0, x0, y0, x1, y1, omx, omy, mx,
528: my, ccw);
529: } else if (joinStyle == JOIN_ROUND) {
530: drawRoundJoin(x0, y0, omx, omy, mx, my, 0, false,
531: ccw, ROUND_JOIN_THRESHOLD);
532: }
533: } else {
534: // Draw internal joins as round
535: drawRoundJoin(x0, y0, omx, omy, mx, my, 0, false, ccw,
536: ROUND_JOIN_INTERNAL_THRESHOLD);
537: }
538:
539: emitLineTo(x0, y0, !ccw);
540: }
541:
542: emitLineTo(x0 + mx, y0 + my, false);
543: emitLineTo(x1 + mx, y1 + my, false);
544:
545: emitLineTo(x0 - mx, y0 - my, true);
546: emitLineTo(x1 - mx, y1 - my, true);
547:
548: lx0 = x1 + mx;
549: ly0 = y1 + my;
550: lx0p = x1 - mx;
551: ly0p = y1 - my;
552: lx1 = x1;
553: ly1 = y1;
554:
555: this .omx = mx;
556: this .omy = my;
557: this .px0 = x0;
558: this .py0 = y0;
559: this .x0 = x1;
560: this .y0 = y1;
561: this .prev = LINE_TO;
562: }
563:
564: public void close() {
565: // System.out.println("Stroker.close()");
566:
567: if (lineToOrigin) {
568: // ignore the previous lineTo
569: lineToOrigin = false;
570: }
571:
572: if (!started) {
573: finish();
574: return;
575: }
576:
577: computeOffset(x0, y0, sx0, sy0, offset);
578: int mx = offset[0];
579: int my = offset[1];
580:
581: // Draw penultimate join
582: boolean ccw = isCCW(px0, py0, x0, y0, sx0, sy0);
583: if (joinSegment) {
584: if (joinStyle == JOIN_MITER) {
585: drawMiter(px0, py0, x0, y0, sx0, sy0, omx, omy, mx, my,
586: ccw);
587: } else if (joinStyle == JOIN_ROUND) {
588: drawRoundJoin(x0, y0, omx, omy, mx, my, 0, false, ccw,
589: ROUND_JOIN_THRESHOLD);
590: }
591: } else {
592: // Draw internal joins as round
593: drawRoundJoin(x0, y0, omx, omy, mx, my, 0, false, ccw,
594: ROUND_JOIN_INTERNAL_THRESHOLD);
595: }
596:
597: emitLineTo(x0 + mx, y0 + my);
598: emitLineTo(sx0 + mx, sy0 + my);
599:
600: ccw = isCCW(x0, y0, sx0, sy0, sx1, sy1);
601:
602: // Draw final join on the outside
603: if (!ccw) {
604: if (joinStyle == JOIN_MITER) {
605: drawMiter(x0, y0, sx0, sy0, sx1, sy1, mx, my, mx0, my0,
606: false);
607: } else if (joinStyle == JOIN_ROUND) {
608: drawRoundJoin(sx0, sy0, mx, my, mx0, my0, 0, false,
609: false, ROUND_JOIN_THRESHOLD);
610: }
611: }
612:
613: emitLineTo(sx0 + mx0, sy0 + my0);
614: emitLineTo(sx0 - mx0, sy0 - my0); // same as reverse[0], reverse[1]
615:
616: // Draw final join on the inside
617: if (ccw) {
618: if (joinStyle == JOIN_MITER) {
619: drawMiter(x0, y0, sx0, sy0, sx1, sy1, -mx, -my, -mx0,
620: -my0, false);
621: } else if (joinStyle == JOIN_ROUND) {
622: drawRoundJoin(sx0, sy0, -mx, -my, -mx0, -my0, 0, true,
623: false, ROUND_JOIN_THRESHOLD);
624: }
625: }
626:
627: emitLineTo(sx0 - mx, sy0 - my);
628: emitLineTo(x0 - mx, y0 - my);
629: for (int i = rindex - 2; i >= 0; i -= 2) {
630: emitLineTo(reverse[i], reverse[i + 1]);
631: }
632:
633: this .joinSegment = false;
634: this .prev = CLOSE;
635: emitClose();
636: }
637:
638: public void end() {
639: // System.out.println("Stroker.end()");
640:
641: if (lineToOrigin) {
642: // not closing the path, do the previous lineTo
643: lineToImpl(sx0, sy0, joinToOrigin);
644: lineToOrigin = false;
645: }
646:
647: if (prev == LINE_TO) {
648: finish();
649: }
650:
651: output.end();
652: this .joinSegment = false;
653: this .prev = MOVE_TO;
654: }
655:
656: long lineLength(long ldx, long ldy) {
657: long ldet = ((long) m00 * m11 - (long) m01 * m10) >> 16;
658: long la = ((long) ldy * m00 - (long) ldx * m10) / ldet;
659: long lb = ((long) ldy * m01 - (long) ldx * m11) / ldet;
660: long llen = (int) PiscesMath.hypot(la, lb);
661: return llen;
662: }
663:
664: private void finish() {
665: if (capStyle == CAP_ROUND) {
666: drawRoundJoin(x0, y0, omx, omy, -omx, -omy, 1, false,
667: false, ROUND_JOIN_THRESHOLD);
668: } else if (capStyle == CAP_SQUARE) {
669: long ldx = (long) (px0 - x0);
670: long ldy = (long) (py0 - y0);
671: long llen = lineLength(ldx, ldy);
672: long s = (long) lineWidth2 * 65536 / llen;
673:
674: int capx = x0 - (int) (ldx * s >> 16);
675: int capy = y0 - (int) (ldy * s >> 16);
676:
677: emitLineTo(capx + omx, capy + omy);
678: emitLineTo(capx - omx, capy - omy);
679: }
680:
681: for (int i = rindex - 2; i >= 0; i -= 2) {
682: emitLineTo(reverse[i], reverse[i + 1]);
683: }
684:
685: if (capStyle == CAP_ROUND) {
686: drawRoundJoin(sx0, sy0, -mx0, -my0, mx0, my0, 1, false,
687: false, ROUND_JOIN_THRESHOLD);
688: } else if (capStyle == CAP_SQUARE) {
689: long ldx = (long) (sx1 - sx0);
690: long ldy = (long) (sy1 - sy0);
691: long llen = lineLength(ldx, ldy);
692: long s = (long) lineWidth2 * 65536 / llen;
693:
694: int capx = sx0 - (int) (ldx * s >> 16);
695: int capy = sy0 - (int) (ldy * s >> 16);
696:
697: emitLineTo(capx - mx0, capy - my0);
698: emitLineTo(capx + mx0, capy + my0);
699: }
700:
701: emitClose();
702: this .joinSegment = false;
703: }
704:
705: private void emitMoveTo(int x0, int y0) {
706: // System.out.println("Stroker.emitMoveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
707: output.moveTo(x0, y0);
708: }
709:
710: private void emitLineTo(int x1, int y1) {
711: // System.out.println("Stroker.emitLineTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
712: output.lineTo(x1, y1);
713: }
714:
715: private void emitLineTo(int x1, int y1, boolean rev) {
716: if (rev) {
717: ensureCapacity(rindex + 2);
718: reverse[rindex++] = x1;
719: reverse[rindex++] = y1;
720: } else {
721: emitLineTo(x1, y1);
722: }
723: }
724:
725: private void emitClose() {
726: // System.out.println("Stroker.emitClose()");
727: output.close();
728: }
729: }
|