changeset 49496 | 1ea202af7a97 |
parent 48284 | fd7fbc929001 |
child 51933 | 4ec74929fbfe |
49495:f46bfa7a2956 | 49496:1ea202af7a97 |
---|---|
1 /* |
1 /* |
2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
5 * This code is free software; you can redistribute it and/or modify it |
6 * under the terms of the GNU General Public License version 2 only, as |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
25 |
25 |
26 package sun.java2d.marlin; |
26 package sun.java2d.marlin; |
27 |
27 |
28 import java.util.Arrays; |
28 import java.util.Arrays; |
29 import sun.java2d.marlin.DHelpers.PolyStack; |
29 import sun.java2d.marlin.DHelpers.PolyStack; |
30 import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer; |
|
31 import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter; |
|
30 |
32 |
31 // TODO: some of the arithmetic here is too verbose and prone to hard to |
33 // TODO: some of the arithmetic here is too verbose and prone to hard to |
32 // debug typos. We should consider making a small Point/Vector class that |
34 // debug typos. We should consider making a small Point/Vector class that |
33 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such |
35 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such |
34 final class DStroker implements DPathConsumer2D, MarlinConst { |
36 final class DStroker implements DPathConsumer2D, MarlinConst { |
35 |
37 |
36 private static final int MOVE_TO = 0; |
38 private static final int MOVE_TO = 0; |
37 private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad |
39 private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad |
38 private static final int CLOSE = 2; |
40 private static final int CLOSE = 2; |
39 |
41 |
40 // pisces used to use fixed point arithmetic with 16 decimal digits. I |
42 // round join threshold = 1 subpixel |
41 // didn't want to change the values of the constant below when I converted |
43 private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS); |
42 // it to floating point, so that's why the divisions by 2^16 are there. |
44 private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN; |
43 private static final double ROUND_JOIN_THRESHOLD = 1000.0d/65536.0d; |
|
44 |
45 |
45 // kappa = (4/3) * (SQRT(2) - 1) |
46 // kappa = (4/3) * (SQRT(2) - 1) |
46 private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d); |
47 private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d); |
47 |
48 |
48 // SQRT(2) |
49 // SQRT(2) |
49 private static final double SQRT_2 = Math.sqrt(2.0d); |
50 private static final double SQRT_2 = Math.sqrt(2.0d); |
50 |
|
51 private static final int MAX_N_CURVES = 11; |
|
52 |
51 |
53 private DPathConsumer2D out; |
52 private DPathConsumer2D out; |
54 |
53 |
55 private int capStyle; |
54 private int capStyle; |
56 private int joinStyle; |
55 private int joinStyle; |
78 // would be error prone and hard to read, so we keep these anyway. |
77 // would be error prone and hard to read, so we keep these anyway. |
79 private double smx, smy, cmx, cmy; |
78 private double smx, smy, cmx, cmy; |
80 |
79 |
81 private final PolyStack reverse; |
80 private final PolyStack reverse; |
82 |
81 |
83 // This is where the curve to be processed is put. We give it |
|
84 // enough room to store all curves. |
|
85 private final double[] middle = new double[MAX_N_CURVES * 6 + 2]; |
|
86 private final double[] lp = new double[8]; |
82 private final double[] lp = new double[8]; |
87 private final double[] rp = new double[8]; |
83 private final double[] rp = new double[8]; |
88 private final double[] subdivTs = new double[MAX_N_CURVES - 1]; |
|
89 |
84 |
90 // per-thread renderer context |
85 // per-thread renderer context |
91 final DRendererContext rdrCtx; |
86 final DRendererContext rdrCtx; |
92 |
87 |
93 // dirty curve |
88 // dirty curve |
104 |
99 |
105 // flag indicating if the path is opened (clipped) |
100 // flag indicating if the path is opened (clipped) |
106 private boolean opened = false; |
101 private boolean opened = false; |
107 // flag indicating if the starting point's cap is done |
102 // flag indicating if the starting point's cap is done |
108 private boolean capStart = false; |
103 private boolean capStart = false; |
104 // flag indicating to monotonize curves |
|
105 private boolean monotonize; |
|
106 |
|
107 private boolean subdivide = false; |
|
108 private final CurveClipSplitter curveSplitter; |
|
109 |
109 |
110 /** |
110 /** |
111 * Constructs a <code>DStroker</code>. |
111 * Constructs a <code>DStroker</code>. |
112 * @param rdrCtx per-thread renderer context |
112 * @param rdrCtx per-thread renderer context |
113 */ |
113 */ |
122 rdrCtx.stats.stat_array_str_polystack_curves, |
122 rdrCtx.stats.stat_array_str_polystack_curves, |
123 rdrCtx.stats.stat_array_str_polystack_types) |
123 rdrCtx.stats.stat_array_str_polystack_types) |
124 : new PolyStack(rdrCtx); |
124 : new PolyStack(rdrCtx); |
125 |
125 |
126 this.curve = rdrCtx.curve; |
126 this.curve = rdrCtx.curve; |
127 this.curveSplitter = rdrCtx.curveClipSplitter; |
|
127 } |
128 } |
128 |
129 |
129 /** |
130 /** |
130 * Inits the <code>DStroker</code>. |
131 * Inits the <code>DStroker</code>. |
131 * |
132 * |
137 * @param joinStyle the desired line join style, one of |
138 * @param joinStyle the desired line join style, one of |
138 * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or |
139 * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or |
139 * <code>JOIN_BEVEL</code>. |
140 * <code>JOIN_BEVEL</code>. |
140 * @param miterLimit the desired miter limit |
141 * @param miterLimit the desired miter limit |
141 * @param scale scaling factor applied to clip boundaries |
142 * @param scale scaling factor applied to clip boundaries |
143 * @param subdivideCurves true to indicate to subdivide curves, false if dasher does |
|
142 * @return this instance |
144 * @return this instance |
143 */ |
145 */ |
144 DStroker init(final DPathConsumer2D pc2d, |
146 DStroker init(final DPathConsumer2D pc2d, |
145 final double lineWidth, |
147 final double lineWidth, |
146 final int capStyle, |
148 final int capStyle, |
147 final int joinStyle, |
149 final int joinStyle, |
148 final double miterLimit, |
150 final double miterLimit, |
149 final double scale) |
151 final double scale, |
152 final boolean subdivideCurves) |
|
150 { |
153 { |
151 this.out = pc2d; |
154 this.out = pc2d; |
152 |
155 |
153 this.lineWidth2 = lineWidth / 2.0d; |
156 this.lineWidth2 = lineWidth / 2.0d; |
154 this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2); |
157 this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2); |
158 this.monotonize = subdivideCurves; |
|
159 |
|
155 this.capStyle = capStyle; |
160 this.capStyle = capStyle; |
156 this.joinStyle = joinStyle; |
161 this.joinStyle = joinStyle; |
157 |
162 |
158 final double limit = miterLimit * lineWidth2; |
163 final double limit = miterLimit * lineWidth2; |
159 this.miterLimitSq = limit * limit; |
164 this.miterLimitSq = limit * limit; |
187 _clipRect[0] -= margin - rdrOffY; |
192 _clipRect[0] -= margin - rdrOffY; |
188 _clipRect[1] += margin + rdrOffY; |
193 _clipRect[1] += margin + rdrOffY; |
189 _clipRect[2] -= margin - rdrOffX; |
194 _clipRect[2] -= margin - rdrOffX; |
190 _clipRect[3] += margin + rdrOffX; |
195 _clipRect[3] += margin + rdrOffX; |
191 this.clipRect = _clipRect; |
196 this.clipRect = _clipRect; |
197 |
|
198 // initialize curve splitter here for stroker & dasher: |
|
199 if (DO_CLIP_SUBDIVIDER) { |
|
200 subdivide = subdivideCurves; |
|
201 // adjust padded clip rectangle: |
|
202 curveSplitter.init(); |
|
203 } else { |
|
204 subdivide = false; |
|
205 } |
|
192 } else { |
206 } else { |
193 this.clipRect = null; |
207 this.clipRect = null; |
194 this.cOutCode = 0; |
208 this.cOutCode = 0; |
195 this.sOutCode = 0; |
209 this.sOutCode = 0; |
196 } |
210 } |
197 return this; // fluent API |
211 return this; // fluent API |
212 } |
|
213 |
|
214 void disableClipping() { |
|
215 this.clipRect = null; |
|
216 this.cOutCode = 0; |
|
217 this.sOutCode = 0; |
|
198 } |
218 } |
199 |
219 |
200 /** |
220 /** |
201 * Disposes this stroker: |
221 * Disposes this stroker: |
202 * clean up before reusing this instance |
222 * clean up before reusing this instance |
211 // Force zero-fill dirty arrays: |
231 // Force zero-fill dirty arrays: |
212 Arrays.fill(offset0, 0.0d); |
232 Arrays.fill(offset0, 0.0d); |
213 Arrays.fill(offset1, 0.0d); |
233 Arrays.fill(offset1, 0.0d); |
214 Arrays.fill(offset2, 0.0d); |
234 Arrays.fill(offset2, 0.0d); |
215 Arrays.fill(miter, 0.0d); |
235 Arrays.fill(miter, 0.0d); |
216 Arrays.fill(middle, 0.0d); |
|
217 Arrays.fill(lp, 0.0d); |
236 Arrays.fill(lp, 0.0d); |
218 Arrays.fill(rp, 0.0d); |
237 Arrays.fill(rp, 0.0d); |
219 Arrays.fill(subdivTs, 0.0d); |
|
220 } |
238 } |
221 } |
239 } |
222 |
240 |
223 private static void computeOffset(final double lx, final double ly, |
241 private static void computeOffset(final double lx, final double ly, |
224 final double w, final double[] m) |
242 final double w, final double[] m) |
246 final double dx2, final double dy2) |
264 final double dx2, final double dy2) |
247 { |
265 { |
248 return dx1 * dy2 <= dy1 * dx2; |
266 return dx1 * dy2 <= dy1 * dx2; |
249 } |
267 } |
250 |
268 |
251 private void drawRoundJoin(double x, double y, |
269 private void mayDrawRoundJoin(double cx, double cy, |
252 double omx, double omy, double mx, double my, |
270 double omx, double omy, |
253 boolean rev, |
271 double mx, double my, |
254 double threshold) |
272 boolean rev) |
255 { |
273 { |
256 if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) { |
274 if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) { |
257 return; |
275 return; |
258 } |
276 } |
259 |
277 |
260 double domx = omx - mx; |
278 final double domx = omx - mx; |
261 double domy = omy - my; |
279 final double domy = omy - my; |
262 double len = domx*domx + domy*domy; |
280 final double lenSq = domx*domx + domy*domy; |
263 if (len < threshold) { |
281 |
282 if (lenSq < ROUND_JOIN_THRESHOLD) { |
|
264 return; |
283 return; |
265 } |
284 } |
266 |
285 |
267 if (rev) { |
286 if (rev) { |
268 omx = -omx; |
287 omx = -omx; |
269 omy = -omy; |
288 omy = -omy; |
270 mx = -mx; |
289 mx = -mx; |
271 my = -my; |
290 my = -my; |
272 } |
291 } |
273 drawRoundJoin(x, y, omx, omy, mx, my, rev); |
292 drawRoundJoin(cx, cy, omx, omy, mx, my, rev); |
274 } |
293 } |
275 |
294 |
276 private void drawRoundJoin(double cx, double cy, |
295 private void drawRoundJoin(double cx, double cy, |
277 double omx, double omy, |
296 double omx, double omy, |
278 double mx, double my, |
297 double mx, double my, |
379 // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] |
398 // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] |
380 private static void computeMiter(final double x0, final double y0, |
399 private static void computeMiter(final double x0, final double y0, |
381 final double x1, final double y1, |
400 final double x1, final double y1, |
382 final double x0p, final double y0p, |
401 final double x0p, final double y0p, |
383 final double x1p, final double y1p, |
402 final double x1p, final double y1p, |
384 final double[] m, int off) |
403 final double[] m) |
385 { |
404 { |
386 double x10 = x1 - x0; |
405 double x10 = x1 - x0; |
387 double y10 = y1 - y0; |
406 double y10 = y1 - y0; |
388 double x10p = x1p - x0p; |
407 double x10p = x1p - x0p; |
389 double y10p = y1p - y0p; |
408 double y10p = y1p - y0p; |
398 // (mx == omx && my == omy) will be true, and drawMiter will return |
417 // (mx == omx && my == omy) will be true, and drawMiter will return |
399 // immediately). |
418 // immediately). |
400 double den = x10*y10p - x10p*y10; |
419 double den = x10*y10p - x10p*y10; |
401 double t = x10p*(y0-y0p) - y10p*(x0-x0p); |
420 double t = x10p*(y0-y0p) - y10p*(x0-x0p); |
402 t /= den; |
421 t /= den; |
403 m[off++] = x0 + t*x10; |
422 m[0] = x0 + t*x10; |
404 m[off] = y0 + t*y10; |
423 m[1] = y0 + t*y10; |
405 } |
424 } |
406 |
425 |
407 // Return the intersection point of the lines (x0, y0) -> (x1, y1) |
426 // Return the intersection point of the lines (x0, y0) -> (x1, y1) |
408 // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] |
427 // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] |
409 private static void safeComputeMiter(final double x0, final double y0, |
428 private static void safeComputeMiter(final double x0, final double y0, |
410 final double x1, final double y1, |
429 final double x1, final double y1, |
411 final double x0p, final double y0p, |
430 final double x0p, final double y0p, |
412 final double x1p, final double y1p, |
431 final double x1p, final double y1p, |
413 final double[] m, int off) |
432 final double[] m) |
414 { |
433 { |
415 double x10 = x1 - x0; |
434 double x10 = x1 - x0; |
416 double y10 = y1 - y0; |
435 double y10 = y1 - y0; |
417 double x10p = x1p - x0p; |
436 double x10p = x1p - x0p; |
418 double y10p = y1p - y0p; |
437 double y10p = y1p - y0p; |
426 // miter drawing because it won't be called by drawMiter (because |
445 // miter drawing because it won't be called by drawMiter (because |
427 // (mx == omx && my == omy) will be true, and drawMiter will return |
446 // (mx == omx && my == omy) will be true, and drawMiter will return |
428 // immediately). |
447 // immediately). |
429 double den = x10*y10p - x10p*y10; |
448 double den = x10*y10p - x10p*y10; |
430 if (den == 0.0d) { |
449 if (den == 0.0d) { |
431 m[off++] = (x0 + x0p) / 2.0d; |
450 m[2] = (x0 + x0p) / 2.0d; |
432 m[off] = (y0 + y0p) / 2.0d; |
451 m[3] = (y0 + y0p) / 2.0d; |
433 return; |
452 } else { |
434 } |
453 double t = x10p*(y0-y0p) - y10p*(x0-x0p); |
435 double t = x10p*(y0-y0p) - y10p*(x0-x0p); |
454 t /= den; |
436 t /= den; |
455 m[2] = x0 + t*x10; |
437 m[off++] = x0 + t*x10; |
456 m[3] = y0 + t*y10; |
438 m[off] = y0 + t*y10; |
457 } |
439 } |
458 } |
440 |
459 |
441 private void drawMiter(final double pdx, final double pdy, |
460 private void drawMiter(final double pdx, final double pdy, |
442 final double x0, final double y0, |
461 final double x0, final double y0, |
443 final double dx, final double dy, |
462 final double dx, final double dy, |
444 double omx, double omy, double mx, double my, |
463 double omx, double omy, |
464 double mx, double my, |
|
445 boolean rev) |
465 boolean rev) |
446 { |
466 { |
447 if ((mx == omx && my == omy) || |
467 if ((mx == omx && my == omy) || |
448 (pdx == 0.0d && pdy == 0.0d) || |
468 (pdx == 0.0d && pdy == 0.0d) || |
449 (dx == 0.0d && dy == 0.0d)) |
469 (dx == 0.0d && dy == 0.0d)) |
457 mx = -mx; |
477 mx = -mx; |
458 my = -my; |
478 my = -my; |
459 } |
479 } |
460 |
480 |
461 computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy, |
481 computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy, |
462 (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, |
482 (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter); |
463 miter, 0); |
|
464 |
483 |
465 final double miterX = miter[0]; |
484 final double miterX = miter[0]; |
466 final double miterY = miter[1]; |
485 final double miterY = miter[1]; |
467 double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0); |
486 double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0); |
468 |
487 |
476 } |
495 } |
477 } |
496 } |
478 |
497 |
479 @Override |
498 @Override |
480 public void moveTo(final double x0, final double y0) { |
499 public void moveTo(final double x0, final double y0) { |
481 moveTo(x0, y0, cOutCode); |
500 _moveTo(x0, y0, cOutCode); |
482 // update starting point: |
501 // update starting point: |
483 this.sx0 = x0; |
502 this.sx0 = x0; |
484 this.sy0 = y0; |
503 this.sy0 = y0; |
485 this.sdx = 1.0d; |
504 this.sdx = 1.0d; |
486 this.sdy = 0.0d; |
505 this.sdy = 0.0d; |
492 this.cOutCode = outcode; |
511 this.cOutCode = outcode; |
493 this.sOutCode = outcode; |
512 this.sOutCode = outcode; |
494 } |
513 } |
495 } |
514 } |
496 |
515 |
497 private void moveTo(final double x0, final double y0, |
516 private void _moveTo(final double x0, final double y0, |
498 final int outcode) |
517 final int outcode) |
499 { |
518 { |
500 if (prev == MOVE_TO) { |
519 if (prev == MOVE_TO) { |
501 this.cx0 = x0; |
520 this.cx0 = x0; |
502 this.cy0 = y0; |
521 this.cy0 = y0; |
519 |
538 |
520 private void lineTo(final double x1, final double y1, |
539 private void lineTo(final double x1, final double y1, |
521 final boolean force) |
540 final boolean force) |
522 { |
541 { |
523 final int outcode0 = this.cOutCode; |
542 final int outcode0 = this.cOutCode; |
543 |
|
524 if (!force && clipRect != null) { |
544 if (!force && clipRect != null) { |
525 final int outcode1 = DHelpers.outcode(x1, y1, clipRect); |
545 final int outcode1 = DHelpers.outcode(x1, y1, clipRect); |
546 |
|
547 // Should clip |
|
548 final int orCode = (outcode0 | outcode1); |
|
549 if (orCode != 0) { |
|
550 final int sideCode = outcode0 & outcode1; |
|
551 |
|
552 // basic rejection criteria: |
|
553 if (sideCode == 0) { |
|
554 // ovelap clip: |
|
555 if (subdivide) { |
|
556 // avoid reentrance |
|
557 subdivide = false; |
|
558 // subdivide curve => callback with subdivided parts: |
|
559 boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1, |
|
560 orCode, this); |
|
561 // reentrance is done: |
|
562 subdivide = true; |
|
563 if (ret) { |
|
564 return; |
|
565 } |
|
566 } |
|
567 // already subdivided so render it |
|
568 } else { |
|
569 this.cOutCode = outcode1; |
|
570 _moveTo(x1, y1, outcode0); |
|
571 opened = true; |
|
572 return; |
|
573 } |
|
574 } |
|
575 |
|
526 this.cOutCode = outcode1; |
576 this.cOutCode = outcode1; |
527 |
|
528 // basic rejection criteria |
|
529 if ((outcode0 & outcode1) != 0) { |
|
530 moveTo(x1, y1, outcode0); |
|
531 opened = true; |
|
532 return; |
|
533 } |
|
534 } |
577 } |
535 |
578 |
536 double dx = x1 - cx0; |
579 double dx = x1 - cx0; |
537 double dy = y1 - cy0; |
580 double dy = y1 - cy0; |
538 if (dx == 0.0d && dy == 0.0d) { |
581 if (dx == 0.0d && dy == 0.0d) { |
750 final boolean cw = isCW(pdx, pdy, dx, dy); |
793 final boolean cw = isCW(pdx, pdy, dx, dy); |
751 if (outcode == 0) { |
794 if (outcode == 0) { |
752 if (joinStyle == JOIN_MITER) { |
795 if (joinStyle == JOIN_MITER) { |
753 drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw); |
796 drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw); |
754 } else if (joinStyle == JOIN_ROUND) { |
797 } else if (joinStyle == JOIN_ROUND) { |
755 drawRoundJoin(x0, y0, |
798 mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw); |
756 omx, omy, |
|
757 mx, my, cw, |
|
758 ROUND_JOIN_THRESHOLD); |
|
759 } |
799 } |
760 } |
800 } |
761 emitLineTo(x0, y0, !cw); |
801 emitLineTo(x0, y0, !cw); |
762 } |
802 } |
763 prev = DRAWING_OP_TO; |
803 prev = DRAWING_OP_TO; |
764 } |
804 } |
765 |
805 |
766 private static boolean within(final double x1, final double y1, |
806 private static boolean within(final double x1, final double y1, |
767 final double x2, final double y2, |
807 final double x2, final double y2, |
768 final double ERR) |
808 final double err) |
769 { |
809 { |
770 assert ERR > 0 : ""; |
810 assert err > 0 : ""; |
771 // compare taxicab distance. ERR will always be small, so using |
811 // compare taxicab distance. ERR will always be small, so using |
772 // true distance won't give much benefit |
812 // true distance won't give much benefit |
773 return (DHelpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs |
813 return (DHelpers.within(x1, x2, err) && // we want to avoid calling Math.abs |
774 DHelpers.within(y1, y2, ERR)); // this is just as good. |
814 DHelpers.within(y1, y2, err)); // this is just as good. |
775 } |
815 } |
776 |
816 |
777 private void getLineOffsets(double x1, double y1, |
817 private void getLineOffsets(final double x1, final double y1, |
778 double x2, double y2, |
818 final double x2, final double y2, |
779 double[] left, double[] right) { |
819 final double[] left, final double[] right) |
820 { |
|
780 computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0); |
821 computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0); |
781 final double mx = offset0[0]; |
822 final double mx = offset0[0]; |
782 final double my = offset0[1]; |
823 final double my = offset0[1]; |
783 left[0] = x1 + mx; |
824 left[0] = x1 + mx; |
784 left[1] = y1 + my; |
825 left[1] = y1 + my; |
785 left[2] = x2 + mx; |
826 left[2] = x2 + mx; |
786 left[3] = y2 + my; |
827 left[3] = y2 + my; |
828 |
|
787 right[0] = x1 - mx; |
829 right[0] = x1 - mx; |
788 right[1] = y1 - my; |
830 right[1] = y1 - my; |
789 right[2] = x2 - mx; |
831 right[2] = x2 - mx; |
790 right[3] = y2 - my; |
832 right[3] = y2 - my; |
791 } |
833 } |
792 |
834 |
793 private int computeOffsetCubic(double[] pts, final int off, |
835 private int computeOffsetCubic(final double[] pts, final int off, |
794 double[] leftOff, double[] rightOff) |
836 final double[] leftOff, |
837 final double[] rightOff) |
|
795 { |
838 { |
796 // if p1=p2 or p3=p4 it means that the derivative at the endpoint |
839 // if p1=p2 or p3=p4 it means that the derivative at the endpoint |
797 // vanishes, which creates problems with computeOffset. Usually |
840 // vanishes, which creates problems with computeOffset. Usually |
798 // this happens when this stroker object is trying to widen |
841 // this happens when this stroker object is trying to widen |
799 // a curve with a cusp. What happens is that curveTo splits |
842 // a curve with a cusp. What happens is that curveTo splits |
800 // the input curve at the cusp, and passes it to this function. |
843 // the input curve at the cusp, and passes it to this function. |
801 // because of inaccuracies in the splitting, we consider points |
844 // because of inaccuracies in the splitting, we consider points |
802 // equal if they're very close to each other. |
845 // equal if they're very close to each other. |
803 final double x1 = pts[off + 0], y1 = pts[off + 1]; |
846 final double x1 = pts[off ], y1 = pts[off + 1]; |
804 final double x2 = pts[off + 2], y2 = pts[off + 3]; |
847 final double x2 = pts[off + 2], y2 = pts[off + 3]; |
805 final double x3 = pts[off + 4], y3 = pts[off + 5]; |
848 final double x3 = pts[off + 4], y3 = pts[off + 5]; |
806 final double x4 = pts[off + 6], y4 = pts[off + 7]; |
849 final double x4 = pts[off + 6], y4 = pts[off + 7]; |
807 |
850 |
808 double dx4 = x4 - x3; |
851 double dx4 = x4 - x3; |
812 |
855 |
813 // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, |
856 // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, |
814 // in which case ignore if p1 == p2 |
857 // in which case ignore if p1 == p2 |
815 final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2)); |
858 final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2)); |
816 final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0d * Math.ulp(y4)); |
859 final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0d * Math.ulp(y4)); |
860 |
|
817 if (p1eqp2 && p3eqp4) { |
861 if (p1eqp2 && p3eqp4) { |
818 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); |
862 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); |
819 return 4; |
863 return 4; |
820 } else if (p1eqp2) { |
864 } else if (p1eqp2) { |
821 dx1 = x3 - x1; |
865 dx1 = x3 - x1; |
827 |
871 |
828 // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line |
872 // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line |
829 double dotsq = (dx1 * dx4 + dy1 * dy4); |
873 double dotsq = (dx1 * dx4 + dy1 * dy4); |
830 dotsq *= dotsq; |
874 dotsq *= dotsq; |
831 double l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4; |
875 double l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4; |
876 |
|
832 if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) { |
877 if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) { |
833 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); |
878 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); |
834 return 4; |
879 return 4; |
835 } |
880 } |
836 |
881 |
940 } |
985 } |
941 |
986 |
942 // compute offset curves using bezier spline through t=0.5 (i.e. |
987 // compute offset curves using bezier spline through t=0.5 (i.e. |
943 // ComputedCurve(0.5) == IdealParallelCurve(0.5)) |
988 // ComputedCurve(0.5) == IdealParallelCurve(0.5)) |
944 // return the kind of curve in the right and left arrays. |
989 // return the kind of curve in the right and left arrays. |
945 private int computeOffsetQuad(double[] pts, final int off, |
990 private int computeOffsetQuad(final double[] pts, final int off, |
946 double[] leftOff, double[] rightOff) |
991 final double[] leftOff, |
947 { |
992 final double[] rightOff) |
948 final double x1 = pts[off + 0], y1 = pts[off + 1]; |
993 { |
994 final double x1 = pts[off ], y1 = pts[off + 1]; |
|
949 final double x2 = pts[off + 2], y2 = pts[off + 3]; |
995 final double x2 = pts[off + 2], y2 = pts[off + 3]; |
950 final double x3 = pts[off + 4], y3 = pts[off + 5]; |
996 final double x3 = pts[off + 4], y3 = pts[off + 5]; |
951 |
997 |
952 final double dx3 = x3 - x2; |
998 final double dx3 = x3 - x2; |
953 final double dy3 = y3 - y2; |
999 final double dy3 = y3 - y2; |
964 |
1010 |
965 // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, |
1011 // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, |
966 // in which case ignore. |
1012 // in which case ignore. |
967 final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2)); |
1013 final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2)); |
968 final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0d * Math.ulp(y3)); |
1014 final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0d * Math.ulp(y3)); |
1015 |
|
969 if (p1eqp2 || p2eqp3) { |
1016 if (p1eqp2 || p2eqp3) { |
970 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); |
1017 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); |
971 return 4; |
1018 return 4; |
972 } |
1019 } |
973 |
1020 |
974 // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line |
1021 // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line |
975 double dotsq = (dx1 * dx3 + dy1 * dy3); |
1022 double dotsq = (dx1 * dx3 + dy1 * dy3); |
976 dotsq *= dotsq; |
1023 dotsq *= dotsq; |
977 double l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3; |
1024 double l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3; |
1025 |
|
978 if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) { |
1026 if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) { |
979 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); |
1027 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); |
980 return 4; |
1028 return 4; |
981 } |
1029 } |
982 |
1030 |
988 |
1036 |
989 double x1p = x1 + offset0[0]; // start |
1037 double x1p = x1 + offset0[0]; // start |
990 double y1p = y1 + offset0[1]; // point |
1038 double y1p = y1 + offset0[1]; // point |
991 double x3p = x3 + offset1[0]; // end |
1039 double x3p = x3 + offset1[0]; // end |
992 double y3p = y3 + offset1[1]; // point |
1040 double y3p = y3 + offset1[1]; // point |
993 safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2); |
1041 safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff); |
994 leftOff[0] = x1p; leftOff[1] = y1p; |
1042 leftOff[0] = x1p; leftOff[1] = y1p; |
995 leftOff[4] = x3p; leftOff[5] = y3p; |
1043 leftOff[4] = x3p; leftOff[5] = y3p; |
996 |
1044 |
997 x1p = x1 - offset0[0]; y1p = y1 - offset0[1]; |
1045 x1p = x1 - offset0[0]; y1p = y1 - offset0[1]; |
998 x3p = x3 - offset1[0]; y3p = y3 - offset1[1]; |
1046 x3p = x3 - offset1[0]; y3p = y3 - offset1[1]; |
999 safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2); |
1047 safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff); |
1000 rightOff[0] = x1p; rightOff[1] = y1p; |
1048 rightOff[0] = x1p; rightOff[1] = y1p; |
1001 rightOff[4] = x3p; rightOff[5] = y3p; |
1049 rightOff[4] = x3p; rightOff[5] = y3p; |
1002 return 6; |
1050 return 6; |
1003 } |
|
1004 |
|
1005 // finds values of t where the curve in pts should be subdivided in order |
|
1006 // to get good offset curves a distance of w away from the middle curve. |
|
1007 // Stores the points in ts, and returns how many of them there were. |
|
1008 private static int findSubdivPoints(final DCurve c, double[] pts, double[] ts, |
|
1009 final int type, final double w) |
|
1010 { |
|
1011 final double x12 = pts[2] - pts[0]; |
|
1012 final double y12 = pts[3] - pts[1]; |
|
1013 // if the curve is already parallel to either axis we gain nothing |
|
1014 // from rotating it. |
|
1015 if (y12 != 0.0d && x12 != 0.0d) { |
|
1016 // we rotate it so that the first vector in the control polygon is |
|
1017 // parallel to the x-axis. This will ensure that rotated quarter |
|
1018 // circles won't be subdivided. |
|
1019 final double hypot = Math.sqrt(x12 * x12 + y12 * y12); |
|
1020 final double cos = x12 / hypot; |
|
1021 final double sin = y12 / hypot; |
|
1022 final double x1 = cos * pts[0] + sin * pts[1]; |
|
1023 final double y1 = cos * pts[1] - sin * pts[0]; |
|
1024 final double x2 = cos * pts[2] + sin * pts[3]; |
|
1025 final double y2 = cos * pts[3] - sin * pts[2]; |
|
1026 final double x3 = cos * pts[4] + sin * pts[5]; |
|
1027 final double y3 = cos * pts[5] - sin * pts[4]; |
|
1028 |
|
1029 switch(type) { |
|
1030 case 8: |
|
1031 final double x4 = cos * pts[6] + sin * pts[7]; |
|
1032 final double y4 = cos * pts[7] - sin * pts[6]; |
|
1033 c.set(x1, y1, x2, y2, x3, y3, x4, y4); |
|
1034 break; |
|
1035 case 6: |
|
1036 c.set(x1, y1, x2, y2, x3, y3); |
|
1037 break; |
|
1038 default: |
|
1039 } |
|
1040 } else { |
|
1041 c.set(pts, type); |
|
1042 } |
|
1043 |
|
1044 int ret = 0; |
|
1045 // we subdivide at values of t such that the remaining rotated |
|
1046 // curves are monotonic in x and y. |
|
1047 ret += c.dxRoots(ts, ret); |
|
1048 ret += c.dyRoots(ts, ret); |
|
1049 // subdivide at inflection points. |
|
1050 if (type == 8) { |
|
1051 // quadratic curves can't have inflection points |
|
1052 ret += c.infPoints(ts, ret); |
|
1053 } |
|
1054 |
|
1055 // now we must subdivide at points where one of the offset curves will have |
|
1056 // a cusp. This happens at ts where the radius of curvature is equal to w. |
|
1057 ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001d); |
|
1058 |
|
1059 ret = DHelpers.filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d); |
|
1060 DHelpers.isort(ts, 0, ret); |
|
1061 return ret; |
|
1062 } |
1051 } |
1063 |
1052 |
1064 @Override |
1053 @Override |
1065 public void curveTo(final double x1, final double y1, |
1054 public void curveTo(final double x1, final double y1, |
1066 final double x2, final double y2, |
1055 final double x2, final double y2, |
1067 final double x3, final double y3) |
1056 final double x3, final double y3) |
1068 { |
1057 { |
1069 final int outcode0 = this.cOutCode; |
1058 final int outcode0 = this.cOutCode; |
1059 |
|
1070 if (clipRect != null) { |
1060 if (clipRect != null) { |
1061 final int outcode1 = DHelpers.outcode(x1, y1, clipRect); |
|
1062 final int outcode2 = DHelpers.outcode(x2, y2, clipRect); |
|
1071 final int outcode3 = DHelpers.outcode(x3, y3, clipRect); |
1063 final int outcode3 = DHelpers.outcode(x3, y3, clipRect); |
1072 this.cOutCode = outcode3; |
1064 |
1073 |
1065 // Should clip |
1074 if ((outcode0 & outcode3) != 0) { |
1066 final int orCode = (outcode0 | outcode1 | outcode2 | outcode3); |
1075 final int outcode1 = DHelpers.outcode(x1, y1, clipRect); |
1067 if (orCode != 0) { |
1076 final int outcode2 = DHelpers.outcode(x2, y2, clipRect); |
1068 final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; |
1077 |
1069 |
1078 // basic rejection criteria |
1070 // basic rejection criteria: |
1079 if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) { |
1071 if (sideCode == 0) { |
1080 moveTo(x3, y3, outcode0); |
1072 // ovelap clip: |
1073 if (subdivide) { |
|
1074 // avoid reentrance |
|
1075 subdivide = false; |
|
1076 // subdivide curve => callback with subdivided parts: |
|
1077 boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, |
|
1078 x2, y2, x3, y3, |
|
1079 orCode, this); |
|
1080 // reentrance is done: |
|
1081 subdivide = true; |
|
1082 if (ret) { |
|
1083 return; |
|
1084 } |
|
1085 } |
|
1086 // already subdivided so render it |
|
1087 } else { |
|
1088 this.cOutCode = outcode3; |
|
1089 _moveTo(x3, y3, outcode0); |
|
1081 opened = true; |
1090 opened = true; |
1082 return; |
1091 return; |
1083 } |
1092 } |
1084 } |
1093 } |
1085 } |
1094 |
1086 |
1095 this.cOutCode = outcode3; |
1087 final double[] mid = middle; |
1096 } |
1088 |
1097 _curveTo(x1, y1, x2, y2, x3, y3, outcode0); |
1089 mid[0] = cx0; mid[1] = cy0; |
1098 } |
1090 mid[2] = x1; mid[3] = y1; |
1099 |
1091 mid[4] = x2; mid[5] = y2; |
1100 private void _curveTo(final double x1, final double y1, |
1092 mid[6] = x3; mid[7] = y3; |
1101 final double x2, final double y2, |
1093 |
1102 final double x3, final double y3, |
1103 final int outcode0) |
|
1104 { |
|
1094 // need these so we can update the state at the end of this method |
1105 // need these so we can update the state at the end of this method |
1095 final double xf = x3, yf = y3; |
1106 double dxs = x1 - cx0; |
1096 double dxs = mid[2] - mid[0]; |
1107 double dys = y1 - cy0; |
1097 double dys = mid[3] - mid[1]; |
1108 double dxf = x3 - x2; |
1098 double dxf = mid[6] - mid[4]; |
1109 double dyf = y3 - y2; |
1099 double dyf = mid[7] - mid[5]; |
1110 |
1100 |
1111 if ((dxs == 0.0d) && (dys == 0.0d)) { |
1101 boolean p1eqp2 = (dxs == 0.0d && dys == 0.0d); |
1112 dxs = x2 - cx0; |
1102 boolean p3eqp4 = (dxf == 0.0d && dyf == 0.0d); |
1113 dys = y2 - cy0; |
1103 if (p1eqp2) { |
1114 if ((dxs == 0.0d) && (dys == 0.0d)) { |
1104 dxs = mid[4] - mid[0]; |
1115 dxs = x3 - cx0; |
1105 dys = mid[5] - mid[1]; |
1116 dys = y3 - cy0; |
1106 if (dxs == 0.0d && dys == 0.0d) { |
1117 } |
1107 dxs = mid[6] - mid[0]; |
1118 } |
1108 dys = mid[7] - mid[1]; |
1119 if ((dxf == 0.0d) && (dyf == 0.0d)) { |
1109 } |
1120 dxf = x3 - x1; |
1110 } |
1121 dyf = y3 - y1; |
1111 if (p3eqp4) { |
1122 if ((dxf == 0.0d) && (dyf == 0.0d)) { |
1112 dxf = mid[6] - mid[2]; |
1123 dxf = x3 - cx0; |
1113 dyf = mid[7] - mid[3]; |
1124 dyf = y3 - cy0; |
1114 if (dxf == 0.0d && dyf == 0.0d) { |
1125 } |
1115 dxf = mid[6] - mid[0]; |
1126 } |
1116 dyf = mid[7] - mid[1]; |
1127 if ((dxs == 0.0d) && (dys == 0.0d)) { |
1117 } |
|
1118 } |
|
1119 if (dxs == 0.0d && dys == 0.0d) { |
|
1120 // this happens if the "curve" is just a point |
1128 // this happens if the "curve" is just a point |
1121 // fix outcode0 for lineTo() call: |
1129 // fix outcode0 for lineTo() call: |
1122 if (clipRect != null) { |
1130 if (clipRect != null) { |
1123 this.cOutCode = outcode0; |
1131 this.cOutCode = outcode0; |
1124 } |
1132 } |
1125 lineTo(mid[0], mid[1]); |
1133 lineTo(cx0, cy0); |
1126 return; |
1134 return; |
1127 } |
1135 } |
1128 |
1136 |
1129 // if these vectors are too small, normalize them, to avoid future |
1137 // if these vectors are too small, normalize them, to avoid future |
1130 // precision problems. |
1138 // precision problems. |
1131 if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { |
1139 if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { |
1132 double len = Math.sqrt(dxs*dxs + dys*dys); |
1140 final double len = Math.sqrt(dxs * dxs + dys * dys); |
1133 dxs /= len; |
1141 dxs /= len; |
1134 dys /= len; |
1142 dys /= len; |
1135 } |
1143 } |
1136 if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { |
1144 if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { |
1137 double len = Math.sqrt(dxf*dxf + dyf*dyf); |
1145 final double len = Math.sqrt(dxf * dxf + dyf * dyf); |
1138 dxf /= len; |
1146 dxf /= len; |
1139 dyf /= len; |
1147 dyf /= len; |
1140 } |
1148 } |
1141 |
1149 |
1142 computeOffset(dxs, dys, lineWidth2, offset0); |
1150 computeOffset(dxs, dys, lineWidth2, offset0); |
1143 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); |
1151 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); |
1144 |
1152 |
1145 final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2); |
1153 int nSplits = 0; |
1146 |
1154 final double[] mid; |
1147 double prevT = 0.0d; |
|
1148 for (int i = 0, off = 0; i < nSplits; i++, off += 6) { |
|
1149 final double t = subdivTs[i]; |
|
1150 DHelpers.subdivideCubicAt((t - prevT) / (1.0d - prevT), |
|
1151 mid, off, mid, off, mid, off + 6); |
|
1152 prevT = t; |
|
1153 } |
|
1154 |
|
1155 final double[] l = lp; |
1155 final double[] l = lp; |
1156 |
|
1157 if (monotonize) { |
|
1158 // monotonize curve: |
|
1159 final CurveBasicMonotonizer monotonizer |
|
1160 = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3); |
|
1161 |
|
1162 nSplits = monotonizer.nbSplits; |
|
1163 mid = monotonizer.middle; |
|
1164 } else { |
|
1165 // use left instead: |
|
1166 mid = l; |
|
1167 mid[0] = cx0; mid[1] = cy0; |
|
1168 mid[2] = x1; mid[3] = y1; |
|
1169 mid[4] = x2; mid[5] = y2; |
|
1170 mid[6] = x3; mid[7] = y3; |
|
1171 } |
|
1156 final double[] r = rp; |
1172 final double[] r = rp; |
1157 |
1173 |
1158 int kind = 0; |
1174 int kind = 0; |
1159 for (int i = 0, off = 0; i <= nSplits; i++, off += 6) { |
1175 for (int i = 0, off = 0; i <= nSplits; i++, off += 6) { |
1160 kind = computeOffsetCubic(mid, off, l, r); |
1176 kind = computeOffsetCubic(mid, off, l, r); |
1174 } |
1190 } |
1175 emitLineToRev(r[kind - 2], r[kind - 1]); |
1191 emitLineToRev(r[kind - 2], r[kind - 1]); |
1176 } |
1192 } |
1177 |
1193 |
1178 this.prev = DRAWING_OP_TO; |
1194 this.prev = DRAWING_OP_TO; |
1179 this.cx0 = xf; |
1195 this.cx0 = x3; |
1180 this.cy0 = yf; |
1196 this.cy0 = y3; |
1181 this.cdx = dxf; |
1197 this.cdx = dxf; |
1182 this.cdy = dyf; |
1198 this.cdy = dyf; |
1183 this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; |
1199 this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; |
1184 this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; |
1200 this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; |
1185 } |
1201 } |
1187 @Override |
1203 @Override |
1188 public void quadTo(final double x1, final double y1, |
1204 public void quadTo(final double x1, final double y1, |
1189 final double x2, final double y2) |
1205 final double x2, final double y2) |
1190 { |
1206 { |
1191 final int outcode0 = this.cOutCode; |
1207 final int outcode0 = this.cOutCode; |
1208 |
|
1192 if (clipRect != null) { |
1209 if (clipRect != null) { |
1210 final int outcode1 = DHelpers.outcode(x1, y1, clipRect); |
|
1193 final int outcode2 = DHelpers.outcode(x2, y2, clipRect); |
1211 final int outcode2 = DHelpers.outcode(x2, y2, clipRect); |
1194 this.cOutCode = outcode2; |
1212 |
1195 |
1213 // Should clip |
1196 if ((outcode0 & outcode2) != 0) { |
1214 final int orCode = (outcode0 | outcode1 | outcode2); |
1197 final int outcode1 = DHelpers.outcode(x1, y1, clipRect); |
1215 if (orCode != 0) { |
1198 |
1216 final int sideCode = outcode0 & outcode1 & outcode2; |
1199 // basic rejection criteria |
1217 |
1200 if ((outcode0 & outcode1 & outcode2) != 0) { |
1218 // basic rejection criteria: |
1201 moveTo(x2, y2, outcode0); |
1219 if (sideCode == 0) { |
1220 // ovelap clip: |
|
1221 if (subdivide) { |
|
1222 // avoid reentrance |
|
1223 subdivide = false; |
|
1224 // subdivide curve => call lineTo() with subdivided curves: |
|
1225 boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, |
|
1226 x2, y2, orCode, this); |
|
1227 // reentrance is done: |
|
1228 subdivide = true; |
|
1229 if (ret) { |
|
1230 return; |
|
1231 } |
|
1232 } |
|
1233 // already subdivided so render it |
|
1234 } else { |
|
1235 this.cOutCode = outcode2; |
|
1236 _moveTo(x2, y2, outcode0); |
|
1202 opened = true; |
1237 opened = true; |
1203 return; |
1238 return; |
1204 } |
1239 } |
1205 } |
1240 } |
1206 } |
1241 |
1207 |
1242 this.cOutCode = outcode2; |
1208 final double[] mid = middle; |
1243 } |
1209 |
1244 _quadTo(x1, y1, x2, y2, outcode0); |
1210 mid[0] = cx0; mid[1] = cy0; |
1245 } |
1211 mid[2] = x1; mid[3] = y1; |
1246 |
1212 mid[4] = x2; mid[5] = y2; |
1247 private void _quadTo(final double x1, final double y1, |
1213 |
1248 final double x2, final double y2, |
1249 final int outcode0) |
|
1250 { |
|
1214 // need these so we can update the state at the end of this method |
1251 // need these so we can update the state at the end of this method |
1215 final double xf = x2, yf = y2; |
1252 double dxs = x1 - cx0; |
1216 double dxs = mid[2] - mid[0]; |
1253 double dys = y1 - cy0; |
1217 double dys = mid[3] - mid[1]; |
1254 double dxf = x2 - x1; |
1218 double dxf = mid[4] - mid[2]; |
1255 double dyf = y2 - y1; |
1219 double dyf = mid[5] - mid[3]; |
1256 |
1220 if ((dxs == 0.0d && dys == 0.0d) || (dxf == 0.0d && dyf == 0.0d)) { |
1257 if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) { |
1221 dxs = dxf = mid[4] - mid[0]; |
1258 dxs = dxf = x2 - cx0; |
1222 dys = dyf = mid[5] - mid[1]; |
1259 dys = dyf = y2 - cy0; |
1223 } |
1260 } |
1224 if (dxs == 0.0d && dys == 0.0d) { |
1261 if ((dxs == 0.0d) && (dys == 0.0d)) { |
1225 // this happens if the "curve" is just a point |
1262 // this happens if the "curve" is just a point |
1226 // fix outcode0 for lineTo() call: |
1263 // fix outcode0 for lineTo() call: |
1227 if (clipRect != null) { |
1264 if (clipRect != null) { |
1228 this.cOutCode = outcode0; |
1265 this.cOutCode = outcode0; |
1229 } |
1266 } |
1230 lineTo(mid[0], mid[1]); |
1267 lineTo(cx0, cy0); |
1231 return; |
1268 return; |
1232 } |
1269 } |
1233 // if these vectors are too small, normalize them, to avoid future |
1270 // if these vectors are too small, normalize them, to avoid future |
1234 // precision problems. |
1271 // precision problems. |
1235 if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { |
1272 if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { |
1236 double len = Math.sqrt(dxs*dxs + dys*dys); |
1273 final double len = Math.sqrt(dxs * dxs + dys * dys); |
1237 dxs /= len; |
1274 dxs /= len; |
1238 dys /= len; |
1275 dys /= len; |
1239 } |
1276 } |
1240 if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { |
1277 if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { |
1241 double len = Math.sqrt(dxf*dxf + dyf*dyf); |
1278 final double len = Math.sqrt(dxf * dxf + dyf * dyf); |
1242 dxf /= len; |
1279 dxf /= len; |
1243 dyf /= len; |
1280 dyf /= len; |
1244 } |
1281 } |
1245 |
|
1246 computeOffset(dxs, dys, lineWidth2, offset0); |
1282 computeOffset(dxs, dys, lineWidth2, offset0); |
1247 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); |
1283 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); |
1248 |
1284 |
1249 int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2); |
1285 int nSplits = 0; |
1250 |
1286 final double[] mid; |
1251 double prevt = 0.0d; |
|
1252 for (int i = 0, off = 0; i < nSplits; i++, off += 4) { |
|
1253 final double t = subdivTs[i]; |
|
1254 DHelpers.subdivideQuadAt((t - prevt) / (1.0d - prevt), |
|
1255 mid, off, mid, off, mid, off + 4); |
|
1256 prevt = t; |
|
1257 } |
|
1258 |
|
1259 final double[] l = lp; |
1287 final double[] l = lp; |
1288 |
|
1289 if (monotonize) { |
|
1290 // monotonize quad: |
|
1291 final CurveBasicMonotonizer monotonizer |
|
1292 = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2); |
|
1293 |
|
1294 nSplits = monotonizer.nbSplits; |
|
1295 mid = monotonizer.middle; |
|
1296 } else { |
|
1297 // use left instead: |
|
1298 mid = l; |
|
1299 mid[0] = cx0; mid[1] = cy0; |
|
1300 mid[2] = x1; mid[3] = y1; |
|
1301 mid[4] = x2; mid[5] = y2; |
|
1302 } |
|
1260 final double[] r = rp; |
1303 final double[] r = rp; |
1261 |
1304 |
1262 int kind = 0; |
1305 int kind = 0; |
1263 for (int i = 0, off = 0; i <= nSplits; i++, off += 4) { |
1306 for (int i = 0, off = 0; i <= nSplits; i++, off += 4) { |
1264 kind = computeOffsetQuad(mid, off, l, r); |
1307 kind = computeOffsetQuad(mid, off, l, r); |
1278 } |
1321 } |
1279 emitLineToRev(r[kind - 2], r[kind - 1]); |
1322 emitLineToRev(r[kind - 2], r[kind - 1]); |
1280 } |
1323 } |
1281 |
1324 |
1282 this.prev = DRAWING_OP_TO; |
1325 this.prev = DRAWING_OP_TO; |
1283 this.cx0 = xf; |
1326 this.cx0 = x2; |
1284 this.cy0 = yf; |
1327 this.cy0 = y2; |
1285 this.cdx = dxf; |
1328 this.cdx = dxf; |
1286 this.cdy = dyf; |
1329 this.cdy = dyf; |
1287 this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; |
1330 this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; |
1288 this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; |
1331 this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; |
1289 } |
1332 } |