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;
30
31 /**
32 * The <code>Dasher</code> class takes a series of linear commands
33 * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
34 * <code>end</code>) and breaks them into smaller segments according to a
35 * dash pattern array and a starting dash phase.
36 *
37 * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
38 * short dash, whereas Pisces does not draw anything. The PostScript
39 * semantics are unclear.
40 *
41 */
42 final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
43
44 static final int REC_LIMIT = 4;
45 static final float ERR = 0.01f;
46 static final float MIN_T_INC = 1f / (1 << REC_LIMIT);
47
48 private PathConsumer2D out;
49 private float[] dash;
50 private int dashLen;
51 private float startPhase;
52 private boolean startDashOn;
53 private int startIdx;
54
55 private boolean starting;
56 private boolean needsMoveTo;
57
58 private int idx;
59 private boolean dashOn;
60 private float phase;
61
62 private float sx, sy;
63 private float x0, y0;
64
65 // temporary storage for the current curve
66 private final float[] curCurvepts;
67
86 dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
87
88 firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
89 firstSegmentsBuffer = firstSegmentsBuffer_ref.initial;
90
91 // we need curCurvepts to be able to contain 2 curves because when
92 // dashing curves, we need to subdivide it
93 curCurvepts = new float[8 * 2];
94 }
95
96 /**
97 * Initialize the <code>Dasher</code>.
98 *
99 * @param out an output <code>PathConsumer2D</code>.
100 * @param dash an array of <code>float</code>s containing the dash pattern
101 * @param dashLen length of the given dash array
102 * @param phase a <code>float</code> containing the dash phase
103 * @param recycleDashes true to indicate to recycle the given dash array
104 * @return this instance
105 */
106 Dasher init(final PathConsumer2D out, float[] dash, int dashLen,
107 float phase, boolean recycleDashes)
108 {
109 if (phase < 0f) {
110 throw new IllegalArgumentException("phase < 0 !");
111 }
112 this.out = out;
113
114 // Normalize so 0 <= phase < dash[0]
115 int idx = 0;
116 dashOn = true;
117 float d;
118 while (phase >= (d = dash[idx])) {
119 phase -= d;
120 idx = (idx + 1) % dashLen;
121 dashOn = !dashOn;
122 }
123
124 this.dash = dash;
125 this.dashLen = dashLen;
126 this.startPhase = this.phase = phase;
127 this.startDashOn = dashOn;
128 this.startIdx = idx;
129 this.starting = true;
130 needsMoveTo = false;
131 firstSegidx = 0;
132
133 this.recycleDashes = recycleDashes;
134
135 return this; // fluent API
136 }
137
138 /**
139 * Disposes this dasher:
140 * clean up before reusing this instance
141 */
142 void dispose() {
143 if (DO_CLEAN_DIRTY) {
144 // Force zero-fill dirty arrays:
145 Arrays.fill(curCurvepts, 0f);
146 }
147 // Return arrays:
148 if (recycleDashes) {
149 dash = dashes_ref.putArray(dash);
150 }
151 firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
152 }
153
154 @Override
155 public void moveTo(float x0, float y0) {
156 if (firstSegidx > 0) {
157 out.moveTo(sx, sy);
158 emitFirstSegments();
159 }
160 needsMoveTo = true;
161 this.idx = startIdx;
162 this.dashOn = this.startDashOn;
163 this.phase = this.startPhase;
164 this.sx = this.x0 = x0;
165 this.sy = this.y0 = y0;
166 this.starting = true;
167 }
168
169 private void emitSeg(float[] buf, int off, int type) {
170 switch (type) {
171 case 8:
172 out.curveTo(buf[off+0], buf[off+1],
173 buf[off+2], buf[off+3],
191 int type = (int)fSegBuf[i];
192 emitSeg(fSegBuf, i + 1, type);
193 i += (type - 1);
194 }
195 firstSegidx = 0;
196 }
197 // We don't emit the first dash right away. If we did, caps would be
198 // drawn on it, but we need joins to be drawn if there's a closePath()
199 // So, we store the path elements that make up the first dash in the
200 // buffer below.
201 private float[] firstSegmentsBuffer; // dynamic array
202 private int firstSegidx;
203
204 // precondition: pts must be in relative coordinates (relative to x0,y0)
205 // fullCurve is true iff the curve in pts has not been split.
206 private void goTo(float[] pts, int off, final int type) {
207 float x = pts[off + type - 4];
208 float y = pts[off + type - 3];
209 if (dashOn) {
210 if (starting) {
211 int len = type - 2 + 1;
212 int segIdx = firstSegidx;
213 float[] buf = firstSegmentsBuffer;
214 if (segIdx + len > buf.length) {
215 if (DO_STATS) {
216 rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
217 .add(segIdx + len);
218 }
219 firstSegmentsBuffer = buf
220 = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
221 segIdx + len);
222 }
223 buf[segIdx++] = type;
224 len--;
225 // small arraycopy (2, 4 or 6) but with offset:
226 System.arraycopy(pts, off, buf, segIdx, len);
227 segIdx += len;
228 firstSegidx = segIdx;
229 } else {
230 if (needsMoveTo) {
231 out.moveTo(x0, y0);
671 lineTo(sx, sy);
672 if (firstSegidx > 0) {
673 if (!dashOn || needsMoveTo) {
674 out.moveTo(sx, sy);
675 }
676 emitFirstSegments();
677 }
678 moveTo(sx, sy);
679 }
680
681 @Override
682 public void pathDone() {
683 if (firstSegidx > 0) {
684 out.moveTo(sx, sy);
685 emitFirstSegments();
686 }
687 out.pathDone();
688
689 // Dispose this instance:
690 dispose();
691 }
692
693 @Override
694 public long getNativeConsumer() {
695 throw new InternalError("Dasher does not use a native consumer");
696 }
697 }
698
|
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 com.sun.marlin;
27
28 import java.util.Arrays;
29 import com.sun.javafx.geom.PathConsumer2D;
30
31 /**
32 * The <code>Dasher</code> class takes a series of linear commands
33 * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
34 * <code>end</code>) and breaks them into smaller segments according to a
35 * dash pattern array and a starting dash phase.
36 *
37 * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
38 * short dash, whereas Pisces does not draw anything. The PostScript
39 * semantics are unclear.
40 *
41 */
42 public final class Dasher implements PathConsumer2D, MarlinConst {
43
44 static final int REC_LIMIT = 4;
45 static final float ERR = 0.01f;
46 static final float MIN_T_INC = 1f / (1 << REC_LIMIT);
47
48 // More than 24 bits of mantissa means we can no longer accurately
49 // measure the number of times cycled through the dash array so we
50 // punt and override the phase to just be 0 past that point.
51 static final float MAX_CYCLES = 16000000f;
52
53 private PathConsumer2D out;
54 private float[] dash;
55 private int dashLen;
56 private float startPhase;
57 private boolean startDashOn;
58 private int startIdx;
59
60 private boolean starting;
61 private boolean needsMoveTo;
62
63 private int idx;
64 private boolean dashOn;
65 private float phase;
66
67 private float sx, sy;
68 private float x0, y0;
69
70 // temporary storage for the current curve
71 private final float[] curCurvepts;
72
91 dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
92
93 firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
94 firstSegmentsBuffer = firstSegmentsBuffer_ref.initial;
95
96 // we need curCurvepts to be able to contain 2 curves because when
97 // dashing curves, we need to subdivide it
98 curCurvepts = new float[8 * 2];
99 }
100
101 /**
102 * Initialize the <code>Dasher</code>.
103 *
104 * @param out an output <code>PathConsumer2D</code>.
105 * @param dash an array of <code>float</code>s containing the dash pattern
106 * @param dashLen length of the given dash array
107 * @param phase a <code>float</code> containing the dash phase
108 * @param recycleDashes true to indicate to recycle the given dash array
109 * @return this instance
110 */
111 public Dasher init(final PathConsumer2D out, float[] dash, int dashLen,
112 float phase, boolean recycleDashes)
113 {
114 this.out = out;
115
116 // Normalize so 0 <= phase < dash[0]
117 int sidx = 0;
118 dashOn = true;
119 float sum = 0f;
120 for (float d : dash) {
121 sum += d;
122 }
123 float cycles = phase / sum;
124 if (phase < 0f) {
125 if (-cycles >= MAX_CYCLES) {
126 phase = 0f;
127 } else {
128 int fullcycles = FloatMath.floor_int(-cycles);
129 if ((fullcycles & dash.length & 1) != 0) {
130 dashOn = !dashOn;
131 }
132 phase += fullcycles * sum;
133 while (phase < 0f) {
134 if (--sidx < 0) {
135 sidx = dash.length - 1;
136 }
137 phase += dash[sidx];
138 dashOn = !dashOn;
139 }
140 }
141 } else if (phase > 0) {
142 if (cycles >= MAX_CYCLES) {
143 phase = 0f;
144 } else {
145 int fullcycles = FloatMath.floor_int(cycles);
146 if ((fullcycles & dash.length & 1) != 0) {
147 dashOn = !dashOn;
148 }
149 phase -= fullcycles * sum;
150 float d;
151 while (phase >= (d = dash[sidx])) {
152 phase -= d;
153 sidx = (sidx + 1) % dash.length;
154 dashOn = !dashOn;
155 }
156 }
157 }
158
159 this.dash = dash;
160 this.dashLen = dashLen;
161 this.startPhase = this.phase = phase;
162 this.startDashOn = dashOn;
163 this.startIdx = sidx;
164 this.starting = true;
165 needsMoveTo = false;
166 firstSegidx = 0;
167
168 this.recycleDashes = recycleDashes;
169
170 return this; // fluent API
171 }
172
173 /**
174 * Disposes this dasher:
175 * clean up before reusing this instance
176 */
177 void dispose() {
178 if (DO_CLEAN_DIRTY) {
179 // Force zero-fill dirty arrays:
180 Arrays.fill(curCurvepts, 0f);
181 }
182 // Return arrays:
183 if (recycleDashes) {
184 dash = dashes_ref.putArray(dash);
185 }
186 firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
187 }
188
189 public float[] copyDashArray(final float[] dashes) {
190 final int len = dashes.length;
191 final float[] newDashes;
192 if (len <= MarlinConst.INITIAL_ARRAY) {
193 newDashes = rdrCtx.dasher.dashes_ref.initial;
194 } else {
195 if (DO_STATS) {
196 rdrCtx.stats.stat_array_dasher_dasher.add(len);
197 }
198 newDashes = rdrCtx.dasher.dashes_ref.getArray(len);
199 }
200 System.arraycopy(dashes, 0, newDashes, 0, len);
201 return newDashes;
202 }
203
204 @Override
205 public void moveTo(float x0, float y0) {
206 if (firstSegidx > 0) {
207 out.moveTo(sx, sy);
208 emitFirstSegments();
209 }
210 needsMoveTo = true;
211 this.idx = startIdx;
212 this.dashOn = this.startDashOn;
213 this.phase = this.startPhase;
214 this.sx = this.x0 = x0;
215 this.sy = this.y0 = y0;
216 this.starting = true;
217 }
218
219 private void emitSeg(float[] buf, int off, int type) {
220 switch (type) {
221 case 8:
222 out.curveTo(buf[off+0], buf[off+1],
223 buf[off+2], buf[off+3],
241 int type = (int)fSegBuf[i];
242 emitSeg(fSegBuf, i + 1, type);
243 i += (type - 1);
244 }
245 firstSegidx = 0;
246 }
247 // We don't emit the first dash right away. If we did, caps would be
248 // drawn on it, but we need joins to be drawn if there's a closePath()
249 // So, we store the path elements that make up the first dash in the
250 // buffer below.
251 private float[] firstSegmentsBuffer; // dynamic array
252 private int firstSegidx;
253
254 // precondition: pts must be in relative coordinates (relative to x0,y0)
255 // fullCurve is true iff the curve in pts has not been split.
256 private void goTo(float[] pts, int off, final int type) {
257 float x = pts[off + type - 4];
258 float y = pts[off + type - 3];
259 if (dashOn) {
260 if (starting) {
261 int len = type - 1; // - 2 + 1
262 int segIdx = firstSegidx;
263 float[] buf = firstSegmentsBuffer;
264 if (segIdx + len > buf.length) {
265 if (DO_STATS) {
266 rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
267 .add(segIdx + len);
268 }
269 firstSegmentsBuffer = buf
270 = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
271 segIdx + len);
272 }
273 buf[segIdx++] = type;
274 len--;
275 // small arraycopy (2, 4 or 6) but with offset:
276 System.arraycopy(pts, off, buf, segIdx, len);
277 segIdx += len;
278 firstSegidx = segIdx;
279 } else {
280 if (needsMoveTo) {
281 out.moveTo(x0, y0);
721 lineTo(sx, sy);
722 if (firstSegidx > 0) {
723 if (!dashOn || needsMoveTo) {
724 out.moveTo(sx, sy);
725 }
726 emitFirstSegments();
727 }
728 moveTo(sx, sy);
729 }
730
731 @Override
732 public void pathDone() {
733 if (firstSegidx > 0) {
734 out.moveTo(sx, sy);
735 emitFirstSegments();
736 }
737 out.pathDone();
738
739 // Dispose this instance:
740 dispose();
741 }
742 }
743
|