30 import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
31
32 /**
33 * The <code>DDasher</code> class takes a series of linear commands
34 * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
35 * <code>end</code>) and breaks them into smaller segments according to a
36 * dash pattern array and a starting dash phase.
37 *
38 * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
39 * short dash, whereas Pisces does not draw anything. The PostScript
40 * semantics are unclear.
41 *
42 */
43 final class DDasher implements DPathConsumer2D, MarlinConst {
44
45 /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */
46 static final int REC_LIMIT = 16;
47 static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial
48 static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT);
49
50 // More than 24 bits of mantissa means we can no longer accurately
51 // measure the number of times cycled through the dash array so we
52 // punt and override the phase to just be 0 past that point.
53 static final double MAX_CYCLES = 16000000.0d;
54
55 private DPathConsumer2D out;
56 private double[] dash;
57 private int dashLen;
58 private double startPhase;
59 private boolean startDashOn;
60 private int startIdx;
61
62 private boolean starting;
63 private boolean needsMoveTo;
64
65 private int idx;
66 private boolean dashOn;
67 private double phase;
68
69 // The starting point of the path
252 this.dashOn = this.startDashOn;
253 this.phase = this.startPhase;
254 this.cx0 = x0;
255 this.cy0 = y0;
256
257 // update starting point:
258 this.sx0 = x0;
259 this.sy0 = y0;
260 this.starting = true;
261
262 if (clipRect != null) {
263 final int outcode = DHelpers.outcode(x0, y0, clipRect);
264 this.cOutCode = outcode;
265 this.outside = false;
266 this.totalSkipLen = 0.0d;
267 }
268 }
269
270 private void emitSeg(double[] buf, int off, int type) {
271 switch (type) {
272 case 8:
273 out.curveTo(buf[off ], buf[off + 1],
274 buf[off + 2], buf[off + 3],
275 buf[off + 4], buf[off + 5]);
276 return;
277 case 6:
278 out.quadTo(buf[off ], buf[off + 1],
279 buf[off + 2], buf[off + 3]);
280 return;
281 case 4:
282 out.lineTo(buf[off], buf[off + 1]);
283 return;
284 default:
285 }
286 }
287
288 private void emitFirstSegments() {
289 final double[] fSegBuf = firstSegmentsBuffer;
290
291 for (int i = 0, len = firstSegidx; i < len; ) {
292 int type = (int)fSegBuf[i];
293 emitSeg(fSegBuf, i + 1, type);
294 i += (type - 1);
295 }
296 firstSegidx = 0;
297 }
298
299 // precondition: pts must be in relative coordinates (relative to x0,y0)
300 private void goTo(final double[] pts, final int off, final int type,
301 final boolean on)
302 {
303 final int index = off + type;
344 // small arraycopy (2, 4 or 6) but with offset:
345 System.arraycopy(pts, off, buf, segIdx, len);
346 firstSegidx = segIdx + len;
347 }
348
349 @Override
350 public void lineTo(final double x1, final double y1) {
351 final int outcode0 = this.cOutCode;
352
353 if (clipRect != null) {
354 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
355
356 // Should clip
357 final int orCode = (outcode0 | outcode1);
358
359 if (orCode != 0) {
360 final int sideCode = outcode0 & outcode1;
361
362 // basic rejection criteria:
363 if (sideCode == 0) {
364 // ovelap clip:
365 if (subdivide) {
366 // avoid reentrance
367 subdivide = false;
368 // subdivide curve => callback with subdivided parts:
369 boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
370 orCode, this);
371 // reentrance is done:
372 subdivide = true;
373 if (ret) {
374 return;
375 }
376 }
377 // already subdivided so render it
378 } else {
379 this.cOutCode = outcode1;
380 skipLineTo(x1, y1);
381 return;
382 }
383 }
384
399
400 double len = dx * dx + dy * dy;
401 if (len == 0.0d) {
402 return;
403 }
404 len = Math.sqrt(len);
405
406 // The scaling factors needed to get the dx and dy of the
407 // transformed dash segments.
408 final double cx = dx / len;
409 final double cy = dy / len;
410
411 final double[] _curCurvepts = curCurvepts;
412 final double[] _dash = dash;
413 final int _dashLen = this.dashLen;
414
415 int _idx = idx;
416 boolean _dashOn = dashOn;
417 double _phase = phase;
418
419 double leftInThisDashSegment, d;
420
421 while (true) {
422 d = _dash[_idx];
423 leftInThisDashSegment = d - _phase;
424
425 if (len <= leftInThisDashSegment) {
426 _curCurvepts[0] = x1;
427 _curCurvepts[1] = y1;
428
429 goTo(_curCurvepts, 0, 4, _dashOn);
430
431 // Advance phase within current dash segment
432 _phase += len;
433
434 // TODO: compare double values using epsilon:
435 if (len == leftInThisDashSegment) {
436 _phase = 0.0d;
437 _idx = (_idx + 1) % _dashLen;
438 _dashOn = !_dashOn;
439 }
440 break;
441 }
442
443 if (_phase == 0.0d) {
444 _curCurvepts[0] = cx0 + d * cx;
445 _curCurvepts[1] = cy0 + d * cy;
446 } else {
447 _curCurvepts[0] = cx0 + leftInThisDashSegment * cx;
448 _curCurvepts[1] = cy0 + leftInThisDashSegment * cy;
449 }
450
451 goTo(_curCurvepts, 0, 4, _dashOn);
452
453 len -= leftInThisDashSegment;
454 // Advance to next dash segment
455 _idx = (_idx + 1) % _dashLen;
456 _dashOn = !_dashOn;
457 _phase = 0.0d;
458 }
459 // Save local state:
460 idx = _idx;
461 dashOn = _dashOn;
462 phase = _phase;
463 }
464
465 private void skipLineTo(final double x1, final double y1) {
466 final double dx = x1 - cx0;
467 final double dy = y1 - cy0;
468
469 double len = dx * dx + dy * dy;
470 if (len != 0.0d) {
471 len = Math.sqrt(len);
472 }
473
489
490 final double[] _dash = dash;
491 final int _dashLen = this.dashLen;
492
493 int _idx = idx;
494 boolean _dashOn = dashOn;
495 double _phase = phase;
496
497 // -2 to ensure having 2 iterations of the post-loop
498 // to compensate the remaining phase
499 final long fullcycles = (long)Math.floor(len / cycleLen) - 2L;
500
501 if (fullcycles > 0L) {
502 len -= cycleLen * fullcycles;
503
504 final long iterations = fullcycles * _dashLen;
505 _idx = (int) (iterations + _idx) % _dashLen;
506 _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L;
507 }
508
509 double leftInThisDashSegment, d;
510
511 while (true) {
512 d = _dash[_idx];
513 leftInThisDashSegment = d - _phase;
514
515 if (len <= leftInThisDashSegment) {
516 // Advance phase within current dash segment
517 _phase += len;
518
519 // TODO: compare double values using epsilon:
520 if (len == leftInThisDashSegment) {
521 _phase = 0.0d;
522 _idx = (_idx + 1) % _dashLen;
523 _dashOn = !_dashOn;
524 }
525 break;
526 }
527
528 len -= leftInThisDashSegment;
529 // Advance to next dash segment
530 _idx = (_idx + 1) % _dashLen;
531 _dashOn = !_dashOn;
532 _phase = 0.0d;
533 }
534 // Save local state:
535 idx = _idx;
536 dashOn = _dashOn;
537 phase = _phase;
538 }
539
540 // preconditions: curCurvepts must be an array of length at least 2 * type,
541 // that contains the curve we want to dash in the first type elements
542 private void somethingTo(final int type) {
543 final double[] _curCurvepts = curCurvepts;
544 if (pointCurve(_curCurvepts, type)) {
545 return;
546 }
547 final LengthIterator _li = li;
548 final double[] _dash = dash;
562
563 while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
564 if (t != 0.0d) {
565 DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
566 _curCurvepts, curCurveoff,
567 _curCurvepts, 0, type);
568 prevT = t;
569 goTo(_curCurvepts, 2, type, _dashOn);
570 curCurveoff = type;
571 }
572 // Advance to next dash segment
573 _idx = (_idx + 1) % _dashLen;
574 _dashOn = !_dashOn;
575 _phase = 0.0d;
576 leftInThisDashSegment = _dash[_idx];
577 }
578
579 goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
580
581 _phase += _li.lastSegLen();
582 if (_phase >= _dash[_idx]) {
583 _phase = 0.0d;
584 _idx = (_idx + 1) % _dashLen;
585 _dashOn = !_dashOn;
586 }
587 // Save local state:
588 idx = _idx;
589 dashOn = _dashOn;
590 phase = _phase;
591
592 // reset LengthIterator:
593 _li.reset();
594 }
595
596 private void skipSomethingTo(final int type) {
597 final double[] _curCurvepts = curCurvepts;
598 if (pointCurve(_curCurvepts, type)) {
599 return;
600 }
601 final LengthIterator _li = li;
602
921
922 @Override
923 public void curveTo(final double x1, final double y1,
924 final double x2, final double y2,
925 final double x3, final double y3)
926 {
927 final int outcode0 = this.cOutCode;
928
929 if (clipRect != null) {
930 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
931 final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
932 final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
933
934 // Should clip
935 final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
936 if (orCode != 0) {
937 final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
938
939 // basic rejection criteria:
940 if (sideCode == 0) {
941 // ovelap clip:
942 if (subdivide) {
943 // avoid reentrance
944 subdivide = false;
945 // subdivide curve => callback with subdivided parts:
946 boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3,
947 orCode, this);
948 // reentrance is done:
949 subdivide = true;
950 if (ret) {
951 return;
952 }
953 }
954 // already subdivided so render it
955 } else {
956 this.cOutCode = outcode3;
957 skipCurveTo(x1, y1, x2, y2, x3, y3);
958 return;
959 }
960 }
961
1007 this.cy0 = y3;
1008 }
1009
1010 @Override
1011 public void quadTo(final double x1, final double y1,
1012 final double x2, final double y2)
1013 {
1014 final int outcode0 = this.cOutCode;
1015
1016 if (clipRect != null) {
1017 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
1018 final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
1019
1020 // Should clip
1021 final int orCode = (outcode0 | outcode1 | outcode2);
1022 if (orCode != 0) {
1023 final int sideCode = outcode0 & outcode1 & outcode2;
1024
1025 // basic rejection criteria:
1026 if (sideCode == 0) {
1027 // ovelap clip:
1028 if (subdivide) {
1029 // avoid reentrance
1030 subdivide = false;
1031 // subdivide curve => call lineTo() with subdivided curves:
1032 boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
1033 x2, y2, orCode, this);
1034 // reentrance is done:
1035 subdivide = true;
1036 if (ret) {
1037 return;
1038 }
1039 }
1040 // already subdivided so render it
1041 } else {
1042 this.cOutCode = outcode2;
1043 skipQuadTo(x1, y1, x2, y2);
1044 return;
1045 }
1046 }
1047
|
30 import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
31
32 /**
33 * The <code>DDasher</code> class takes a series of linear commands
34 * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
35 * <code>end</code>) and breaks them into smaller segments according to a
36 * dash pattern array and a starting dash phase.
37 *
38 * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
39 * short dash, whereas Pisces does not draw anything. The PostScript
40 * semantics are unclear.
41 *
42 */
43 final class DDasher implements DPathConsumer2D, MarlinConst {
44
45 /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */
46 static final int REC_LIMIT = 16;
47 static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial
48 static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT);
49
50 static final double EPS = 1e-6d;
51
52 // More than 24 bits of mantissa means we can no longer accurately
53 // measure the number of times cycled through the dash array so we
54 // punt and override the phase to just be 0 past that point.
55 static final double MAX_CYCLES = 16000000.0d;
56
57 private DPathConsumer2D out;
58 private double[] dash;
59 private int dashLen;
60 private double startPhase;
61 private boolean startDashOn;
62 private int startIdx;
63
64 private boolean starting;
65 private boolean needsMoveTo;
66
67 private int idx;
68 private boolean dashOn;
69 private double phase;
70
71 // The starting point of the path
254 this.dashOn = this.startDashOn;
255 this.phase = this.startPhase;
256 this.cx0 = x0;
257 this.cy0 = y0;
258
259 // update starting point:
260 this.sx0 = x0;
261 this.sy0 = y0;
262 this.starting = true;
263
264 if (clipRect != null) {
265 final int outcode = DHelpers.outcode(x0, y0, clipRect);
266 this.cOutCode = outcode;
267 this.outside = false;
268 this.totalSkipLen = 0.0d;
269 }
270 }
271
272 private void emitSeg(double[] buf, int off, int type) {
273 switch (type) {
274 case 4:
275 out.lineTo(buf[off], buf[off + 1]);
276 return;
277 case 8:
278 out.curveTo(buf[off ], buf[off + 1],
279 buf[off + 2], buf[off + 3],
280 buf[off + 4], buf[off + 5]);
281 return;
282 case 6:
283 out.quadTo(buf[off ], buf[off + 1],
284 buf[off + 2], buf[off + 3]);
285 return;
286 default:
287 }
288 }
289
290 private void emitFirstSegments() {
291 final double[] fSegBuf = firstSegmentsBuffer;
292
293 for (int i = 0, len = firstSegidx; i < len; ) {
294 int type = (int)fSegBuf[i];
295 emitSeg(fSegBuf, i + 1, type);
296 i += (type - 1);
297 }
298 firstSegidx = 0;
299 }
300
301 // precondition: pts must be in relative coordinates (relative to x0,y0)
302 private void goTo(final double[] pts, final int off, final int type,
303 final boolean on)
304 {
305 final int index = off + type;
346 // small arraycopy (2, 4 or 6) but with offset:
347 System.arraycopy(pts, off, buf, segIdx, len);
348 firstSegidx = segIdx + len;
349 }
350
351 @Override
352 public void lineTo(final double x1, final double y1) {
353 final int outcode0 = this.cOutCode;
354
355 if (clipRect != null) {
356 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
357
358 // Should clip
359 final int orCode = (outcode0 | outcode1);
360
361 if (orCode != 0) {
362 final int sideCode = outcode0 & outcode1;
363
364 // basic rejection criteria:
365 if (sideCode == 0) {
366 // overlap clip:
367 if (subdivide) {
368 // avoid reentrance
369 subdivide = false;
370 // subdivide curve => callback with subdivided parts:
371 boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
372 orCode, this);
373 // reentrance is done:
374 subdivide = true;
375 if (ret) {
376 return;
377 }
378 }
379 // already subdivided so render it
380 } else {
381 this.cOutCode = outcode1;
382 skipLineTo(x1, y1);
383 return;
384 }
385 }
386
401
402 double len = dx * dx + dy * dy;
403 if (len == 0.0d) {
404 return;
405 }
406 len = Math.sqrt(len);
407
408 // The scaling factors needed to get the dx and dy of the
409 // transformed dash segments.
410 final double cx = dx / len;
411 final double cy = dy / len;
412
413 final double[] _curCurvepts = curCurvepts;
414 final double[] _dash = dash;
415 final int _dashLen = this.dashLen;
416
417 int _idx = idx;
418 boolean _dashOn = dashOn;
419 double _phase = phase;
420
421 double leftInThisDashSegment, rem;
422
423 while (true) {
424 leftInThisDashSegment = _dash[_idx] - _phase;
425 rem = len - leftInThisDashSegment;
426
427 if (rem <= EPS) {
428 _curCurvepts[0] = x1;
429 _curCurvepts[1] = y1;
430
431 goTo(_curCurvepts, 0, 4, _dashOn);
432
433 // Advance phase within current dash segment
434 _phase += len;
435
436 // compare values using epsilon:
437 if (Math.abs(rem) <= EPS) {
438 _phase = 0.0d;
439 _idx = (_idx + 1) % _dashLen;
440 _dashOn = !_dashOn;
441 }
442 break;
443 }
444
445 _curCurvepts[0] = cx0 + leftInThisDashSegment * cx;
446 _curCurvepts[1] = cy0 + leftInThisDashSegment * cy;
447
448 goTo(_curCurvepts, 0, 4, _dashOn);
449
450 len = rem;
451 // Advance to next dash segment
452 _idx = (_idx + 1) % _dashLen;
453 _dashOn = !_dashOn;
454 _phase = 0.0d;
455 }
456 // Save local state:
457 idx = _idx;
458 dashOn = _dashOn;
459 phase = _phase;
460 }
461
462 private void skipLineTo(final double x1, final double y1) {
463 final double dx = x1 - cx0;
464 final double dy = y1 - cy0;
465
466 double len = dx * dx + dy * dy;
467 if (len != 0.0d) {
468 len = Math.sqrt(len);
469 }
470
486
487 final double[] _dash = dash;
488 final int _dashLen = this.dashLen;
489
490 int _idx = idx;
491 boolean _dashOn = dashOn;
492 double _phase = phase;
493
494 // -2 to ensure having 2 iterations of the post-loop
495 // to compensate the remaining phase
496 final long fullcycles = (long)Math.floor(len / cycleLen) - 2L;
497
498 if (fullcycles > 0L) {
499 len -= cycleLen * fullcycles;
500
501 final long iterations = fullcycles * _dashLen;
502 _idx = (int) (iterations + _idx) % _dashLen;
503 _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L;
504 }
505
506 double leftInThisDashSegment, rem;
507
508 while (true) {
509 leftInThisDashSegment = _dash[_idx] - _phase;
510 rem = len - leftInThisDashSegment;
511
512 if (rem <= EPS) {
513 // Advance phase within current dash segment
514 _phase += len;
515
516 // compare values using epsilon:
517 if (Math.abs(rem) <= EPS) {
518 _phase = 0.0d;
519 _idx = (_idx + 1) % _dashLen;
520 _dashOn = !_dashOn;
521 }
522 break;
523 }
524
525 len = rem;
526 // Advance to next dash segment
527 _idx = (_idx + 1) % _dashLen;
528 _dashOn = !_dashOn;
529 _phase = 0.0d;
530 }
531 // Save local state:
532 idx = _idx;
533 dashOn = _dashOn;
534 phase = _phase;
535 }
536
537 // preconditions: curCurvepts must be an array of length at least 2 * type,
538 // that contains the curve we want to dash in the first type elements
539 private void somethingTo(final int type) {
540 final double[] _curCurvepts = curCurvepts;
541 if (pointCurve(_curCurvepts, type)) {
542 return;
543 }
544 final LengthIterator _li = li;
545 final double[] _dash = dash;
559
560 while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
561 if (t != 0.0d) {
562 DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
563 _curCurvepts, curCurveoff,
564 _curCurvepts, 0, type);
565 prevT = t;
566 goTo(_curCurvepts, 2, type, _dashOn);
567 curCurveoff = type;
568 }
569 // Advance to next dash segment
570 _idx = (_idx + 1) % _dashLen;
571 _dashOn = !_dashOn;
572 _phase = 0.0d;
573 leftInThisDashSegment = _dash[_idx];
574 }
575
576 goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
577
578 _phase += _li.lastSegLen();
579
580 // compare values using epsilon:
581 if (_phase + EPS >= _dash[_idx]) {
582 _phase = 0.0d;
583 _idx = (_idx + 1) % _dashLen;
584 _dashOn = !_dashOn;
585 }
586 // Save local state:
587 idx = _idx;
588 dashOn = _dashOn;
589 phase = _phase;
590
591 // reset LengthIterator:
592 _li.reset();
593 }
594
595 private void skipSomethingTo(final int type) {
596 final double[] _curCurvepts = curCurvepts;
597 if (pointCurve(_curCurvepts, type)) {
598 return;
599 }
600 final LengthIterator _li = li;
601
920
921 @Override
922 public void curveTo(final double x1, final double y1,
923 final double x2, final double y2,
924 final double x3, final double y3)
925 {
926 final int outcode0 = this.cOutCode;
927
928 if (clipRect != null) {
929 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
930 final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
931 final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
932
933 // Should clip
934 final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
935 if (orCode != 0) {
936 final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
937
938 // basic rejection criteria:
939 if (sideCode == 0) {
940 // overlap clip:
941 if (subdivide) {
942 // avoid reentrance
943 subdivide = false;
944 // subdivide curve => callback with subdivided parts:
945 boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3,
946 orCode, this);
947 // reentrance is done:
948 subdivide = true;
949 if (ret) {
950 return;
951 }
952 }
953 // already subdivided so render it
954 } else {
955 this.cOutCode = outcode3;
956 skipCurveTo(x1, y1, x2, y2, x3, y3);
957 return;
958 }
959 }
960
1006 this.cy0 = y3;
1007 }
1008
1009 @Override
1010 public void quadTo(final double x1, final double y1,
1011 final double x2, final double y2)
1012 {
1013 final int outcode0 = this.cOutCode;
1014
1015 if (clipRect != null) {
1016 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
1017 final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
1018
1019 // Should clip
1020 final int orCode = (outcode0 | outcode1 | outcode2);
1021 if (orCode != 0) {
1022 final int sideCode = outcode0 & outcode1 & outcode2;
1023
1024 // basic rejection criteria:
1025 if (sideCode == 0) {
1026 // overlap clip:
1027 if (subdivide) {
1028 // avoid reentrance
1029 subdivide = false;
1030 // subdivide curve => call lineTo() with subdivided curves:
1031 boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
1032 x2, y2, orCode, this);
1033 // reentrance is done:
1034 subdivide = true;
1035 if (ret) {
1036 return;
1037 }
1038 }
1039 // already subdivided so render it
1040 } else {
1041 this.cOutCode = outcode2;
1042 skipQuadTo(x1, y1, x2, y2);
1043 return;
1044 }
1045 }
1046
|