249 // buffer below. |
252 // buffer below. |
250 private double[] firstSegmentsBuffer; // dynamic array |
253 private double[] firstSegmentsBuffer; // dynamic array |
251 private int firstSegidx; |
254 private int firstSegidx; |
252 |
255 |
253 // precondition: pts must be in relative coordinates (relative to x0,y0) |
256 // precondition: pts must be in relative coordinates (relative to x0,y0) |
254 private void goTo(double[] pts, int off, final int type) { |
257 private void goTo(final double[] pts, final int off, final int type, |
255 double x = pts[off + type - 4]; |
258 final boolean on) |
256 double y = pts[off + type - 3]; |
259 { |
257 if (dashOn) { |
260 final int index = off + type; |
|
261 final double x = pts[index - 4]; |
|
262 final double y = pts[index - 3]; |
|
263 |
|
264 if (on) { |
258 if (starting) { |
265 if (starting) { |
259 int len = type - 1; // - 2 + 1 |
266 goTo_starting(pts, off, type); |
260 int segIdx = firstSegidx; |
|
261 double[] buf = firstSegmentsBuffer; |
|
262 if (segIdx + len > buf.length) { |
|
263 if (DO_STATS) { |
|
264 rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer |
|
265 .add(segIdx + len); |
|
266 } |
|
267 firstSegmentsBuffer = buf |
|
268 = firstSegmentsBuffer_ref.widenArray(buf, segIdx, |
|
269 segIdx + len); |
|
270 } |
|
271 buf[segIdx++] = type; |
|
272 len--; |
|
273 // small arraycopy (2, 4 or 6) but with offset: |
|
274 System.arraycopy(pts, off, buf, segIdx, len); |
|
275 segIdx += len; |
|
276 firstSegidx = segIdx; |
|
277 } else { |
267 } else { |
278 if (needsMoveTo) { |
268 if (needsMoveTo) { |
|
269 needsMoveTo = false; |
279 out.moveTo(x0, y0); |
270 out.moveTo(x0, y0); |
280 needsMoveTo = false; |
|
281 } |
271 } |
282 emitSeg(pts, off, type); |
272 emitSeg(pts, off, type); |
283 } |
273 } |
284 } else { |
274 } else { |
285 starting = false; |
275 if (starting) { |
|
276 // low probability test (hotspot) |
|
277 starting = false; |
|
278 } |
286 needsMoveTo = true; |
279 needsMoveTo = true; |
287 } |
280 } |
288 this.x0 = x; |
281 this.x0 = x; |
289 this.y0 = y; |
282 this.y0 = y; |
290 } |
283 } |
291 |
284 |
|
285 private void goTo_starting(final double[] pts, final int off, final int type) { |
|
286 int len = type - 1; // - 2 + 1 |
|
287 int segIdx = firstSegidx; |
|
288 double[] buf = firstSegmentsBuffer; |
|
289 |
|
290 if (segIdx + len > buf.length) { |
|
291 if (DO_STATS) { |
|
292 rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer |
|
293 .add(segIdx + len); |
|
294 } |
|
295 firstSegmentsBuffer = buf |
|
296 = firstSegmentsBuffer_ref.widenArray(buf, segIdx, |
|
297 segIdx + len); |
|
298 } |
|
299 buf[segIdx++] = type; |
|
300 len--; |
|
301 // small arraycopy (2, 4 or 6) but with offset: |
|
302 System.arraycopy(pts, off, buf, segIdx, len); |
|
303 firstSegidx = segIdx + len; |
|
304 } |
|
305 |
292 @Override |
306 @Override |
293 public void lineTo(double x1, double y1) { |
307 public void lineTo(final double x1, final double y1) { |
294 double dx = x1 - x0; |
308 final double dx = x1 - x0; |
295 double dy = y1 - y0; |
309 final double dy = y1 - y0; |
296 |
310 |
297 double len = dx*dx + dy*dy; |
311 double len = dx*dx + dy*dy; |
298 if (len == 0.0d) { |
312 if (len == 0.0d) { |
299 return; |
313 return; |
300 } |
314 } |
305 final double cx = dx / len; |
319 final double cx = dx / len; |
306 final double cy = dy / len; |
320 final double cy = dy / len; |
307 |
321 |
308 final double[] _curCurvepts = curCurvepts; |
322 final double[] _curCurvepts = curCurvepts; |
309 final double[] _dash = dash; |
323 final double[] _dash = dash; |
|
324 final int _dashLen = this.dashLen; |
|
325 |
|
326 int _idx = idx; |
|
327 boolean _dashOn = dashOn; |
|
328 double _phase = phase; |
310 |
329 |
311 double leftInThisDashSegment; |
330 double leftInThisDashSegment; |
312 double dashdx, dashdy, p; |
331 double d, dashdx, dashdy, p; |
313 |
332 |
314 while (true) { |
333 while (true) { |
315 leftInThisDashSegment = _dash[idx] - phase; |
334 d = _dash[_idx]; |
|
335 leftInThisDashSegment = d - _phase; |
316 |
336 |
317 if (len <= leftInThisDashSegment) { |
337 if (len <= leftInThisDashSegment) { |
318 _curCurvepts[0] = x1; |
338 _curCurvepts[0] = x1; |
319 _curCurvepts[1] = y1; |
339 _curCurvepts[1] = y1; |
320 goTo(_curCurvepts, 0, 4); |
340 |
|
341 goTo(_curCurvepts, 0, 4, _dashOn); |
321 |
342 |
322 // Advance phase within current dash segment |
343 // Advance phase within current dash segment |
323 phase += len; |
344 _phase += len; |
|
345 |
324 // TODO: compare double values using epsilon: |
346 // TODO: compare double values using epsilon: |
325 if (len == leftInThisDashSegment) { |
347 if (len == leftInThisDashSegment) { |
326 phase = 0.0d; |
348 _phase = 0.0d; |
327 idx = (idx + 1) % dashLen; |
349 _idx = (_idx + 1) % _dashLen; |
328 dashOn = !dashOn; |
350 _dashOn = !_dashOn; |
329 } |
351 } |
|
352 |
|
353 // Save local state: |
|
354 idx = _idx; |
|
355 dashOn = _dashOn; |
|
356 phase = _phase; |
330 return; |
357 return; |
331 } |
358 } |
332 |
359 |
333 dashdx = _dash[idx] * cx; |
360 dashdx = d * cx; |
334 dashdy = _dash[idx] * cy; |
361 dashdy = d * cy; |
335 |
362 |
336 if (phase == 0.0d) { |
363 if (_phase == 0.0d) { |
337 _curCurvepts[0] = x0 + dashdx; |
364 _curCurvepts[0] = x0 + dashdx; |
338 _curCurvepts[1] = y0 + dashdy; |
365 _curCurvepts[1] = y0 + dashdy; |
339 } else { |
366 } else { |
340 p = leftInThisDashSegment / _dash[idx]; |
367 p = leftInThisDashSegment / d; |
341 _curCurvepts[0] = x0 + p * dashdx; |
368 _curCurvepts[0] = x0 + p * dashdx; |
342 _curCurvepts[1] = y0 + p * dashdy; |
369 _curCurvepts[1] = y0 + p * dashdy; |
343 } |
370 } |
344 |
371 |
345 goTo(_curCurvepts, 0, 4); |
372 goTo(_curCurvepts, 0, 4, _dashOn); |
346 |
373 |
347 len -= leftInThisDashSegment; |
374 len -= leftInThisDashSegment; |
348 // Advance to next dash segment |
375 // Advance to next dash segment |
349 idx = (idx + 1) % dashLen; |
376 _idx = (_idx + 1) % _dashLen; |
350 dashOn = !dashOn; |
377 _dashOn = !_dashOn; |
351 phase = 0.0d; |
378 _phase = 0.0d; |
352 } |
379 } |
353 } |
380 } |
354 |
381 |
355 // shared instance in DDasher |
382 // shared instance in DDasher |
356 private final LengthIterator li = new LengthIterator(); |
383 private final LengthIterator li = new LengthIterator(); |
357 |
384 |
358 // preconditions: curCurvepts must be an array of length at least 2 * type, |
385 // preconditions: curCurvepts must be an array of length at least 2 * type, |
359 // that contains the curve we want to dash in the first type elements |
386 // that contains the curve we want to dash in the first type elements |
360 private void somethingTo(int type) { |
387 private void somethingTo(final int type) { |
361 if (pointCurve(curCurvepts, type)) { |
388 if (pointCurve(curCurvepts, type)) { |
362 return; |
389 return; |
363 } |
390 } |
364 li.initializeIterationOnCurve(curCurvepts, type); |
391 final LengthIterator _li = li; |
|
392 final double[] _curCurvepts = curCurvepts; |
|
393 final double[] _dash = dash; |
|
394 final int _dashLen = this.dashLen; |
|
395 |
|
396 _li.initializeIterationOnCurve(_curCurvepts, type); |
|
397 |
|
398 int _idx = idx; |
|
399 boolean _dashOn = dashOn; |
|
400 double _phase = phase; |
365 |
401 |
366 // initially the current curve is at curCurvepts[0...type] |
402 // initially the current curve is at curCurvepts[0...type] |
367 int curCurveoff = 0; |
403 int curCurveoff = 0; |
368 double lastSplitT = 0.0d; |
404 double lastSplitT = 0.0d; |
369 double t; |
405 double t; |
370 double leftInThisDashSegment = dash[idx] - phase; |
406 double leftInThisDashSegment = _dash[_idx] - _phase; |
371 |
407 |
372 while ((t = li.next(leftInThisDashSegment)) < 1.0d) { |
408 while ((t = _li.next(leftInThisDashSegment)) < 1.0d) { |
373 if (t != 0.0d) { |
409 if (t != 0.0d) { |
374 DHelpers.subdivideAt((t - lastSplitT) / (1.0d - lastSplitT), |
410 DHelpers.subdivideAt((t - lastSplitT) / (1.0d - lastSplitT), |
375 curCurvepts, curCurveoff, |
411 _curCurvepts, curCurveoff, |
376 curCurvepts, 0, |
412 _curCurvepts, 0, |
377 curCurvepts, type, type); |
413 _curCurvepts, type, type); |
378 lastSplitT = t; |
414 lastSplitT = t; |
379 goTo(curCurvepts, 2, type); |
415 goTo(_curCurvepts, 2, type, _dashOn); |
380 curCurveoff = type; |
416 curCurveoff = type; |
381 } |
417 } |
382 // Advance to next dash segment |
418 // Advance to next dash segment |
383 idx = (idx + 1) % dashLen; |
419 _idx = (_idx + 1) % _dashLen; |
384 dashOn = !dashOn; |
420 _dashOn = !_dashOn; |
385 phase = 0.0d; |
421 _phase = 0.0d; |
386 leftInThisDashSegment = dash[idx]; |
422 leftInThisDashSegment = _dash[_idx]; |
387 } |
423 } |
388 goTo(curCurvepts, curCurveoff+2, type); |
424 |
389 phase += li.lastSegLen(); |
425 goTo(_curCurvepts, curCurveoff + 2, type, _dashOn); |
390 if (phase >= dash[idx]) { |
426 |
391 phase = 0.0d; |
427 _phase += _li.lastSegLen(); |
392 idx = (idx + 1) % dashLen; |
428 if (_phase >= _dash[_idx]) { |
393 dashOn = !dashOn; |
429 _phase = 0.0d; |
394 } |
430 _idx = (_idx + 1) % _dashLen; |
|
431 _dashOn = !_dashOn; |
|
432 } |
|
433 // Save local state: |
|
434 idx = _idx; |
|
435 dashOn = _dashOn; |
|
436 phase = _phase; |
|
437 |
395 // reset LengthIterator: |
438 // reset LengthIterator: |
396 li.reset(); |
439 _li.reset(); |
397 } |
440 } |
398 |
441 |
399 private static boolean pointCurve(double[] curve, int type) { |
442 private static boolean pointCurve(double[] curve, int type) { |
400 for (int i = 2; i < type; i++) { |
443 for (int i = 2; i < type; i++) { |
401 if (curve[i] != curve[i-2]) { |
444 if (curve[i] != curve[i-2]) { |
417 // limit+1 curves - one for each level of the tree + 1. |
460 // limit+1 curves - one for each level of the tree + 1. |
418 // NOTE: the way we do things here is not enough to traverse a general |
461 // NOTE: the way we do things here is not enough to traverse a general |
419 // tree; however, the trees we are interested in have the property that |
462 // tree; however, the trees we are interested in have the property that |
420 // every non leaf node has exactly 2 children |
463 // every non leaf node has exactly 2 children |
421 static final class LengthIterator { |
464 static final class LengthIterator { |
422 private enum Side {LEFT, RIGHT}; |
465 private enum Side {LEFT, RIGHT} |
423 // Holds the curves at various levels of the recursion. The root |
466 // Holds the curves at various levels of the recursion. The root |
424 // (i.e. the original curve) is at recCurveStack[0] (but then it |
467 // (i.e. the original curve) is at recCurveStack[0] (but then it |
425 // gets subdivided, the left half is put at 1, so most of the time |
468 // gets subdivided, the left half is put at 1, so most of the time |
426 // only the right half of the original curve is at 0) |
469 // only the right half of the original curve is at 0) |
427 private final double[][] recCurveStack; // dirty |
470 private final double[][] recCurveStack; // dirty |
667 } |
710 } |
668 |
711 |
669 // this is a bit of a hack. It returns -1 if we're not on a leaf, and |
712 // this is a bit of a hack. It returns -1 if we're not on a leaf, and |
670 // the length of the leaf if we are on a leaf. |
713 // the length of the leaf if we are on a leaf. |
671 private double onLeaf() { |
714 private double onLeaf() { |
672 double[] curve = recCurveStack[recLevel]; |
715 final double[] curve = recCurveStack[recLevel]; |
|
716 final int _curveType = curveType; |
673 double polyLen = 0.0d; |
717 double polyLen = 0.0d; |
674 |
718 |
675 double x0 = curve[0], y0 = curve[1]; |
719 double x0 = curve[0], y0 = curve[1]; |
676 for (int i = 2; i < curveType; i += 2) { |
720 for (int i = 2; i < _curveType; i += 2) { |
677 final double x1 = curve[i], y1 = curve[i+1]; |
721 final double x1 = curve[i], y1 = curve[i+1]; |
678 final double len = DHelpers.linelen(x0, y0, x1, y1); |
722 final double len = DHelpers.linelen(x0, y0, x1, y1); |
679 polyLen += len; |
723 polyLen += len; |
680 curLeafCtrlPolyLengths[i/2 - 1] = len; |
724 curLeafCtrlPolyLengths[(i >> 1) - 1] = len; |
681 x0 = x1; |
725 x0 = x1; |
682 y0 = y1; |
726 y0 = y1; |
683 } |
727 } |
684 |
728 |
685 final double lineLen = DHelpers.linelen(curve[0], curve[1], |
729 final double lineLen = DHelpers.linelen(curve[0], curve[1], |
686 curve[curveType-2], |
730 curve[_curveType-2], |
687 curve[curveType-1]); |
731 curve[_curveType-1]); |
688 if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) { |
732 if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) { |
689 return (polyLen + lineLen) / 2.0d; |
733 return (polyLen + lineLen) / 2.0d; |
690 } |
734 } |
691 return -1.0d; |
735 return -1.0d; |
692 } |
736 } |
693 } |
737 } |
694 |
738 |
695 @Override |
739 @Override |
696 public void curveTo(double x1, double y1, |
740 public void curveTo(final double x1, final double y1, |
697 double x2, double y2, |
741 final double x2, final double y2, |
698 double x3, double y3) |
742 final double x3, final double y3) |
699 { |
743 { |
700 final double[] _curCurvepts = curCurvepts; |
744 final double[] _curCurvepts = curCurvepts; |
701 _curCurvepts[0] = x0; _curCurvepts[1] = y0; |
745 _curCurvepts[0] = x0; _curCurvepts[1] = y0; |
702 _curCurvepts[2] = x1; _curCurvepts[3] = y1; |
746 _curCurvepts[2] = x1; _curCurvepts[3] = y1; |
703 _curCurvepts[4] = x2; _curCurvepts[5] = y2; |
747 _curCurvepts[4] = x2; _curCurvepts[5] = y2; |
704 _curCurvepts[6] = x3; _curCurvepts[7] = y3; |
748 _curCurvepts[6] = x3; _curCurvepts[7] = y3; |
705 somethingTo(8); |
749 somethingTo(8); |
706 } |
750 } |
707 |
751 |
708 @Override |
752 @Override |
709 public void quadTo(double x1, double y1, double x2, double y2) { |
753 public void quadTo(final double x1, final double y1, |
|
754 final double x2, final double y2) |
|
755 { |
710 final double[] _curCurvepts = curCurvepts; |
756 final double[] _curCurvepts = curCurvepts; |
711 _curCurvepts[0] = x0; _curCurvepts[1] = y0; |
757 _curCurvepts[0] = x0; _curCurvepts[1] = y0; |
712 _curCurvepts[2] = x1; _curCurvepts[3] = y1; |
758 _curCurvepts[2] = x1; _curCurvepts[3] = y1; |
713 _curCurvepts[4] = x2; _curCurvepts[5] = y2; |
759 _curCurvepts[4] = x2; _curCurvepts[5] = y2; |
714 somethingTo(6); |
760 somethingTo(6); |
715 } |
761 } |
716 |
762 |
717 @Override |
763 @Override |
718 public void closePath() { |
764 public void closePath() { |
719 lineTo(sx, sy); |
765 lineTo(sx, sy); |
720 if (firstSegidx > 0) { |
766 if (firstSegidx != 0) { |
721 if (!dashOn || needsMoveTo) { |
767 if (!dashOn || needsMoveTo) { |
722 out.moveTo(sx, sy); |
768 out.moveTo(sx, sy); |
723 } |
769 } |
724 emitFirstSegments(); |
770 emitFirstSegments(); |
725 } |
771 } |
726 moveTo(sx, sy); |
772 moveTo(sx, sy); |
727 } |
773 } |
728 |
774 |
729 @Override |
775 @Override |
730 public void pathDone() { |
776 public void pathDone() { |
731 if (firstSegidx > 0) { |
777 if (firstSegidx != 0) { |
732 out.moveTo(sx, sy); |
778 out.moveTo(sx, sy); |
733 emitFirstSegments(); |
779 emitFirstSegments(); |
734 } |
780 } |
735 out.pathDone(); |
781 out.pathDone(); |
736 |
782 |