author | lbourges |
Tue, 08 May 2018 10:13:21 +0200 | |
changeset 50135 | 793e481c7641 |
parent 49496 | 1ea202af7a97 |
child 57929 | 13178f7e75d5 |
permissions | -rw-r--r-- |
34417 | 1 |
/* |
49496 | 2 |
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. |
34417 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
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 |
|
7 |
* published by the Free Software Foundation. Oracle designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Oracle in the LICENSE file that accompanied this code. |
|
10 |
* |
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
24 |
*/ |
|
25 |
||
26 |
package sun.java2d.marlin; |
|
27 |
||
28 |
import java.util.Arrays; |
|
29 |
import sun.awt.geom.PathConsumer2D; |
|
49496 | 30 |
import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer; |
31 |
import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter; |
|
34417 | 32 |
|
33 |
/** |
|
34 |
* The <code>Dasher</code> class takes a series of linear commands |
|
35 |
* (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and |
|
36 |
* <code>end</code>) and breaks them into smaller segments according to a |
|
37 |
* dash pattern array and a starting dash phase. |
|
38 |
* |
|
39 |
* <p> Issues: in J2Se, a zero length dash segment as drawn as a very |
|
40 |
* short dash, whereas Pisces does not draw anything. The PostScript |
|
41 |
* semantics are unclear. |
|
42 |
* |
|
43 |
*/ |
|
47126 | 44 |
final class Dasher implements PathConsumer2D, MarlinConst { |
34417 | 45 |
|
49496 | 46 |
/* huge circle with radius ~ 2E9 only needs 12 subdivision levels */ |
47 |
static final int REC_LIMIT = 16; |
|
48 |
static final float CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 |
|
47126 | 49 |
static final float MIN_T_INC = 1.0f / (1 << REC_LIMIT); |
50 |
||
51 |
// More than 24 bits of mantissa means we can no longer accurately |
|
52 |
// measure the number of times cycled through the dash array so we |
|
53 |
// punt and override the phase to just be 0 past that point. |
|
54 |
static final float MAX_CYCLES = 16000000.0f; |
|
34417 | 55 |
|
56 |
private PathConsumer2D out; |
|
57 |
private float[] dash; |
|
58 |
private int dashLen; |
|
59 |
private float startPhase; |
|
60 |
private boolean startDashOn; |
|
61 |
private int startIdx; |
|
62 |
||
63 |
private boolean starting; |
|
64 |
private boolean needsMoveTo; |
|
65 |
||
66 |
private int idx; |
|
67 |
private boolean dashOn; |
|
68 |
private float phase; |
|
69 |
||
49496 | 70 |
// The starting point of the path |
71 |
private float sx0, sy0; |
|
72 |
// the current point |
|
73 |
private float cx0, cy0; |
|
34417 | 74 |
|
75 |
// temporary storage for the current curve |
|
76 |
private final float[] curCurvepts; |
|
77 |
||
78 |
// per-thread renderer context |
|
79 |
final RendererContext rdrCtx; |
|
80 |
||
81 |
// flag to recycle dash array copy |
|
82 |
boolean recycleDashes; |
|
83 |
||
49496 | 84 |
// We don't emit the first dash right away. If we did, caps would be |
85 |
// drawn on it, but we need joins to be drawn if there's a closePath() |
|
86 |
// So, we store the path elements that make up the first dash in the |
|
87 |
// buffer below. |
|
88 |
private float[] firstSegmentsBuffer; // dynamic array |
|
89 |
private int firstSegidx; |
|
90 |
||
40421
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
91 |
// dashes ref (dirty) |
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
92 |
final FloatArrayCache.Reference dashes_ref; |
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
93 |
// firstSegmentsBuffer ref (dirty) |
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
94 |
final FloatArrayCache.Reference firstSegmentsBuffer_ref; |
34417 | 95 |
|
49496 | 96 |
// Bounds of the drawing region, at pixel precision. |
97 |
private float[] clipRect; |
|
98 |
||
99 |
// the outcode of the current point |
|
100 |
private int cOutCode = 0; |
|
101 |
||
102 |
private boolean subdivide = DO_CLIP_SUBDIVIDER; |
|
103 |
||
104 |
private final LengthIterator li = new LengthIterator(); |
|
105 |
||
106 |
private final CurveClipSplitter curveSplitter; |
|
107 |
||
108 |
private float cycleLen; |
|
109 |
private boolean outside; |
|
110 |
private float totalSkipLen; |
|
111 |
||
34417 | 112 |
/** |
113 |
* Constructs a <code>Dasher</code>. |
|
114 |
* @param rdrCtx per-thread renderer context |
|
115 |
*/ |
|
116 |
Dasher(final RendererContext rdrCtx) { |
|
117 |
this.rdrCtx = rdrCtx; |
|
118 |
||
40421
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
119 |
dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K |
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
120 |
|
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
121 |
firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K |
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
122 |
firstSegmentsBuffer = firstSegmentsBuffer_ref.initial; |
34417 | 123 |
|
124 |
// we need curCurvepts to be able to contain 2 curves because when |
|
125 |
// dashing curves, we need to subdivide it |
|
126 |
curCurvepts = new float[8 * 2]; |
|
49496 | 127 |
|
128 |
this.curveSplitter = rdrCtx.curveClipSplitter; |
|
34417 | 129 |
} |
130 |
||
131 |
/** |
|
132 |
* Initialize the <code>Dasher</code>. |
|
133 |
* |
|
134 |
* @param out an output <code>PathConsumer2D</code>. |
|
135 |
* @param dash an array of <code>float</code>s containing the dash pattern |
|
136 |
* @param dashLen length of the given dash array |
|
137 |
* @param phase a <code>float</code> containing the dash phase |
|
138 |
* @param recycleDashes true to indicate to recycle the given dash array |
|
139 |
* @return this instance |
|
140 |
*/ |
|
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
141 |
Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen, |
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
142 |
float phase, final boolean recycleDashes) |
34417 | 143 |
{ |
144 |
this.out = out; |
|
145 |
||
146 |
// Normalize so 0 <= phase < dash[0] |
|
47126 | 147 |
int sidx = 0; |
34417 | 148 |
dashOn = true; |
49496 | 149 |
|
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
150 |
// note: BasicStroke constructor checks dash elements and sum > 0 |
47126 | 151 |
float sum = 0.0f; |
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
152 |
for (int i = 0; i < dashLen; i++) { |
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
153 |
sum += dash[i]; |
47126 | 154 |
} |
49496 | 155 |
this.cycleLen = sum; |
156 |
||
47126 | 157 |
float cycles = phase / sum; |
158 |
if (phase < 0.0f) { |
|
159 |
if (-cycles >= MAX_CYCLES) { |
|
160 |
phase = 0.0f; |
|
161 |
} else { |
|
162 |
int fullcycles = FloatMath.floor_int(-cycles); |
|
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
163 |
if ((fullcycles & dashLen & 1) != 0) { |
47126 | 164 |
dashOn = !dashOn; |
165 |
} |
|
166 |
phase += fullcycles * sum; |
|
167 |
while (phase < 0.0f) { |
|
168 |
if (--sidx < 0) { |
|
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
169 |
sidx = dashLen - 1; |
47126 | 170 |
} |
171 |
phase += dash[sidx]; |
|
172 |
dashOn = !dashOn; |
|
173 |
} |
|
174 |
} |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
175 |
} else if (phase > 0.0f) { |
47126 | 176 |
if (cycles >= MAX_CYCLES) { |
177 |
phase = 0.0f; |
|
178 |
} else { |
|
179 |
int fullcycles = FloatMath.floor_int(cycles); |
|
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
180 |
if ((fullcycles & dashLen & 1) != 0) { |
47126 | 181 |
dashOn = !dashOn; |
182 |
} |
|
183 |
phase -= fullcycles * sum; |
|
184 |
float d; |
|
185 |
while (phase >= (d = dash[sidx])) { |
|
186 |
phase -= d; |
|
50135
793e481c7641
8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application
lbourges
parents:
49496
diff
changeset
|
187 |
sidx = (sidx + 1) % dashLen; |
47126 | 188 |
dashOn = !dashOn; |
189 |
} |
|
190 |
} |
|
34417 | 191 |
} |
192 |
||
193 |
this.dash = dash; |
|
194 |
this.dashLen = dashLen; |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
195 |
this.phase = phase; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
196 |
this.startPhase = phase; |
34417 | 197 |
this.startDashOn = dashOn; |
47126 | 198 |
this.startIdx = sidx; |
34417 | 199 |
this.starting = true; |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
200 |
this.needsMoveTo = false; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
201 |
this.firstSegidx = 0; |
34417 | 202 |
|
203 |
this.recycleDashes = recycleDashes; |
|
204 |
||
49496 | 205 |
if (rdrCtx.doClip) { |
206 |
this.clipRect = rdrCtx.clipRect; |
|
207 |
} else { |
|
208 |
this.clipRect = null; |
|
209 |
this.cOutCode = 0; |
|
210 |
} |
|
34417 | 211 |
return this; // fluent API |
212 |
} |
|
213 |
||
214 |
/** |
|
215 |
* Disposes this dasher: |
|
216 |
* clean up before reusing this instance |
|
217 |
*/ |
|
218 |
void dispose() { |
|
39519
21bfc4452441
8159093: Fix coding conventions in Marlin renderer
lbourges
parents:
34417
diff
changeset
|
219 |
if (DO_CLEAN_DIRTY) { |
34417 | 220 |
// Force zero-fill dirty arrays: |
47126 | 221 |
Arrays.fill(curCurvepts, 0.0f); |
34417 | 222 |
} |
223 |
// Return arrays: |
|
40421
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
224 |
if (recycleDashes) { |
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
225 |
dash = dashes_ref.putArray(dash); |
34417 | 226 |
} |
40421
d5ee65e2b0fb
8159638: Improve array caches and renderer stats in Marlin renderer
lbourges
parents:
39519
diff
changeset
|
227 |
firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer); |
34417 | 228 |
} |
229 |
||
47126 | 230 |
float[] copyDashArray(final float[] dashes) { |
231 |
final int len = dashes.length; |
|
232 |
final float[] newDashes; |
|
233 |
if (len <= MarlinConst.INITIAL_ARRAY) { |
|
234 |
newDashes = dashes_ref.initial; |
|
235 |
} else { |
|
236 |
if (DO_STATS) { |
|
237 |
rdrCtx.stats.stat_array_dasher_dasher.add(len); |
|
238 |
} |
|
239 |
newDashes = dashes_ref.getArray(len); |
|
240 |
} |
|
241 |
System.arraycopy(dashes, 0, newDashes, 0, len); |
|
242 |
return newDashes; |
|
243 |
} |
|
244 |
||
34417 | 245 |
@Override |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
246 |
public void moveTo(final float x0, final float y0) { |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
247 |
if (firstSegidx != 0) { |
49496 | 248 |
out.moveTo(sx0, sy0); |
34417 | 249 |
emitFirstSegments(); |
250 |
} |
|
49496 | 251 |
this.needsMoveTo = true; |
34417 | 252 |
this.idx = startIdx; |
253 |
this.dashOn = this.startDashOn; |
|
254 |
this.phase = this.startPhase; |
|
49496 | 255 |
this.cx0 = x0; |
256 |
this.cy0 = y0; |
|
257 |
||
258 |
// update starting point: |
|
259 |
this.sx0 = x0; |
|
260 |
this.sy0 = y0; |
|
34417 | 261 |
this.starting = true; |
49496 | 262 |
|
263 |
if (clipRect != null) { |
|
264 |
final int outcode = Helpers.outcode(x0, y0, clipRect); |
|
265 |
this.cOutCode = outcode; |
|
266 |
this.outside = false; |
|
267 |
this.totalSkipLen = 0.0f; |
|
268 |
} |
|
34417 | 269 |
} |
270 |
||
271 |
private void emitSeg(float[] buf, int off, int type) { |
|
272 |
switch (type) { |
|
273 |
case 8: |
|
49496 | 274 |
out.curveTo(buf[off ], buf[off + 1], |
275 |
buf[off + 2], buf[off + 3], |
|
276 |
buf[off + 4], buf[off + 5]); |
|
34417 | 277 |
return; |
278 |
case 6: |
|
49496 | 279 |
out.quadTo(buf[off ], buf[off + 1], |
280 |
buf[off + 2], buf[off + 3]); |
|
34417 | 281 |
return; |
282 |
case 4: |
|
49496 | 283 |
out.lineTo(buf[off], buf[off + 1]); |
34417 | 284 |
return; |
285 |
default: |
|
286 |
} |
|
287 |
} |
|
288 |
||
289 |
private void emitFirstSegments() { |
|
290 |
final float[] fSegBuf = firstSegmentsBuffer; |
|
291 |
||
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
292 |
for (int i = 0, len = firstSegidx; i < len; ) { |
34417 | 293 |
int type = (int)fSegBuf[i]; |
294 |
emitSeg(fSegBuf, i + 1, type); |
|
295 |
i += (type - 1); |
|
296 |
} |
|
297 |
firstSegidx = 0; |
|
298 |
} |
|
299 |
||
300 |
// precondition: pts must be in relative coordinates (relative to x0,y0) |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
301 |
private void goTo(final float[] pts, final int off, final int type, |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
302 |
final boolean on) |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
303 |
{ |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
304 |
final int index = off + type; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
305 |
final float x = pts[index - 4]; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
306 |
final float y = pts[index - 3]; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
307 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
308 |
if (on) { |
34417 | 309 |
if (starting) { |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
310 |
goTo_starting(pts, off, type); |
34417 | 311 |
} else { |
312 |
if (needsMoveTo) { |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
313 |
needsMoveTo = false; |
49496 | 314 |
out.moveTo(cx0, cy0); |
34417 | 315 |
} |
316 |
emitSeg(pts, off, type); |
|
317 |
} |
|
318 |
} else { |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
319 |
if (starting) { |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
320 |
// low probability test (hotspot) |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
321 |
starting = false; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
322 |
} |
34417 | 323 |
needsMoveTo = true; |
324 |
} |
|
49496 | 325 |
this.cx0 = x; |
326 |
this.cy0 = y; |
|
34417 | 327 |
} |
328 |
||
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
329 |
private void goTo_starting(final float[] pts, final int off, final int type) { |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
330 |
int len = type - 1; // - 2 + 1 |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
331 |
int segIdx = firstSegidx; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
332 |
float[] buf = firstSegmentsBuffer; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
333 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
334 |
if (segIdx + len > buf.length) { |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
335 |
if (DO_STATS) { |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
336 |
rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
337 |
.add(segIdx + len); |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
338 |
} |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
339 |
firstSegmentsBuffer = buf |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
340 |
= firstSegmentsBuffer_ref.widenArray(buf, segIdx, |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
341 |
segIdx + len); |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
342 |
} |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
343 |
buf[segIdx++] = type; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
344 |
len--; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
345 |
// small arraycopy (2, 4 or 6) but with offset: |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
346 |
System.arraycopy(pts, off, buf, segIdx, len); |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
347 |
firstSegidx = segIdx + len; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
348 |
} |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
349 |
|
34417 | 350 |
@Override |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
351 |
public void lineTo(final float x1, final float y1) { |
49496 | 352 |
final int outcode0 = this.cOutCode; |
353 |
||
354 |
if (clipRect != null) { |
|
355 |
final int outcode1 = Helpers.outcode(x1, y1, clipRect); |
|
356 |
||
357 |
// Should clip |
|
358 |
final int orCode = (outcode0 | outcode1); |
|
359 |
||
360 |
if (orCode != 0) { |
|
361 |
final int sideCode = outcode0 & outcode1; |
|
34417 | 362 |
|
49496 | 363 |
// basic rejection criteria: |
364 |
if (sideCode == 0) { |
|
365 |
// ovelap clip: |
|
366 |
if (subdivide) { |
|
367 |
// avoid reentrance |
|
368 |
subdivide = false; |
|
369 |
// subdivide curve => callback with subdivided parts: |
|
370 |
boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1, |
|
371 |
orCode, this); |
|
372 |
// reentrance is done: |
|
373 |
subdivide = true; |
|
374 |
if (ret) { |
|
375 |
return; |
|
376 |
} |
|
377 |
} |
|
378 |
// already subdivided so render it |
|
379 |
} else { |
|
380 |
this.cOutCode = outcode1; |
|
381 |
skipLineTo(x1, y1); |
|
382 |
return; |
|
383 |
} |
|
384 |
} |
|
385 |
||
386 |
this.cOutCode = outcode1; |
|
387 |
||
388 |
if (this.outside) { |
|
389 |
this.outside = false; |
|
390 |
// Adjust current index, phase & dash: |
|
391 |
skipLen(); |
|
392 |
} |
|
393 |
} |
|
394 |
_lineTo(x1, y1); |
|
395 |
} |
|
396 |
||
397 |
private void _lineTo(final float x1, final float y1) { |
|
398 |
final float dx = x1 - cx0; |
|
399 |
final float dy = y1 - cy0; |
|
400 |
||
401 |
float len = dx * dx + dy * dy; |
|
47126 | 402 |
if (len == 0.0f) { |
34417 | 403 |
return; |
404 |
} |
|
405 |
len = (float) Math.sqrt(len); |
|
406 |
||
407 |
// The scaling factors needed to get the dx and dy of the |
|
408 |
// transformed dash segments. |
|
409 |
final float cx = dx / len; |
|
410 |
final float cy = dy / len; |
|
411 |
||
412 |
final float[] _curCurvepts = curCurvepts; |
|
413 |
final float[] _dash = dash; |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
414 |
final int _dashLen = this.dashLen; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
415 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
416 |
int _idx = idx; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
417 |
boolean _dashOn = dashOn; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
418 |
float _phase = phase; |
34417 | 419 |
|
49496 | 420 |
float leftInThisDashSegment, d; |
34417 | 421 |
|
422 |
while (true) { |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
423 |
d = _dash[_idx]; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
424 |
leftInThisDashSegment = d - _phase; |
34417 | 425 |
|
426 |
if (len <= leftInThisDashSegment) { |
|
427 |
_curCurvepts[0] = x1; |
|
428 |
_curCurvepts[1] = y1; |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
429 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
430 |
goTo(_curCurvepts, 0, 4, _dashOn); |
34417 | 431 |
|
432 |
// Advance phase within current dash segment |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
433 |
_phase += len; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
434 |
|
34417 | 435 |
// TODO: compare float values using epsilon: |
436 |
if (len == leftInThisDashSegment) { |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
437 |
_phase = 0.0f; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
438 |
_idx = (_idx + 1) % _dashLen; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
439 |
_dashOn = !_dashOn; |
34417 | 440 |
} |
49496 | 441 |
break; |
34417 | 442 |
} |
443 |
||
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
444 |
if (_phase == 0.0f) { |
49496 | 445 |
_curCurvepts[0] = cx0 + d * cx; |
446 |
_curCurvepts[1] = cy0 + d * cy; |
|
34417 | 447 |
} else { |
49496 | 448 |
_curCurvepts[0] = cx0 + leftInThisDashSegment * cx; |
449 |
_curCurvepts[1] = cy0 + leftInThisDashSegment * cy; |
|
34417 | 450 |
} |
451 |
||
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
452 |
goTo(_curCurvepts, 0, 4, _dashOn); |
34417 | 453 |
|
454 |
len -= leftInThisDashSegment; |
|
455 |
// Advance to next dash segment |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
456 |
_idx = (_idx + 1) % _dashLen; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
457 |
_dashOn = !_dashOn; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
458 |
_phase = 0.0f; |
34417 | 459 |
} |
49496 | 460 |
// Save local state: |
461 |
idx = _idx; |
|
462 |
dashOn = _dashOn; |
|
463 |
phase = _phase; |
|
464 |
} |
|
465 |
||
466 |
private void skipLineTo(final float x1, final float y1) { |
|
467 |
final float dx = x1 - cx0; |
|
468 |
final float dy = y1 - cy0; |
|
469 |
||
470 |
float len = dx * dx + dy * dy; |
|
471 |
if (len != 0.0f) { |
|
472 |
len = (float)Math.sqrt(len); |
|
473 |
} |
|
474 |
||
475 |
// Accumulate skipped length: |
|
476 |
this.outside = true; |
|
477 |
this.totalSkipLen += len; |
|
478 |
||
479 |
// Fix initial move: |
|
480 |
this.needsMoveTo = true; |
|
481 |
this.starting = false; |
|
482 |
||
483 |
this.cx0 = x1; |
|
484 |
this.cy0 = y1; |
|
34417 | 485 |
} |
486 |
||
49496 | 487 |
public void skipLen() { |
488 |
float len = this.totalSkipLen; |
|
489 |
this.totalSkipLen = 0.0f; |
|
490 |
||
491 |
final float[] _dash = dash; |
|
492 |
final int _dashLen = this.dashLen; |
|
493 |
||
494 |
int _idx = idx; |
|
495 |
boolean _dashOn = dashOn; |
|
496 |
float _phase = phase; |
|
497 |
||
498 |
// -2 to ensure having 2 iterations of the post-loop |
|
499 |
// to compensate the remaining phase |
|
500 |
final long fullcycles = (long)Math.floor(len / cycleLen) - 2L; |
|
501 |
||
502 |
if (fullcycles > 0L) { |
|
503 |
len -= cycleLen * fullcycles; |
|
504 |
||
505 |
final long iterations = fullcycles * _dashLen; |
|
506 |
_idx = (int) (iterations + _idx) % _dashLen; |
|
507 |
_dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L; |
|
508 |
} |
|
509 |
||
510 |
float leftInThisDashSegment, d; |
|
511 |
||
512 |
while (true) { |
|
513 |
d = _dash[_idx]; |
|
514 |
leftInThisDashSegment = d - _phase; |
|
515 |
||
516 |
if (len <= leftInThisDashSegment) { |
|
517 |
// Advance phase within current dash segment |
|
518 |
_phase += len; |
|
519 |
||
520 |
// TODO: compare float values using epsilon: |
|
521 |
if (len == leftInThisDashSegment) { |
|
522 |
_phase = 0.0f; |
|
523 |
_idx = (_idx + 1) % _dashLen; |
|
524 |
_dashOn = !_dashOn; |
|
525 |
} |
|
526 |
break; |
|
527 |
} |
|
528 |
||
529 |
len -= leftInThisDashSegment; |
|
530 |
// Advance to next dash segment |
|
531 |
_idx = (_idx + 1) % _dashLen; |
|
532 |
_dashOn = !_dashOn; |
|
533 |
_phase = 0.0f; |
|
534 |
} |
|
535 |
// Save local state: |
|
536 |
idx = _idx; |
|
537 |
dashOn = _dashOn; |
|
538 |
phase = _phase; |
|
539 |
} |
|
34417 | 540 |
|
541 |
// preconditions: curCurvepts must be an array of length at least 2 * type, |
|
542 |
// that contains the curve we want to dash in the first type elements |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
543 |
private void somethingTo(final int type) { |
49496 | 544 |
final float[] _curCurvepts = curCurvepts; |
545 |
if (pointCurve(_curCurvepts, type)) { |
|
34417 | 546 |
return; |
547 |
} |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
548 |
final LengthIterator _li = li; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
549 |
final float[] _dash = dash; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
550 |
final int _dashLen = this.dashLen; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
551 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
552 |
_li.initializeIterationOnCurve(_curCurvepts, type); |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
553 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
554 |
int _idx = idx; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
555 |
boolean _dashOn = dashOn; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
556 |
float _phase = phase; |
34417 | 557 |
|
558 |
// initially the current curve is at curCurvepts[0...type] |
|
559 |
int curCurveoff = 0; |
|
49496 | 560 |
float prevT = 0.0f; |
34417 | 561 |
float t; |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
562 |
float leftInThisDashSegment = _dash[_idx] - _phase; |
34417 | 563 |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
564 |
while ((t = _li.next(leftInThisDashSegment)) < 1.0f) { |
47126 | 565 |
if (t != 0.0f) { |
49496 | 566 |
Helpers.subdivideAt((t - prevT) / (1.0f - prevT), |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
567 |
_curCurvepts, curCurveoff, |
49496 | 568 |
_curCurvepts, 0, type); |
569 |
prevT = t; |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
570 |
goTo(_curCurvepts, 2, type, _dashOn); |
34417 | 571 |
curCurveoff = type; |
572 |
} |
|
573 |
// Advance to next dash segment |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
574 |
_idx = (_idx + 1) % _dashLen; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
575 |
_dashOn = !_dashOn; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
576 |
_phase = 0.0f; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
577 |
leftInThisDashSegment = _dash[_idx]; |
34417 | 578 |
} |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
579 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
580 |
goTo(_curCurvepts, curCurveoff + 2, type, _dashOn); |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
581 |
|
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
582 |
_phase += _li.lastSegLen(); |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
583 |
if (_phase >= _dash[_idx]) { |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
584 |
_phase = 0.0f; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
585 |
_idx = (_idx + 1) % _dashLen; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
586 |
_dashOn = !_dashOn; |
34417 | 587 |
} |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
588 |
// Save local state: |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
589 |
idx = _idx; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
590 |
dashOn = _dashOn; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
591 |
phase = _phase; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
592 |
|
34417 | 593 |
// reset LengthIterator: |
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
594 |
_li.reset(); |
34417 | 595 |
} |
596 |
||
49496 | 597 |
private void skipSomethingTo(final int type) { |
598 |
final float[] _curCurvepts = curCurvepts; |
|
599 |
if (pointCurve(_curCurvepts, type)) { |
|
600 |
return; |
|
601 |
} |
|
602 |
final LengthIterator _li = li; |
|
603 |
||
604 |
_li.initializeIterationOnCurve(_curCurvepts, type); |
|
605 |
||
606 |
// In contrary to somethingTo(), |
|
607 |
// just estimate properly the curve length: |
|
608 |
final float len = _li.totalLength(); |
|
609 |
||
610 |
// Accumulate skipped length: |
|
611 |
this.outside = true; |
|
612 |
this.totalSkipLen += len; |
|
613 |
||
614 |
// Fix initial move: |
|
615 |
this.needsMoveTo = true; |
|
616 |
this.starting = false; |
|
617 |
} |
|
618 |
||
619 |
private static boolean pointCurve(final float[] curve, final int type) { |
|
34417 | 620 |
for (int i = 2; i < type; i++) { |
621 |
if (curve[i] != curve[i-2]) { |
|
622 |
return false; |
|
623 |
} |
|
624 |
} |
|
625 |
return true; |
|
626 |
} |
|
627 |
||
628 |
// Objects of this class are used to iterate through curves. They return |
|
629 |
// t values where the left side of the curve has a specified length. |
|
630 |
// It does this by subdividing the input curve until a certain error |
|
631 |
// condition has been met. A recursive subdivision procedure would |
|
632 |
// return as many as 1<<limit curves, but this is an iterator and we |
|
633 |
// don't need all the curves all at once, so what we carry out a |
|
634 |
// lazy inorder traversal of the recursion tree (meaning we only move |
|
635 |
// through the tree when we need the next subdivided curve). This saves |
|
636 |
// us a lot of memory because at any one time we only need to store |
|
637 |
// limit+1 curves - one for each level of the tree + 1. |
|
638 |
// NOTE: the way we do things here is not enough to traverse a general |
|
639 |
// tree; however, the trees we are interested in have the property that |
|
640 |
// every non leaf node has exactly 2 children |
|
641 |
static final class LengthIterator { |
|
642 |
// Holds the curves at various levels of the recursion. The root |
|
643 |
// (i.e. the original curve) is at recCurveStack[0] (but then it |
|
644 |
// gets subdivided, the left half is put at 1, so most of the time |
|
645 |
// only the right half of the original curve is at 0) |
|
646 |
private final float[][] recCurveStack; // dirty |
|
49496 | 647 |
// sidesRight[i] indicates whether the node at level i+1 in the path from |
34417 | 648 |
// the root to the current leaf is a left or right child of its parent. |
49496 | 649 |
private final boolean[] sidesRight; // dirty |
34417 | 650 |
private int curveType; |
651 |
// lastT and nextT delimit the current leaf. |
|
652 |
private float nextT; |
|
653 |
private float lenAtNextT; |
|
654 |
private float lastT; |
|
655 |
private float lenAtLastT; |
|
656 |
private float lenAtLastSplit; |
|
657 |
private float lastSegLen; |
|
658 |
// the current level in the recursion tree. 0 is the root. limit |
|
659 |
// is the deepest possible leaf. |
|
660 |
private int recLevel; |
|
661 |
private boolean done; |
|
662 |
||
663 |
// the lengths of the lines of the control polygon. Only its first |
|
664 |
// curveType/2 - 1 elements are valid. This is an optimization. See |
|
47126 | 665 |
// next() for more detail. |
34417 | 666 |
private final float[] curLeafCtrlPolyLengths = new float[3]; |
667 |
||
668 |
LengthIterator() { |
|
39519
21bfc4452441
8159093: Fix coding conventions in Marlin renderer
lbourges
parents:
34417
diff
changeset
|
669 |
this.recCurveStack = new float[REC_LIMIT + 1][8]; |
49496 | 670 |
this.sidesRight = new boolean[REC_LIMIT]; |
34417 | 671 |
// if any methods are called without first initializing this object |
672 |
// on a curve, we want it to fail ASAP. |
|
673 |
this.nextT = Float.MAX_VALUE; |
|
674 |
this.lenAtNextT = Float.MAX_VALUE; |
|
675 |
this.lenAtLastSplit = Float.MIN_VALUE; |
|
676 |
this.recLevel = Integer.MIN_VALUE; |
|
677 |
this.lastSegLen = Float.MAX_VALUE; |
|
678 |
this.done = true; |
|
679 |
} |
|
680 |
||
681 |
/** |
|
682 |
* Reset this LengthIterator. |
|
683 |
*/ |
|
684 |
void reset() { |
|
685 |
// keep data dirty |
|
686 |
// as it appears not useful to reset data: |
|
39519
21bfc4452441
8159093: Fix coding conventions in Marlin renderer
lbourges
parents:
34417
diff
changeset
|
687 |
if (DO_CLEAN_DIRTY) { |
34417 | 688 |
final int recLimit = recCurveStack.length - 1; |
689 |
for (int i = recLimit; i >= 0; i--) { |
|
47126 | 690 |
Arrays.fill(recCurveStack[i], 0.0f); |
34417 | 691 |
} |
49496 | 692 |
Arrays.fill(sidesRight, false); |
47126 | 693 |
Arrays.fill(curLeafCtrlPolyLengths, 0.0f); |
694 |
Arrays.fill(nextRoots, 0.0f); |
|
695 |
Arrays.fill(flatLeafCoefCache, 0.0f); |
|
696 |
flatLeafCoefCache[2] = -1.0f; |
|
34417 | 697 |
} |
698 |
} |
|
699 |
||
49496 | 700 |
void initializeIterationOnCurve(final float[] pts, final int type) { |
34417 | 701 |
// optimize arraycopy (8 values faster than 6 = type): |
702 |
System.arraycopy(pts, 0, recCurveStack[0], 0, 8); |
|
703 |
this.curveType = type; |
|
704 |
this.recLevel = 0; |
|
47126 | 705 |
this.lastT = 0.0f; |
706 |
this.lenAtLastT = 0.0f; |
|
707 |
this.nextT = 0.0f; |
|
708 |
this.lenAtNextT = 0.0f; |
|
34417 | 709 |
goLeft(); // initializes nextT and lenAtNextT properly |
47126 | 710 |
this.lenAtLastSplit = 0.0f; |
34417 | 711 |
if (recLevel > 0) { |
49496 | 712 |
this.sidesRight[0] = false; |
34417 | 713 |
this.done = false; |
714 |
} else { |
|
715 |
// the root of the tree is a leaf so we're done. |
|
49496 | 716 |
this.sidesRight[0] = true; |
34417 | 717 |
this.done = true; |
718 |
} |
|
47126 | 719 |
this.lastSegLen = 0.0f; |
34417 | 720 |
} |
721 |
||
722 |
// 0 == false, 1 == true, -1 == invalid cached value. |
|
723 |
private int cachedHaveLowAcceleration = -1; |
|
724 |
||
49496 | 725 |
private boolean haveLowAcceleration(final float err) { |
34417 | 726 |
if (cachedHaveLowAcceleration == -1) { |
727 |
final float len1 = curLeafCtrlPolyLengths[0]; |
|
728 |
final float len2 = curLeafCtrlPolyLengths[1]; |
|
729 |
// the test below is equivalent to !within(len1/len2, 1, err). |
|
730 |
// It is using a multiplication instead of a division, so it |
|
731 |
// should be a bit faster. |
|
47126 | 732 |
if (!Helpers.within(len1, len2, err * len2)) { |
34417 | 733 |
cachedHaveLowAcceleration = 0; |
734 |
return false; |
|
735 |
} |
|
736 |
if (curveType == 8) { |
|
737 |
final float len3 = curLeafCtrlPolyLengths[2]; |
|
738 |
// if len1 is close to 2 and 2 is close to 3, that probably |
|
739 |
// means 1 is close to 3 so the second part of this test might |
|
740 |
// not be needed, but it doesn't hurt to include it. |
|
741 |
final float errLen3 = err * len3; |
|
742 |
if (!(Helpers.within(len2, len3, errLen3) && |
|
743 |
Helpers.within(len1, len3, errLen3))) { |
|
744 |
cachedHaveLowAcceleration = 0; |
|
745 |
return false; |
|
746 |
} |
|
747 |
} |
|
748 |
cachedHaveLowAcceleration = 1; |
|
749 |
return true; |
|
750 |
} |
|
751 |
||
752 |
return (cachedHaveLowAcceleration == 1); |
|
753 |
} |
|
754 |
||
755 |
// we want to avoid allocations/gc so we keep this array so we |
|
756 |
// can put roots in it, |
|
757 |
private final float[] nextRoots = new float[4]; |
|
758 |
||
759 |
// caches the coefficients of the current leaf in its flattened |
|
760 |
// form (see inside next() for what that means). The cache is |
|
761 |
// invalid when it's third element is negative, since in any |
|
762 |
// valid flattened curve, this would be >= 0. |
|
47126 | 763 |
private final float[] flatLeafCoefCache = new float[]{0.0f, 0.0f, -1.0f, 0.0f}; |
34417 | 764 |
|
765 |
// returns the t value where the remaining curve should be split in |
|
766 |
// order for the left subdivided curve to have length len. If len |
|
767 |
// is >= than the length of the uniterated curve, it returns 1. |
|
768 |
float next(final float len) { |
|
769 |
final float targetLength = lenAtLastSplit + len; |
|
770 |
while (lenAtNextT < targetLength) { |
|
771 |
if (done) { |
|
772 |
lastSegLen = lenAtNextT - lenAtLastSplit; |
|
47126 | 773 |
return 1.0f; |
34417 | 774 |
} |
775 |
goToNextLeaf(); |
|
776 |
} |
|
777 |
lenAtLastSplit = targetLength; |
|
778 |
final float leaflen = lenAtNextT - lenAtLastT; |
|
779 |
float t = (targetLength - lenAtLastT) / leaflen; |
|
780 |
||
781 |
// cubicRootsInAB is a fairly expensive call, so we just don't do it |
|
782 |
// if the acceleration in this section of the curve is small enough. |
|
783 |
if (!haveLowAcceleration(0.05f)) { |
|
784 |
// We flatten the current leaf along the x axis, so that we're |
|
785 |
// left with a, b, c which define a 1D Bezier curve. We then |
|
786 |
// solve this to get the parameter of the original leaf that |
|
787 |
// gives us the desired length. |
|
788 |
final float[] _flatLeafCoefCache = flatLeafCoefCache; |
|
789 |
||
47126 | 790 |
if (_flatLeafCoefCache[2] < 0.0f) { |
791 |
float x = curLeafCtrlPolyLengths[0], |
|
792 |
y = x + curLeafCtrlPolyLengths[1]; |
|
34417 | 793 |
if (curveType == 8) { |
794 |
float z = y + curLeafCtrlPolyLengths[2]; |
|
47126 | 795 |
_flatLeafCoefCache[0] = 3.0f * (x - y) + z; |
796 |
_flatLeafCoefCache[1] = 3.0f * (y - 2.0f * x); |
|
797 |
_flatLeafCoefCache[2] = 3.0f * x; |
|
34417 | 798 |
_flatLeafCoefCache[3] = -z; |
799 |
} else if (curveType == 6) { |
|
47126 | 800 |
_flatLeafCoefCache[0] = 0.0f; |
801 |
_flatLeafCoefCache[1] = y - 2.0f * x; |
|
802 |
_flatLeafCoefCache[2] = 2.0f * x; |
|
34417 | 803 |
_flatLeafCoefCache[3] = -y; |
804 |
} |
|
805 |
} |
|
806 |
float a = _flatLeafCoefCache[0]; |
|
807 |
float b = _flatLeafCoefCache[1]; |
|
808 |
float c = _flatLeafCoefCache[2]; |
|
809 |
float d = t * _flatLeafCoefCache[3]; |
|
810 |
||
811 |
// we use cubicRootsInAB here, because we want only roots in 0, 1, |
|
812 |
// and our quadratic root finder doesn't filter, so it's just a |
|
813 |
// matter of convenience. |
|
49496 | 814 |
final int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0f, 1.0f); |
34417 | 815 |
if (n == 1 && !Float.isNaN(nextRoots[0])) { |
816 |
t = nextRoots[0]; |
|
817 |
} |
|
818 |
} |
|
819 |
// t is relative to the current leaf, so we must make it a valid parameter |
|
820 |
// of the original curve. |
|
821 |
t = t * (nextT - lastT) + lastT; |
|
47126 | 822 |
if (t >= 1.0f) { |
823 |
t = 1.0f; |
|
34417 | 824 |
done = true; |
825 |
} |
|
826 |
// even if done = true, if we're here, that means targetLength |
|
827 |
// is equal to, or very, very close to the total length of the |
|
828 |
// curve, so lastSegLen won't be too high. In cases where len |
|
829 |
// overshoots the curve, this method will exit in the while |
|
830 |
// loop, and lastSegLen will still be set to the right value. |
|
831 |
lastSegLen = len; |
|
832 |
return t; |
|
833 |
} |
|
834 |
||
49496 | 835 |
float totalLength() { |
836 |
while (!done) { |
|
837 |
goToNextLeaf(); |
|
838 |
} |
|
839 |
// reset LengthIterator: |
|
840 |
reset(); |
|
841 |
||
842 |
return lenAtNextT; |
|
843 |
} |
|
844 |
||
34417 | 845 |
float lastSegLen() { |
846 |
return lastSegLen; |
|
847 |
} |
|
848 |
||
849 |
// go to the next leaf (in an inorder traversal) in the recursion tree |
|
850 |
// preconditions: must be on a leaf, and that leaf must not be the root. |
|
851 |
private void goToNextLeaf() { |
|
852 |
// We must go to the first ancestor node that has an unvisited |
|
853 |
// right child. |
|
49496 | 854 |
final boolean[] _sides = sidesRight; |
34417 | 855 |
int _recLevel = recLevel; |
49496 | 856 |
_recLevel--; |
34417 | 857 |
|
49496 | 858 |
while(_sides[_recLevel]) { |
34417 | 859 |
if (_recLevel == 0) { |
860 |
recLevel = 0; |
|
861 |
done = true; |
|
862 |
return; |
|
863 |
} |
|
864 |
_recLevel--; |
|
865 |
} |
|
866 |
||
49496 | 867 |
_sides[_recLevel] = true; |
34417 | 868 |
// optimize arraycopy (8 values faster than 6 = type): |
49496 | 869 |
System.arraycopy(recCurveStack[_recLevel++], 0, |
870 |
recCurveStack[_recLevel], 0, 8); |
|
34417 | 871 |
recLevel = _recLevel; |
872 |
goLeft(); |
|
873 |
} |
|
874 |
||
875 |
// go to the leftmost node from the current node. Return its length. |
|
876 |
private void goLeft() { |
|
49496 | 877 |
final float len = onLeaf(); |
47126 | 878 |
if (len >= 0.0f) { |
34417 | 879 |
lastT = nextT; |
880 |
lenAtLastT = lenAtNextT; |
|
39519
21bfc4452441
8159093: Fix coding conventions in Marlin renderer
lbourges
parents:
34417
diff
changeset
|
881 |
nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC; |
34417 | 882 |
lenAtNextT += len; |
883 |
// invalidate caches |
|
47126 | 884 |
flatLeafCoefCache[2] = -1.0f; |
34417 | 885 |
cachedHaveLowAcceleration = -1; |
886 |
} else { |
|
49496 | 887 |
Helpers.subdivide(recCurveStack[recLevel], |
888 |
recCurveStack[recLevel + 1], |
|
889 |
recCurveStack[recLevel], curveType); |
|
890 |
||
891 |
sidesRight[recLevel] = false; |
|
34417 | 892 |
recLevel++; |
893 |
goLeft(); |
|
894 |
} |
|
895 |
} |
|
896 |
||
897 |
// this is a bit of a hack. It returns -1 if we're not on a leaf, and |
|
898 |
// the length of the leaf if we are on a leaf. |
|
899 |
private float onLeaf() { |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
900 |
final float[] curve = recCurveStack[recLevel]; |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
901 |
final int _curveType = curveType; |
47126 | 902 |
float polyLen = 0.0f; |
34417 | 903 |
|
904 |
float x0 = curve[0], y0 = curve[1]; |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
905 |
for (int i = 2; i < _curveType; i += 2) { |
49496 | 906 |
final float x1 = curve[i], y1 = curve[i + 1]; |
34417 | 907 |
final float len = Helpers.linelen(x0, y0, x1, y1); |
908 |
polyLen += len; |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
909 |
curLeafCtrlPolyLengths[(i >> 1) - 1] = len; |
34417 | 910 |
x0 = x1; |
911 |
y0 = y1; |
|
912 |
} |
|
913 |
||
49496 | 914 |
final float lineLen = Helpers.linelen(curve[0], curve[1], x0, y0); |
915 |
||
916 |
if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) { |
|
47126 | 917 |
return (polyLen + lineLen) / 2.0f; |
34417 | 918 |
} |
47126 | 919 |
return -1.0f; |
34417 | 920 |
} |
921 |
} |
|
922 |
||
923 |
@Override |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
924 |
public void curveTo(final float x1, final float y1, |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
925 |
final float x2, final float y2, |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
926 |
final float x3, final float y3) |
34417 | 927 |
{ |
49496 | 928 |
final int outcode0 = this.cOutCode; |
929 |
||
930 |
if (clipRect != null) { |
|
931 |
final int outcode1 = Helpers.outcode(x1, y1, clipRect); |
|
932 |
final int outcode2 = Helpers.outcode(x2, y2, clipRect); |
|
933 |
final int outcode3 = Helpers.outcode(x3, y3, clipRect); |
|
934 |
||
935 |
// Should clip |
|
936 |
final int orCode = (outcode0 | outcode1 | outcode2 | outcode3); |
|
937 |
if (orCode != 0) { |
|
938 |
final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; |
|
939 |
||
940 |
// basic rejection criteria: |
|
941 |
if (sideCode == 0) { |
|
942 |
// ovelap clip: |
|
943 |
if (subdivide) { |
|
944 |
// avoid reentrance |
|
945 |
subdivide = false; |
|
946 |
// subdivide curve => callback with subdivided parts: |
|
947 |
boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3, |
|
948 |
orCode, this); |
|
949 |
// reentrance is done: |
|
950 |
subdivide = true; |
|
951 |
if (ret) { |
|
952 |
return; |
|
953 |
} |
|
954 |
} |
|
955 |
// already subdivided so render it |
|
956 |
} else { |
|
957 |
this.cOutCode = outcode3; |
|
958 |
skipCurveTo(x1, y1, x2, y2, x3, y3); |
|
959 |
return; |
|
960 |
} |
|
961 |
} |
|
962 |
||
963 |
this.cOutCode = outcode3; |
|
964 |
||
965 |
if (this.outside) { |
|
966 |
this.outside = false; |
|
967 |
// Adjust current index, phase & dash: |
|
968 |
skipLen(); |
|
969 |
} |
|
970 |
} |
|
971 |
_curveTo(x1, y1, x2, y2, x3, y3); |
|
972 |
} |
|
973 |
||
974 |
private void _curveTo(final float x1, final float y1, |
|
975 |
final float x2, final float y2, |
|
976 |
final float x3, final float y3) |
|
977 |
{ |
|
34417 | 978 |
final float[] _curCurvepts = curCurvepts; |
49496 | 979 |
|
980 |
// monotonize curve: |
|
981 |
final CurveBasicMonotonizer monotonizer |
|
982 |
= rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3); |
|
983 |
||
984 |
final int nSplits = monotonizer.nbSplits; |
|
985 |
final float[] mid = monotonizer.middle; |
|
986 |
||
987 |
for (int i = 0, off = 0; i <= nSplits; i++, off += 6) { |
|
988 |
// optimize arraycopy (8 values faster than 6 = type): |
|
989 |
System.arraycopy(mid, off, _curCurvepts, 0, 8); |
|
990 |
||
991 |
somethingTo(8); |
|
992 |
} |
|
993 |
} |
|
994 |
||
995 |
private void skipCurveTo(final float x1, final float y1, |
|
996 |
final float x2, final float y2, |
|
997 |
final float x3, final float y3) |
|
998 |
{ |
|
999 |
final float[] _curCurvepts = curCurvepts; |
|
1000 |
_curCurvepts[0] = cx0; _curCurvepts[1] = cy0; |
|
1001 |
_curCurvepts[2] = x1; _curCurvepts[3] = y1; |
|
1002 |
_curCurvepts[4] = x2; _curCurvepts[5] = y2; |
|
1003 |
_curCurvepts[6] = x3; _curCurvepts[7] = y3; |
|
1004 |
||
1005 |
skipSomethingTo(8); |
|
1006 |
||
1007 |
this.cx0 = x3; |
|
1008 |
this.cy0 = y3; |
|
34417 | 1009 |
} |
1010 |
||
1011 |
@Override |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
1012 |
public void quadTo(final float x1, final float y1, |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
1013 |
final float x2, final float y2) |
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
1014 |
{ |
49496 | 1015 |
final int outcode0 = this.cOutCode; |
1016 |
||
1017 |
if (clipRect != null) { |
|
1018 |
final int outcode1 = Helpers.outcode(x1, y1, clipRect); |
|
1019 |
final int outcode2 = Helpers.outcode(x2, y2, clipRect); |
|
1020 |
||
1021 |
// Should clip |
|
1022 |
final int orCode = (outcode0 | outcode1 | outcode2); |
|
1023 |
if (orCode != 0) { |
|
1024 |
final int sideCode = outcode0 & outcode1 & outcode2; |
|
1025 |
||
1026 |
// basic rejection criteria: |
|
1027 |
if (sideCode == 0) { |
|
1028 |
// ovelap clip: |
|
1029 |
if (subdivide) { |
|
1030 |
// avoid reentrance |
|
1031 |
subdivide = false; |
|
1032 |
// subdivide curve => call lineTo() with subdivided curves: |
|
1033 |
boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, |
|
1034 |
x2, y2, orCode, this); |
|
1035 |
// reentrance is done: |
|
1036 |
subdivide = true; |
|
1037 |
if (ret) { |
|
1038 |
return; |
|
1039 |
} |
|
1040 |
} |
|
1041 |
// already subdivided so render it |
|
1042 |
} else { |
|
1043 |
this.cOutCode = outcode2; |
|
1044 |
skipQuadTo(x1, y1, x2, y2); |
|
1045 |
return; |
|
1046 |
} |
|
1047 |
} |
|
1048 |
||
1049 |
this.cOutCode = outcode2; |
|
1050 |
||
1051 |
if (this.outside) { |
|
1052 |
this.outside = false; |
|
1053 |
// Adjust current index, phase & dash: |
|
1054 |
skipLen(); |
|
1055 |
} |
|
1056 |
} |
|
1057 |
_quadTo(x1, y1, x2, y2); |
|
1058 |
} |
|
1059 |
||
1060 |
private void _quadTo(final float x1, final float y1, |
|
1061 |
final float x2, final float y2) |
|
1062 |
{ |
|
34417 | 1063 |
final float[] _curCurvepts = curCurvepts; |
49496 | 1064 |
|
1065 |
// monotonize quad: |
|
1066 |
final CurveBasicMonotonizer monotonizer |
|
1067 |
= rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2); |
|
1068 |
||
1069 |
final int nSplits = monotonizer.nbSplits; |
|
1070 |
final float[] mid = monotonizer.middle; |
|
1071 |
||
1072 |
for (int i = 0, off = 0; i <= nSplits; i++, off += 4) { |
|
1073 |
// optimize arraycopy (8 values faster than 6 = type): |
|
1074 |
System.arraycopy(mid, off, _curCurvepts, 0, 8); |
|
1075 |
||
1076 |
somethingTo(6); |
|
1077 |
} |
|
1078 |
} |
|
1079 |
||
1080 |
private void skipQuadTo(final float x1, final float y1, |
|
1081 |
final float x2, final float y2) |
|
1082 |
{ |
|
1083 |
final float[] _curCurvepts = curCurvepts; |
|
1084 |
_curCurvepts[0] = cx0; _curCurvepts[1] = cy0; |
|
1085 |
_curCurvepts[2] = x1; _curCurvepts[3] = y1; |
|
1086 |
_curCurvepts[4] = x2; _curCurvepts[5] = y2; |
|
1087 |
||
1088 |
skipSomethingTo(6); |
|
1089 |
||
1090 |
this.cx0 = x2; |
|
1091 |
this.cy0 = y2; |
|
34417 | 1092 |
} |
1093 |
||
1094 |
@Override |
|
1095 |
public void closePath() { |
|
49496 | 1096 |
if (cx0 != sx0 || cy0 != sy0) { |
1097 |
lineTo(sx0, sy0); |
|
1098 |
} |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
1099 |
if (firstSegidx != 0) { |
34417 | 1100 |
if (!dashOn || needsMoveTo) { |
49496 | 1101 |
out.moveTo(sx0, sy0); |
34417 | 1102 |
} |
1103 |
emitFirstSegments(); |
|
1104 |
} |
|
49496 | 1105 |
moveTo(sx0, sy0); |
34417 | 1106 |
} |
1107 |
||
1108 |
@Override |
|
1109 |
public void pathDone() { |
|
48284
fd7fbc929001
8191814: Marlin rasterizer spends time computing geometry for stroked segments that do not intersect the clip
lbourges
parents:
47216
diff
changeset
|
1110 |
if (firstSegidx != 0) { |
49496 | 1111 |
out.moveTo(sx0, sy0); |
34417 | 1112 |
emitFirstSegments(); |
1113 |
} |
|
1114 |
out.pathDone(); |
|
1115 |
||
1116 |
// Dispose this instance: |
|
1117 |
dispose(); |
|
1118 |
} |
|
1119 |
||
1120 |
@Override |
|
1121 |
public long getNativeConsumer() { |
|
1122 |
throw new InternalError("Dasher does not use a native consumer"); |
|
1123 |
} |
|
1124 |
} |
|
1125 |