History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/java2d/pisces (13 of 18352 files). Show all

Unified · Split

13 files
  1. java/pkg/sun/java2d/pisces/Curve.java +290 −0
  2. java/pkg/sun/java2d/pisces/Dasher.java +575 −0
  3. java/pkg/sun/java2d/pisces/Helpers.java +458 −0
  4. java/pkg/sun/java2d/pisces/META-INF/mkfile +6 −0
  5. java/pkg/sun/java2d/pisces/META-INF/services/mkfile +2 −0
  6. java/pkg/sun/java2d/pisces/META-INF/services/sun.java2d.pipe.RenderingEngine +2 −0
  7. java/pkg/sun/java2d/pisces/PiscesCache.java +186 −0
  8. java/pkg/sun/java2d/pisces/PiscesRenderingEngine.java +656 −0
  9. java/pkg/sun/java2d/pisces/PiscesTileGenerator.java +252 −0
  10. java/pkg/sun/java2d/pisces/Renderer.java +571 −0
  11. java/pkg/sun/java2d/pisces/Stroker.java +1223 −0
  12. java/pkg/sun/java2d/pisces/TransformingPathConsumer2D.java +393 −0
  13. java/pkg/sun/java2d/pisces/mkfile +20 −0
java/pkg/sun/java2d/pisces/Curve.java+290−0
@@ -0,0 +1,290 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import java.util.Iterator;
29
30final class Curve {
31
32 float ax, ay, bx, by, cx, cy, dx, dy;
33 float dax, day, dbx, dby;
34
35 Curve() {
36 }
37
38 void set(float[] points, int type) {
39 switch(type) {
40 case 8:
41 set(points[0], points[1],
42 points[2], points[3],
43 points[4], points[5],
44 points[6], points[7]);
45 break;
46 case 6:
47 set(points[0], points[1],
48 points[2], points[3],
49 points[4], points[5]);
50 break;
51 default:
52 throw new InternalError("Curves can only be cubic or quadratic");
53 }
54 }
55
56 void set(float x1, float y1,
57 float x2, float y2,
58 float x3, float y3,
59 float x4, float y4)
60 {
61 ax = 3 * (x2 - x3) + x4 - x1;
62 ay = 3 * (y2 - y3) + y4 - y1;
63 bx = 3 * (x1 - 2 * x2 + x3);
64 by = 3 * (y1 - 2 * y2 + y3);
65 cx = 3 * (x2 - x1);
66 cy = 3 * (y2 - y1);
67 dx = x1;
68 dy = y1;
69 dax = 3 * ax; day = 3 * ay;
70 dbx = 2 * bx; dby = 2 * by;
71 }
72
73 void set(float x1, float y1,
74 float x2, float y2,
75 float x3, float y3)
76 {
77 ax = ay = 0f;
78
79 bx = x1 - 2 * x2 + x3;
80 by = y1 - 2 * y2 + y3;
81 cx = 2 * (x2 - x1);
82 cy = 2 * (y2 - y1);
83 dx = x1;
84 dy = y1;
85 dax = 0; day = 0;
86 dbx = 2 * bx; dby = 2 * by;
87 }
88
89 float xat(float t) {
90 return t * (t * (t * ax + bx) + cx) + dx;
91 }
92 float yat(float t) {
93 return t * (t * (t * ay + by) + cy) + dy;
94 }
95
96 float dxat(float t) {
97 return t * (t * dax + dbx) + cx;
98 }
99
100 float dyat(float t) {
101 return t * (t * day + dby) + cy;
102 }
103
104 int dxRoots(float[] roots, int off) {
105 return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
106 }
107
108 int dyRoots(float[] roots, int off) {
109 return Helpers.quadraticRoots(day, dby, cy, roots, off);
110 }
111
112 int infPoints(float[] pts, int off) {
113 // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
114 // Fortunately, this turns out to be quadratic, so there are at
115 // most 2 inflection points.
116 final float a = dax * dby - dbx * day;
117 final float b = 2 * (cy * dax - day * cx);
118 final float c = cy * dbx - cx * dby;
119
120 return Helpers.quadraticRoots(a, b, c, pts, off);
121 }
122
123 // finds points where the first and second derivative are
124 // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
125 // * is a dot product). Unfortunately, we have to solve a cubic.
126 private int perpendiculardfddf(float[] pts, int off) {
127 assert pts.length >= off + 4;
128
129 // these are the coefficients of some multiple of g(t) (not g(t),
130 // because the roots of a polynomial are not changed after multiplication
131 // by a constant, and this way we save a few multiplications).
132 final float a = 2*(dax*dax + day*day);
133 final float b = 3*(dax*dbx + day*dby);
134 final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby;
135 final float d = dbx*cx + dby*cy;
136 return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
137 }
138
139 // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
140 // a variant of the false position algorithm to find the roots. False
141 // position requires that 2 initial values x0,x1 be given, and that the
142 // function must have opposite signs at those values. To find such
143 // values, we need the local extrema of the ROC function, for which we
144 // need the roots of its derivative; however, it's harder to find the
145 // roots of the derivative in this case than it is to find the roots
146 // of the original function. So, we find all points where this curve's
147 // first and second derivative are perpendicular, and we pretend these
148 // are our local extrema. There are at most 3 of these, so we will check
149 // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
150 // points, so roc-w can have at least 6 roots. This shouldn't be a
151 // problem for what we're trying to do (draw a nice looking curve).
152 int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
153 // no OOB exception, because by now off<=6, and roots.length >= 10
154 assert off <= 6 && roots.length >= 10;
155 int ret = off;
156 int numPerpdfddf = perpendiculardfddf(roots, off);
157 float t0 = 0, ft0 = ROCsq(t0) - w*w;
158 roots[off + numPerpdfddf] = 1f; // always check interval end points
159 numPerpdfddf++;
160 for (int i = off; i < off + numPerpdfddf; i++) {
161 float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
162 if (ft0 == 0f) {
163 roots[ret++] = t0;
164 } else if (ft1 * ft0 < 0f) { // have opposite signs
165 // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
166 // ROC(t) >= 0 for all t.
167 roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
168 }
169 t0 = t1;
170 ft0 = ft1;
171 }
172
173 return ret - off;
174 }
175
176 private static float eliminateInf(float x) {
177 return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
178 (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
179 }
180
181 // A slight modification of the false position algorithm on wikipedia.
182 // This only works for the ROCsq-x functions. It might be nice to have
183 // the function as an argument, but that would be awkward in java6.
184 // TODO: It is something to consider for java8 (or whenever lambda
185 // expressions make it into the language), depending on how closures
186 // and turn out. Same goes for the newton's method
187 // algorithm in Helpers.java
188 private float falsePositionROCsqMinusX(float x0, float x1,
189 final float x, final float err)
190 {
191 final int iterLimit = 100;
192 int side = 0;
193 float t = x1, ft = eliminateInf(ROCsq(t) - x);
194 float s = x0, fs = eliminateInf(ROCsq(s) - x);
195 float r = s, fr;
196 for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
197 r = (fs * t - ft * s) / (fs - ft);
198 fr = ROCsq(r) - x;
199 if (sameSign(fr, ft)) {
200 ft = fr; t = r;
201 if (side < 0) {
202 fs /= (1 << (-side));
203 side--;
204 } else {
205 side = -1;
206 }
207 } else if (fr * fs > 0) {
208 fs = fr; s = r;
209 if (side > 0) {
210 ft /= (1 << side);
211 side++;
212 } else {
213 side = 1;
214 }
215 } else {
216 break;
217 }
218 }
219 return r;
220 }
221
222 private static boolean sameSign(double x, double y) {
223 // another way is to test if x*y > 0. This is bad for small x, y.
224 return (x < 0 && y < 0) || (x > 0 && y > 0);
225 }
226
227 // returns the radius of curvature squared at t of this curve
228 // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
229 private float ROCsq(final float t) {
230 // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
231 final float dx = t * (t * dax + dbx) + cx;
232 final float dy = t * (t * day + dby) + cy;
233 final float ddx = 2 * dax * t + dbx;
234 final float ddy = 2 * day * t + dby;
235 final float dx2dy2 = dx*dx + dy*dy;
236 final float ddx2ddy2 = ddx*ddx + ddy*ddy;
237 final float ddxdxddydy = ddx*dx + ddy*dy;
238 return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
239 }
240
241 // curve to be broken should be in pts
242 // this will change the contents of pts but not Ts
243 // TODO: There's no reason for Ts to be an array. All we need is a sequence
244 // of t values at which to subdivide. An array statisfies this condition,
245 // but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.
246 // Doing this will also make dashing easier, since we could easily make
247 // LengthIterator an Iterator<Float> and feed it to this function to simplify
248 // the loop in Dasher.somethingTo.
249 static Iterator<Integer> breakPtsAtTs(final float[] pts, final int type,
250 final float[] Ts, final int numTs)
251 {
252 assert pts.length >= 2*type && numTs <= Ts.length;
253 return new Iterator<Integer>() {
254 // these prevent object creation and destruction during autoboxing.
255 // Because of this, the compiler should be able to completely
256 // eliminate the boxing costs.
257 final Integer i0 = 0;
258 final Integer itype = type;
259 int nextCurveIdx = 0;
260 Integer curCurveOff = i0;
261 float prevT = 0;
262
263 @Override public boolean hasNext() {
264 return nextCurveIdx < numTs + 1;
265 }
266
267 @Override public Integer next() {
268 Integer ret;
269 if (nextCurveIdx < numTs) {
270 float curT = Ts[nextCurveIdx];
271 float splitT = (curT - prevT) / (1 - prevT);
272 Helpers.subdivideAt(splitT,
273 pts, curCurveOff,
274 pts, 0,
275 pts, type, type);
276 prevT = curT;
277 ret = i0;
278 curCurveOff = itype;
279 } else {
280 ret = curCurveOff;
281 }
282 nextCurveIdx++;
283 return ret;
284 }
285
286 @Override public void remove() {}
287 };
288 }
289}
290
java/pkg/sun/java2d/pisces/Dasher.java+575−0
@@ -0,0 +1,575 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import sun.awt.geom.PathConsumer2D;
29
30/**
31 * The <code>Dasher</code> class takes a series of linear commands
32 * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
33 * <code>end</code>) and breaks them into smaller segments according to a
34 * dash pattern array and a starting dash phase.
35 *
36 * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
37 * short dash, whereas Pisces does not draw anything. The PostScript
38 * semantics are unclear.
39 *
40 */
41final class Dasher implements sun.awt.geom.PathConsumer2D {
42
43 private final PathConsumer2D out;
44 private final float[] dash;
45 private final float startPhase;
46 private final boolean startDashOn;
47 private final int startIdx;
48
49 private boolean starting;
50 private boolean needsMoveTo;
51
52 private int idx;
53 private boolean dashOn;
54 private float phase;
55
56 private float sx, sy;
57 private float x0, y0;
58
59 // temporary storage for the current curve
60 private float[] curCurvepts;
61
62 /**
63 * Constructs a <code>Dasher</code>.
64 *
65 * @param out an output <code>PathConsumer2D</code>.
66 * @param dash an array of <code>float</code>s containing the dash pattern
67 * @param phase a <code>float</code> containing the dash phase
68 */
69 public Dasher(PathConsumer2D out, float[] dash, float phase) {
70 if (phase < 0) {
71 throw new IllegalArgumentException("phase < 0 !");
72 }
73
74 this.out = out;
75
76 // Normalize so 0 <= phase < dash[0]
77 int idx = 0;
78 dashOn = true;
79 float d;
80 while (phase >= (d = dash[idx])) {
81 phase -= d;
82 idx = (idx + 1) % dash.length;
83 dashOn = !dashOn;
84 }
85
86 this.dash = dash;
87 this.startPhase = this.phase = phase;
88 this.startDashOn = dashOn;
89 this.startIdx = idx;
90 this.starting = true;
91
92 // we need curCurvepts to be able to contain 2 curves because when
93 // dashing curves, we need to subdivide it
94 curCurvepts = new float[8 * 2];
95 }
96
97 public void moveTo(float x0, float y0) {
98 if (firstSegidx > 0) {
99 out.moveTo(sx, sy);
100 emitFirstSegments();
101 }
102 needsMoveTo = true;
103 this.idx = startIdx;
104 this.dashOn = this.startDashOn;
105 this.phase = this.startPhase;
106 this.sx = this.x0 = x0;
107 this.sy = this.y0 = y0;
108 this.starting = true;
109 }
110
111 private void emitSeg(float[] buf, int off, int type) {
112 switch (type) {
113 case 8:
114 out.curveTo(buf[off+0], buf[off+1],
115 buf[off+2], buf[off+3],
116 buf[off+4], buf[off+5]);
117 break;
118 case 6:
119 out.quadTo(buf[off+0], buf[off+1],
120 buf[off+2], buf[off+3]);
121 break;
122 case 4:
123 out.lineTo(buf[off], buf[off+1]);
124 }
125 }
126
127 private void emitFirstSegments() {
128 for (int i = 0; i < firstSegidx; ) {
129 emitSeg(firstSegmentsBuffer, i+1, (int)firstSegmentsBuffer[i]);
130 i += (((int)firstSegmentsBuffer[i]) - 1);
131 }
132 firstSegidx = 0;
133 }
134
135 // We don't emit the first dash right away. If we did, caps would be
136 // drawn on it, but we need joins to be drawn if there's a closePath()
137 // So, we store the path elements that make up the first dash in the
138 // buffer below.
139 private float[] firstSegmentsBuffer = new float[7];
140 private int firstSegidx = 0;
141 // precondition: pts must be in relative coordinates (relative to x0,y0)
142 // fullCurve is true iff the curve in pts has not been split.
143 private void goTo(float[] pts, int off, final int type) {
144 float x = pts[off + type - 4];
145 float y = pts[off + type - 3];
146 if (dashOn) {
147 if (starting) {
148 firstSegmentsBuffer = Helpers.widenArray(firstSegmentsBuffer,
149 firstSegidx, type - 2);
150 firstSegmentsBuffer[firstSegidx++] = type;
151 System.arraycopy(pts, off, firstSegmentsBuffer, firstSegidx, type - 2);
152 firstSegidx += type - 2;
153 } else {
154 if (needsMoveTo) {
155 out.moveTo(x0, y0);
156 needsMoveTo = false;
157 }
158 emitSeg(pts, off, type);
159 }
160 } else {
161 starting = false;
162 needsMoveTo = true;
163 }
164 this.x0 = x;
165 this.y0 = y;
166 }
167
168 public void lineTo(float x1, float y1) {
169 float dx = x1 - x0;
170 float dy = y1 - y0;
171
172 float len = (float) Math.sqrt(dx*dx + dy*dy);
173
174 if (len == 0) {
175 return;
176 }
177
178 // The scaling factors needed to get the dx and dy of the
179 // transformed dash segments.
180 float cx = dx / len;
181 float cy = dy / len;
182
183 while (true) {
184 float leftInThisDashSegment = dash[idx] - phase;
185 if (len <= leftInThisDashSegment) {
186 curCurvepts[0] = x1;
187 curCurvepts[1] = y1;
188 goTo(curCurvepts, 0, 4);
189 // Advance phase within current dash segment
190 phase += len;
191 if (len == leftInThisDashSegment) {
192 phase = 0f;
193 idx = (idx + 1) % dash.length;
194 dashOn = !dashOn;
195 }
196 return;
197 }
198
199 float dashdx = dash[idx] * cx;
200 float dashdy = dash[idx] * cy;
201 if (phase == 0) {
202 curCurvepts[0] = x0 + dashdx;
203 curCurvepts[1] = y0 + dashdy;
204 } else {
205 float p = leftInThisDashSegment / dash[idx];
206 curCurvepts[0] = x0 + p * dashdx;
207 curCurvepts[1] = y0 + p * dashdy;
208 }
209
210 goTo(curCurvepts, 0, 4);
211
212 len -= leftInThisDashSegment;
213 // Advance to next dash segment
214 idx = (idx + 1) % dash.length;
215 dashOn = !dashOn;
216 phase = 0;
217 }
218 }
219
220 private LengthIterator li = null;
221
222 // preconditions: curCurvepts must be an array of length at least 2 * type,
223 // that contains the curve we want to dash in the first type elements
224 private void somethingTo(int type) {
225 if (pointCurve(curCurvepts, type)) {
226 return;
227 }
228 if (li == null) {
229 li = new LengthIterator(4, 0.01f);
230 }
231 li.initializeIterationOnCurve(curCurvepts, type);
232
233 int curCurveoff = 0; // initially the current curve is at curCurvepts[0...type]
234 float lastSplitT = 0;
235 float t = 0;
236 float leftInThisDashSegment = dash[idx] - phase;
237 while ((t = li.next(leftInThisDashSegment)) < 1) {
238 if (t != 0) {
239 Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT),
240 curCurvepts, curCurveoff,
241 curCurvepts, 0,
242 curCurvepts, type, type);
243 lastSplitT = t;
244 goTo(curCurvepts, 2, type);
245 curCurveoff = type;
246 }
247 // Advance to next dash segment
248 idx = (idx + 1) % dash.length;
249 dashOn = !dashOn;
250 phase = 0;
251 leftInThisDashSegment = dash[idx];
252 }
253 goTo(curCurvepts, curCurveoff+2, type);
254 phase += li.lastSegLen();
255 if (phase >= dash[idx]) {
256 phase = 0f;
257 idx = (idx + 1) % dash.length;
258 dashOn = !dashOn;
259 }
260 }
261
262 private static boolean pointCurve(float[] curve, int type) {
263 for (int i = 2; i < type; i++) {
264 if (curve[i] != curve[i-2]) {
265 return false;
266 }
267 }
268 return true;
269 }
270
271 // Objects of this class are used to iterate through curves. They return
272 // t values where the left side of the curve has a specified length.
273 // It does this by subdividing the input curve until a certain error
274 // condition has been met. A recursive subdivision procedure would
275 // return as many as 1<<limit curves, but this is an iterator and we
276 // don't need all the curves all at once, so what we carry out a
277 // lazy inorder traversal of the recursion tree (meaning we only move
278 // through the tree when we need the next subdivided curve). This saves
279 // us a lot of memory because at any one time we only need to store
280 // limit+1 curves - one for each level of the tree + 1.
281 // NOTE: the way we do things here is not enough to traverse a general
282 // tree; however, the trees we are interested in have the property that
283 // every non leaf node has exactly 2 children
284 private static class LengthIterator {
285 private enum Side {LEFT, RIGHT};
286 // Holds the curves at various levels of the recursion. The root
287 // (i.e. the original curve) is at recCurveStack[0] (but then it
288 // gets subdivided, the left half is put at 1, so most of the time
289 // only the right half of the original curve is at 0)
290 private float[][] recCurveStack;
291 // sides[i] indicates whether the node at level i+1 in the path from
292 // the root to the current leaf is a left or right child of its parent.
293 private Side[] sides;
294 private int curveType;
295 private final int limit;
296 private final float ERR;
297 private final float minTincrement;
298 // lastT and nextT delimit the current leaf.
299 private float nextT;
300 private float lenAtNextT;
301 private float lastT;
302 private float lenAtLastT;
303 private float lenAtLastSplit;
304 private float lastSegLen;
305 // the current level in the recursion tree. 0 is the root. limit
306 // is the deepest possible leaf.
307 private int recLevel;
308 private boolean done;
309
310 // the lengths of the lines of the control polygon. Only its first
311 // curveType/2 - 1 elements are valid. This is an optimization. See
312 // next(float) for more detail.
313 private float[] curLeafCtrlPolyLengths = new float[3];
314
315 public LengthIterator(int reclimit, float err) {
316 this.limit = reclimit;
317 this.minTincrement = 1f / (1 << limit);
318 this.ERR = err;
319 this.recCurveStack = new float[reclimit+1][8];
320 this.sides = new Side[reclimit];
321 // if any methods are called without first initializing this object on
322 // a curve, we want it to fail ASAP.
323 this.nextT = Float.MAX_VALUE;
324 this.lenAtNextT = Float.MAX_VALUE;
325 this.lenAtLastSplit = Float.MIN_VALUE;
326 this.recLevel = Integer.MIN_VALUE;
327 this.lastSegLen = Float.MAX_VALUE;
328 this.done = true;
329 }
330
331 public void initializeIterationOnCurve(float[] pts, int type) {
332 System.arraycopy(pts, 0, recCurveStack[0], 0, type);
333 this.curveType = type;
334 this.recLevel = 0;
335 this.lastT = 0;
336 this.lenAtLastT = 0;
337 this.nextT = 0;
338 this.lenAtNextT = 0;
339 goLeft(); // initializes nextT and lenAtNextT properly
340 this.lenAtLastSplit = 0;
341 if (recLevel > 0) {
342 this.sides[0] = Side.LEFT;
343 this.done = false;
344 } else {
345 // the root of the tree is a leaf so we're done.
346 this.sides[0] = Side.RIGHT;
347 this.done = true;
348 }
349 this.lastSegLen = 0;
350 }
351
352 // 0 == false, 1 == true, -1 == invalid cached value.
353 private int cachedHaveLowAcceleration = -1;
354
355 private boolean haveLowAcceleration(float err) {
356 if (cachedHaveLowAcceleration == -1) {
357 final float len1 = curLeafCtrlPolyLengths[0];
358 final float len2 = curLeafCtrlPolyLengths[1];
359 // the test below is equivalent to !within(len1/len2, 1, err).
360 // It is using a multiplication instead of a division, so it
361 // should be a bit faster.
362 if (!Helpers.within(len1, len2, err*len2)) {
363 cachedHaveLowAcceleration = 0;
364 return false;
365 }
366 if (curveType == 8) {
367 final float len3 = curLeafCtrlPolyLengths[2];
368 // if len1 is close to 2 and 2 is close to 3, that probably
369 // means 1 is close to 3 so the second part of this test might
370 // not be needed, but it doesn't hurt to include it.
371 if (!(Helpers.within(len2, len3, err*len3) &&
372 Helpers.within(len1, len3, err*len3))) {
373 cachedHaveLowAcceleration = 0;
374 return false;
375 }
376 }
377 cachedHaveLowAcceleration = 1;
378 return true;
379 }
380
381 return (cachedHaveLowAcceleration == 1);
382 }
383
384 // we want to avoid allocations/gc so we keep this array so we
385 // can put roots in it,
386 private float[] nextRoots = new float[4];
387
388 // caches the coefficients of the current leaf in its flattened
389 // form (see inside next() for what that means). The cache is
390 // invalid when it's third element is negative, since in any
391 // valid flattened curve, this would be >= 0.
392 private float[] flatLeafCoefCache = new float[] {0, 0, -1, 0};
393 // returns the t value where the remaining curve should be split in
394 // order for the left subdivided curve to have length len. If len
395 // is >= than the length of the uniterated curve, it returns 1.
396 public float next(final float len) {
397 final float targetLength = lenAtLastSplit + len;
398 while(lenAtNextT < targetLength) {
399 if (done) {
400 lastSegLen = lenAtNextT - lenAtLastSplit;
401 return 1;
402 }
403 goToNextLeaf();
404 }
405 lenAtLastSplit = targetLength;
406 final float leaflen = lenAtNextT - lenAtLastT;
407 float t = (targetLength - lenAtLastT) / leaflen;
408
409 // cubicRootsInAB is a fairly expensive call, so we just don't do it
410 // if the acceleration in this section of the curve is small enough.
411 if (!haveLowAcceleration(0.05f)) {
412 // We flatten the current leaf along the x axis, so that we're
413 // left with a, b, c which define a 1D Bezier curve. We then
414 // solve this to get the parameter of the original leaf that
415 // gives us the desired length.
416
417 if (flatLeafCoefCache[2] < 0) {
418 float x = 0+curLeafCtrlPolyLengths[0],
419 y = x+curLeafCtrlPolyLengths[1];
420 if (curveType == 8) {
421 float z = y + curLeafCtrlPolyLengths[2];
422 flatLeafCoefCache[0] = 3*(x - y) + z;
423 flatLeafCoefCache[1] = 3*(y - 2*x);
424 flatLeafCoefCache[2] = 3*x;
425 flatLeafCoefCache[3] = -z;
426 } else if (curveType == 6) {
427 flatLeafCoefCache[0] = 0f;
428 flatLeafCoefCache[1] = y - 2*x;
429 flatLeafCoefCache[2] = 2*x;
430 flatLeafCoefCache[3] = -y;
431 }
432 }
433 float a = flatLeafCoefCache[0];
434 float b = flatLeafCoefCache[1];
435 float c = flatLeafCoefCache[2];
436 float d = t*flatLeafCoefCache[3];
437
438 // we use cubicRootsInAB here, because we want only roots in 0, 1,
439 // and our quadratic root finder doesn't filter, so it's just a
440 // matter of convenience.
441 int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1);
442 if (n == 1 && !Float.isNaN(nextRoots[0])) {
443 t = nextRoots[0];
444 }
445 }
446 // t is relative to the current leaf, so we must make it a valid parameter
447 // of the original curve.
448 t = t * (nextT - lastT) + lastT;
449 if (t >= 1) {
450 t = 1;
451 done = true;
452 }
453 // even if done = true, if we're here, that means targetLength
454 // is equal to, or very, very close to the total length of the
455 // curve, so lastSegLen won't be too high. In cases where len
456 // overshoots the curve, this method will exit in the while
457 // loop, and lastSegLen will still be set to the right value.
458 lastSegLen = len;
459 return t;
460 }
461
462 public float lastSegLen() {
463 return lastSegLen;
464 }
465
466 // go to the next leaf (in an inorder traversal) in the recursion tree
467 // preconditions: must be on a leaf, and that leaf must not be the root.
468 private void goToNextLeaf() {
469 // We must go to the first ancestor node that has an unvisited
470 // right child.
471 recLevel--;
472 while(sides[recLevel] == Side.RIGHT) {
473 if (recLevel == 0) {
474 done = true;
475 return;
476 }
477 recLevel--;
478 }
479
480 sides[recLevel] = Side.RIGHT;
481 System.arraycopy(recCurveStack[recLevel], 0, recCurveStack[recLevel+1], 0, curveType);
482 recLevel++;
483 goLeft();
484 }
485
486 // go to the leftmost node from the current node. Return its length.
487 private void goLeft() {
488 float len = onLeaf();
489 if (len >= 0) {
490 lastT = nextT;
491 lenAtLastT = lenAtNextT;
492 nextT += (1 << (limit - recLevel)) * minTincrement;
493 lenAtNextT += len;
494 // invalidate caches
495 flatLeafCoefCache[2] = -1;
496 cachedHaveLowAcceleration = -1;
497 } else {
498 Helpers.subdivide(recCurveStack[recLevel], 0,
499 recCurveStack[recLevel+1], 0,
500 recCurveStack[recLevel], 0, curveType);
501 sides[recLevel] = Side.LEFT;
502 recLevel++;
503 goLeft();
504 }
505 }
506
507 // this is a bit of a hack. It returns -1 if we're not on a leaf, and
508 // the length of the leaf if we are on a leaf.
509 private float onLeaf() {
510 float[] curve = recCurveStack[recLevel];
511 float polyLen = 0;
512
513 float x0 = curve[0], y0 = curve[1];
514 for (int i = 2; i < curveType; i += 2) {
515 final float x1 = curve[i], y1 = curve[i+1];
516 final float len = Helpers.linelen(x0, y0, x1, y1);
517 polyLen += len;
518 curLeafCtrlPolyLengths[i/2 - 1] = len;
519 x0 = x1;
520 y0 = y1;
521 }
522
523 final float lineLen = Helpers.linelen(curve[0], curve[1], curve[curveType-2], curve[curveType-1]);
524 if (polyLen - lineLen < ERR || recLevel == limit) {
525 return (polyLen + lineLen)/2;
526 }
527 return -1;
528 }
529 }
530
531 @Override
532 public void curveTo(float x1, float y1,
533 float x2, float y2,
534 float x3, float y3)
535 {
536 curCurvepts[0] = x0; curCurvepts[1] = y0;
537 curCurvepts[2] = x1; curCurvepts[3] = y1;
538 curCurvepts[4] = x2; curCurvepts[5] = y2;
539 curCurvepts[6] = x3; curCurvepts[7] = y3;
540 somethingTo(8);
541 }
542
543 @Override
544 public void quadTo(float x1, float y1, float x2, float y2) {
545 curCurvepts[0] = x0; curCurvepts[1] = y0;
546 curCurvepts[2] = x1; curCurvepts[3] = y1;
547 curCurvepts[4] = x2; curCurvepts[5] = y2;
548 somethingTo(6);
549 }
550
551 public void closePath() {
552 lineTo(sx, sy);
553 if (firstSegidx > 0) {
554 if (!dashOn || needsMoveTo) {
555 out.moveTo(sx, sy);
556 }
557 emitFirstSegments();
558 }
559 moveTo(sx, sy);
560 }
561
562 public void pathDone() {
563 if (firstSegidx > 0) {
564 out.moveTo(sx, sy);
565 emitFirstSegments();
566 }
567 out.pathDone();
568 }
569
570 @Override
571 public long getNativeConsumer() {
572 throw new InternalError("Dasher does not use a native consumer");
573 }
574}
575
java/pkg/sun/java2d/pisces/Helpers.java+458−0
@@ -0,0 +1,458 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import java.util.Arrays;
29import static java.lang.Math.PI;
30import static java.lang.Math.cos;
31import static java.lang.Math.sqrt;
32import static java.lang.Math.cbrt;
33import static java.lang.Math.acos;
34
35
36final class Helpers {
37 private Helpers() {
38 throw new Error("This is a non instantiable class");
39 }
40
41 static boolean within(final float x, final float y, final float err) {
42 final float d = y - x;
43 return (d <= err && d >= -err);
44 }
45
46 static boolean within(final double x, final double y, final double err) {
47 final double d = y - x;
48 return (d <= err && d >= -err);
49 }
50
51 static int quadraticRoots(final float a, final float b,
52 final float c, float[] zeroes, final int off)
53 {
54 int ret = off;
55 float t;
56 if (a != 0f) {
57 final float dis = b*b - 4*a*c;
58 if (dis > 0) {
59 final float sqrtDis = (float)Math.sqrt(dis);
60 // depending on the sign of b we use a slightly different
61 // algorithm than the traditional one to find one of the roots
62 // so we can avoid adding numbers of different signs (which
63 // might result in loss of precision).
64 if (b >= 0) {
65 zeroes[ret++] = (2 * c) / (-b - sqrtDis);
66 zeroes[ret++] = (-b - sqrtDis) / (2 * a);
67 } else {
68 zeroes[ret++] = (-b + sqrtDis) / (2 * a);
69 zeroes[ret++] = (2 * c) / (-b + sqrtDis);
70 }
71 } else if (dis == 0f) {
72 t = (-b) / (2 * a);
73 zeroes[ret++] = t;
74 }
75 } else {
76 if (b != 0f) {
77 t = (-c) / b;
78 zeroes[ret++] = t;
79 }
80 }
81 return ret - off;
82 }
83
84 // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
85 static int cubicRootsInAB(float d, float a, float b, float c,
86 float[] pts, final int off,
87 final float A, final float B)
88 {
89 if (d == 0) {
90 int num = quadraticRoots(a, b, c, pts, off);
91 return filterOutNotInAB(pts, off, num, A, B) - off;
92 }
93 // From Graphics Gems:
94 // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
95 // (also from awt.geom.CubicCurve2D. But here we don't need as
96 // much accuracy and we don't want to create arrays so we use
97 // our own customized version).
98
99 /* normal form: x^3 + ax^2 + bx + c = 0 */
100 a /= d;
101 b /= d;
102 c /= d;
103
104 // substitute x = y - A/3 to eliminate quadratic term:
105 // x^3 +Px + Q = 0
106 //
107 // Since we actually need P/3 and Q/2 for all of the
108 // calculations that follow, we will calculate
109 // p = P/3
110 // q = Q/2
111 // instead and use those values for simplicity of the code.
112 double sq_A = a * a;
113 double p = 1.0/3 * (-1.0/3 * sq_A + b);
114 double q = 1.0/2 * (2.0/27 * a * sq_A - 1.0/3 * a * b + c);
115
116 /* use Cardano's formula */
117
118 double cb_p = p * p * p;
119 double D = q * q + cb_p;
120
121 int num;
122 if (D < 0) {
123 // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
124 final double phi = 1.0/3 * acos(-q / sqrt(-cb_p));
125 final double t = 2 * sqrt(-p);
126
127 pts[ off+0 ] = (float)( t * cos(phi));
128 pts[ off+1 ] = (float)(-t * cos(phi + PI / 3));
129 pts[ off+2 ] = (float)(-t * cos(phi - PI / 3));
130 num = 3;
131 } else {
132 final double sqrt_D = sqrt(D);
133 final double u = cbrt(sqrt_D - q);
134 final double v = - cbrt(sqrt_D + q);
135
136 pts[ off ] = (float)(u + v);
137 num = 1;
138
139 if (within(D, 0, 1e-8)) {
140 pts[off+1] = -(pts[off] / 2);
141 num = 2;
142 }
143 }
144
145 final float sub = 1.0f/3 * a;
146
147 for (int i = 0; i < num; ++i) {
148 pts[ off+i ] -= sub;
149 }
150
151 return filterOutNotInAB(pts, off, num, A, B) - off;
152 }
153
154 // These use a hardcoded factor of 2 for increasing sizes. Perhaps this
155 // should be provided as an argument.
156 static float[] widenArray(float[] in, final int cursize, final int numToAdd) {
157 if (in.length >= cursize + numToAdd) {
158 return in;
159 }
160 return Arrays.copyOf(in, 2 * (cursize + numToAdd));
161 }
162
163 static int[] widenArray(int[] in, final int cursize, final int numToAdd) {
164 if (in.length >= cursize + numToAdd) {
165 return in;
166 }
167 return Arrays.copyOf(in, 2 * (cursize + numToAdd));
168 }
169
170 static float evalCubic(final float a, final float b,
171 final float c, final float d,
172 final float t)
173 {
174 return t * (t * (t * a + b) + c) + d;
175 }
176
177 static float evalQuad(final float a, final float b,
178 final float c, final float t)
179 {
180 return t * (t * a + b) + c;
181 }
182
183 // returns the index 1 past the last valid element remaining after filtering
184 static int filterOutNotInAB(float[] nums, final int off, final int len,
185 final float a, final float b)
186 {
187 int ret = off;
188 for (int i = off; i < off + len; i++) {
189 if (nums[i] >= a && nums[i] < b) {
190 nums[ret++] = nums[i];
191 }
192 }
193 return ret;
194 }
195
196 static float polyLineLength(float[] poly, final int off, final int nCoords) {
197 assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
198 float acc = 0;
199 for (int i = off + 2; i < off + nCoords; i += 2) {
200 acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
201 }
202 return acc;
203 }
204
205 static float linelen(float x1, float y1, float x2, float y2) {
206 final float dx = x2 - x1;
207 final float dy = y2 - y1;
208 return (float)Math.sqrt(dx*dx + dy*dy);
209 }
210
211 static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
212 float[] right, int rightoff, int type)
213 {
214 switch(type) {
215 case 6:
216 Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
217 break;
218 case 8:
219 Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
220 break;
221 default:
222 throw new InternalError("Unsupported curve type");
223 }
224 }
225
226 static void isort(float[] a, int off, int len) {
227 for (int i = off + 1; i < off + len; i++) {
228 float ai = a[i];
229 int j = i - 1;
230 for (; j >= off && a[j] > ai; j--) {
231 a[j+1] = a[j];
232 }
233 a[j+1] = ai;
234 }
235 }
236
237 // Most of these are copied from classes in java.awt.geom because we need
238 // float versions of these functions, and Line2D, CubicCurve2D,
239 // QuadCurve2D don't provide them.
240 /**
241 * Subdivides the cubic curve specified by the coordinates
242 * stored in the <code>src</code> array at indices <code>srcoff</code>
243 * through (<code>srcoff</code>&nbsp;+&nbsp;7) and stores the
244 * resulting two subdivided curves into the two result arrays at the
245 * corresponding indices.
246 * Either or both of the <code>left</code> and <code>right</code>
247 * arrays may be <code>null</code> or a reference to the same array
248 * as the <code>src</code> array.
249 * Note that the last point in the first subdivided curve is the
250 * same as the first point in the second subdivided curve. Thus,
251 * it is possible to pass the same array for <code>left</code>
252 * and <code>right</code> and to use offsets, such as <code>rightoff</code>
253 * equals (<code>leftoff</code> + 6), in order
254 * to avoid allocating extra storage for this common point.
255 * @param src the array holding the coordinates for the source curve
256 * @param srcoff the offset into the array of the beginning of the
257 * the 6 source coordinates
258 * @param left the array for storing the coordinates for the first
259 * half of the subdivided curve
260 * @param leftoff the offset into the array of the beginning of the
261 * the 6 left coordinates
262 * @param right the array for storing the coordinates for the second
263 * half of the subdivided curve
264 * @param rightoff the offset into the array of the beginning of the
265 * the 6 right coordinates
266 * @since 1.7
267 */
268 static void subdivideCubic(float src[], int srcoff,
269 float left[], int leftoff,
270 float right[], int rightoff)
271 {
272 float x1 = src[srcoff + 0];
273 float y1 = src[srcoff + 1];
274 float ctrlx1 = src[srcoff + 2];
275 float ctrly1 = src[srcoff + 3];
276 float ctrlx2 = src[srcoff + 4];
277 float ctrly2 = src[srcoff + 5];
278 float x2 = src[srcoff + 6];
279 float y2 = src[srcoff + 7];
280 if (left != null) {
281 left[leftoff + 0] = x1;
282 left[leftoff + 1] = y1;
283 }
284 if (right != null) {
285 right[rightoff + 6] = x2;
286 right[rightoff + 7] = y2;
287 }
288 x1 = (x1 + ctrlx1) / 2.0f;
289 y1 = (y1 + ctrly1) / 2.0f;
290 x2 = (x2 + ctrlx2) / 2.0f;
291 y2 = (y2 + ctrly2) / 2.0f;
292 float centerx = (ctrlx1 + ctrlx2) / 2.0f;
293 float centery = (ctrly1 + ctrly2) / 2.0f;
294 ctrlx1 = (x1 + centerx) / 2.0f;
295 ctrly1 = (y1 + centery) / 2.0f;
296 ctrlx2 = (x2 + centerx) / 2.0f;
297 ctrly2 = (y2 + centery) / 2.0f;
298 centerx = (ctrlx1 + ctrlx2) / 2.0f;
299 centery = (ctrly1 + ctrly2) / 2.0f;
300 if (left != null) {
301 left[leftoff + 2] = x1;
302 left[leftoff + 3] = y1;
303 left[leftoff + 4] = ctrlx1;
304 left[leftoff + 5] = ctrly1;
305 left[leftoff + 6] = centerx;
306 left[leftoff + 7] = centery;
307 }
308 if (right != null) {
309 right[rightoff + 0] = centerx;
310 right[rightoff + 1] = centery;
311 right[rightoff + 2] = ctrlx2;
312 right[rightoff + 3] = ctrly2;
313 right[rightoff + 4] = x2;
314 right[rightoff + 5] = y2;
315 }
316 }
317
318
319 static void subdivideCubicAt(float t, float src[], int srcoff,
320 float left[], int leftoff,
321 float right[], int rightoff)
322 {
323 float x1 = src[srcoff + 0];
324 float y1 = src[srcoff + 1];
325 float ctrlx1 = src[srcoff + 2];
326 float ctrly1 = src[srcoff + 3];
327 float ctrlx2 = src[srcoff + 4];
328 float ctrly2 = src[srcoff + 5];
329 float x2 = src[srcoff + 6];
330 float y2 = src[srcoff + 7];
331 if (left != null) {
332 left[leftoff + 0] = x1;
333 left[leftoff + 1] = y1;
334 }
335 if (right != null) {
336 right[rightoff + 6] = x2;
337 right[rightoff + 7] = y2;
338 }
339 x1 = x1 + t * (ctrlx1 - x1);
340 y1 = y1 + t * (ctrly1 - y1);
341 x2 = ctrlx2 + t * (x2 - ctrlx2);
342 y2 = ctrly2 + t * (y2 - ctrly2);
343 float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
344 float centery = ctrly1 + t * (ctrly2 - ctrly1);
345 ctrlx1 = x1 + t * (centerx - x1);
346 ctrly1 = y1 + t * (centery - y1);
347 ctrlx2 = centerx + t * (x2 - centerx);
348 ctrly2 = centery + t * (y2 - centery);
349 centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
350 centery = ctrly1 + t * (ctrly2 - ctrly1);
351 if (left != null) {
352 left[leftoff + 2] = x1;
353 left[leftoff + 3] = y1;
354 left[leftoff + 4] = ctrlx1;
355 left[leftoff + 5] = ctrly1;
356 left[leftoff + 6] = centerx;
357 left[leftoff + 7] = centery;
358 }
359 if (right != null) {
360 right[rightoff + 0] = centerx;
361 right[rightoff + 1] = centery;
362 right[rightoff + 2] = ctrlx2;
363 right[rightoff + 3] = ctrly2;
364 right[rightoff + 4] = x2;
365 right[rightoff + 5] = y2;
366 }
367 }
368
369 static void subdivideQuad(float src[], int srcoff,
370 float left[], int leftoff,
371 float right[], int rightoff)
372 {
373 float x1 = src[srcoff + 0];
374 float y1 = src[srcoff + 1];
375 float ctrlx = src[srcoff + 2];
376 float ctrly = src[srcoff + 3];
377 float x2 = src[srcoff + 4];
378 float y2 = src[srcoff + 5];
379 if (left != null) {
380 left[leftoff + 0] = x1;
381 left[leftoff + 1] = y1;
382 }
383 if (right != null) {
384 right[rightoff + 4] = x2;
385 right[rightoff + 5] = y2;
386 }
387 x1 = (x1 + ctrlx) / 2.0f;
388 y1 = (y1 + ctrly) / 2.0f;
389 x2 = (x2 + ctrlx) / 2.0f;
390 y2 = (y2 + ctrly) / 2.0f;
391 ctrlx = (x1 + x2) / 2.0f;
392 ctrly = (y1 + y2) / 2.0f;
393 if (left != null) {
394 left[leftoff + 2] = x1;
395 left[leftoff + 3] = y1;
396 left[leftoff + 4] = ctrlx;
397 left[leftoff + 5] = ctrly;
398 }
399 if (right != null) {
400 right[rightoff + 0] = ctrlx;
401 right[rightoff + 1] = ctrly;
402 right[rightoff + 2] = x2;
403 right[rightoff + 3] = y2;
404 }
405 }
406
407 static void subdivideQuadAt(float t, float src[], int srcoff,
408 float left[], int leftoff,
409 float right[], int rightoff)
410 {
411 float x1 = src[srcoff + 0];
412 float y1 = src[srcoff + 1];
413 float ctrlx = src[srcoff + 2];
414 float ctrly = src[srcoff + 3];
415 float x2 = src[srcoff + 4];
416 float y2 = src[srcoff + 5];
417 if (left != null) {
418 left[leftoff + 0] = x1;
419 left[leftoff + 1] = y1;
420 }
421 if (right != null) {
422 right[rightoff + 4] = x2;
423 right[rightoff + 5] = y2;
424 }
425 x1 = x1 + t * (ctrlx - x1);
426 y1 = y1 + t * (ctrly - y1);
427 x2 = ctrlx + t * (x2 - ctrlx);
428 y2 = ctrly + t * (y2 - ctrly);
429 ctrlx = x1 + t * (x2 - x1);
430 ctrly = y1 + t * (y2 - y1);
431 if (left != null) {
432 left[leftoff + 2] = x1;
433 left[leftoff + 3] = y1;
434 left[leftoff + 4] = ctrlx;
435 left[leftoff + 5] = ctrly;
436 }
437 if (right != null) {
438 right[rightoff + 0] = ctrlx;
439 right[rightoff + 1] = ctrly;
440 right[rightoff + 2] = x2;
441 right[rightoff + 3] = y2;
442 }
443 }
444
445 static void subdivideAt(float t, float src[], int srcoff,
446 float left[], int leftoff,
447 float right[], int rightoff, int size)
448 {
449 switch(size) {
450 case 8:
451 subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
452 break;
453 case 6:
454 subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
455 break;
456 }
457 }
458}
java/pkg/sun/java2d/pisces/META-INF/mkfile+6−0
@@ -0,0 +1,6 @@
1<../../../../../../mkconfig
2
3DIRS=\
4 services\
5
6<$ROOT/mkfiles/mksubdirs
java/pkg/sun/java2d/pisces/META-INF/services/mkfile+2−0
@@ -0,0 +1,2 @@
1<../../../../../../../mkconfig
2
java/pkg/sun/java2d/pisces/META-INF/services/sun.java2d.pipe.RenderingEngine+2−0
@@ -0,0 +1,2 @@
1# Pisces Rendering Engine module
2sun.java2d.pisces.PiscesRenderingEngine
java/pkg/sun/java2d/pisces/PiscesCache.java+186−0
@@ -0,0 +1,186 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import java.util.Arrays;
29
30/**
31 * An object used to cache pre-rendered complex paths.
32 *
33 * @see PiscesRenderer#render
34 */
35final class PiscesCache {
36
37 final int bboxX0, bboxY0, bboxX1, bboxY1;
38
39 // rowAARLE[i] holds the encoding of the pixel row with y = bboxY0+i.
40 // The format of each of the inner arrays is: rowAARLE[i][0,1] = (x0, n)
41 // where x0 is the first x in row i with nonzero alpha, and n is the
42 // number of RLE entries in this row. rowAARLE[i][j,j+1] for j>1 is
43 // (val,runlen)
44 final int[][] rowAARLE;
45
46 // RLE encodings are added in increasing y rows and then in increasing
47 // x inside those rows. Therefore, at any one time there is a well
48 // defined position (x,y) where a run length is about to be added (or
49 // the row terminated). x0,y0 is this (x,y)-(bboxX0,bboxY0). They
50 // are used to get indices into the current tile.
51 private int x0 = Integer.MIN_VALUE, y0 = Integer.MIN_VALUE;
52
53 // touchedTile[i][j] is the sum of all the alphas in the tile with
54 // y=i*TILE_SIZE+bboxY0 and x=j*TILE_SIZE+bboxX0.
55 private final int[][] touchedTile;
56
57 static final int TILE_SIZE_LG = 5;
58 static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32
59 private static final int INIT_ROW_SIZE = 8; // enough for 3 run lengths
60
61 PiscesCache(int minx, int miny, int maxx, int maxy) {
62 assert maxy >= miny && maxx >= minx;
63 bboxX0 = minx;
64 bboxY0 = miny;
65 bboxX1 = maxx + 1;
66 bboxY1 = maxy + 1;
67 // we could just leave the inner arrays as null and allocate them
68 // lazily (which would be beneficial for shapes with gaps), but we
69 // assume there won't be too many of those so we allocate everything
70 // up front (which is better for other cases)
71 rowAARLE = new int[bboxY1 - bboxY0 + 1][INIT_ROW_SIZE];
72 x0 = 0;
73 y0 = -1; // -1 makes the first assert in startRow succeed
74 // the ceiling of (maxy - miny + 1) / TILE_SIZE;
75 int nyTiles = (maxy - miny + TILE_SIZE) >> TILE_SIZE_LG;
76 int nxTiles = (maxx - minx + TILE_SIZE) >> TILE_SIZE_LG;
77
78 touchedTile = new int[nyTiles][nxTiles];
79 }
80
81 void addRLERun(int val, int runLen) {
82 if (runLen > 0) {
83 addTupleToRow(y0, val, runLen);
84 if (val != 0) {
85 // the x and y of the current row, minus bboxX0, bboxY0
86 int tx = x0 >> TILE_SIZE_LG;
87 int ty = y0 >> TILE_SIZE_LG;
88 int tx1 = (x0 + runLen - 1) >> TILE_SIZE_LG;
89 // while we forbid rows from starting before bboxx0, our users
90 // can still store rows that go beyond bboxx1 (although this
91 // shouldn't happen), so it's a good idea to check that i
92 // is not going out of bounds in touchedTile[ty]
93 if (tx1 >= touchedTile[ty].length) {
94 tx1 = touchedTile[ty].length - 1;
95 }
96 if (tx <= tx1) {
97 int nextTileXCoord = (tx + 1) << TILE_SIZE_LG;
98 if (nextTileXCoord > x0+runLen) {
99 touchedTile[ty][tx] += val * runLen;
100 } else {
101 touchedTile[ty][tx] += val * (nextTileXCoord - x0);
102 }
103 tx++;
104 }
105 // don't go all the way to tx1 - we need to handle the last
106 // tile as a special case (just like we did with the first
107 for (; tx < tx1; tx++) {
108// try {
109 touchedTile[ty][tx] += (val << TILE_SIZE_LG);
110// } catch (RuntimeException e) {
111// System.out.println("x0, y0: " + x0 + ", " + y0);
112// System.out.printf("tx, ty, tx1: %d, %d, %d %n", tx, ty, tx1);
113// System.out.printf("bboxX/Y0/1: %d, %d, %d, %d %n",
114// bboxX0, bboxY0, bboxX1, bboxY1);
115// throw e;
116// }
117 }
118 // they will be equal unless x0>>TILE_SIZE_LG == tx1
119 if (tx == tx1) {
120 int lastXCoord = Math.min(x0 + runLen, (tx + 1) << TILE_SIZE_LG);
121 int txXCoord = tx << TILE_SIZE_LG;
122 touchedTile[ty][tx] += val * (lastXCoord - txXCoord);
123 }
124 }
125 x0 += runLen;
126 }
127 }
128
129 void startRow(int y, int x) {
130 // rows are supposed to be added by increasing y.
131 assert y - bboxY0 > y0;
132 assert y <= bboxY1; // perhaps this should be < instead of <=
133
134 y0 = y - bboxY0;
135 // this should be a new, uninitialized row.
136 assert rowAARLE[y0][1] == 0;
137
138 x0 = x - bboxX0;
139 assert x0 >= 0 : "Input must not be to the left of bbox bounds";
140
141 // the way addTupleToRow is implemented it would work for this but it's
142 // not a good idea to use it because it is meant for adding
143 // RLE tuples, not the first tuple (which is special).
144 rowAARLE[y0][0] = x;
145 rowAARLE[y0][1] = 2;
146 }
147
148 int alphaSumInTile(int x, int y) {
149 x -= bboxX0;
150 y -= bboxY0;
151 return touchedTile[y>>TILE_SIZE_LG][x>>TILE_SIZE_LG];
152 }
153
154 int minTouched(int rowidx) {
155 return rowAARLE[rowidx][0];
156 }
157
158 int rowLength(int rowidx) {
159 return rowAARLE[rowidx][1];
160 }
161
162 private void addTupleToRow(int row, int a, int b) {
163 int end = rowAARLE[row][1];
164 rowAARLE[row] = Helpers.widenArray(rowAARLE[row], end, 2);
165 rowAARLE[row][end++] = a;
166 rowAARLE[row][end++] = b;
167 rowAARLE[row][1] = end;
168 }
169
170 @Override
171 public String toString() {
172 String ret = "bbox = ["+
173 bboxX0+", "+bboxY0+" => "+
174 bboxX1+", "+bboxY1+"]\n";
175 for (int[] row : rowAARLE) {
176 if (row != null) {
177 ret += ("minTouchedX=" + row[0] +
178 "\tRLE Entries: " + Arrays.toString(
179 Arrays.copyOfRange(row, 2, row[1])) + "\n");
180 } else {
181 ret += "[]\n";
182 }
183 }
184 return ret;
185 }
186}
java/pkg/sun/java2d/pisces/PiscesRenderingEngine.java+656−0
@@ -0,0 +1,656 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import java.awt.Shape;
29import java.awt.BasicStroke;
30import java.awt.geom.Path2D;
31import java.awt.geom.AffineTransform;
32import java.awt.geom.PathIterator;
33
34import sun.awt.geom.PathConsumer2D;
35import sun.java2d.pipe.Region;
36import sun.java2d.pipe.RenderingEngine;
37import sun.java2d.pipe.AATileGenerator;
38
39public class PiscesRenderingEngine extends RenderingEngine {
40 private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA}
41
42 /**
43 * Create a widened path as specified by the parameters.
44 * <p>
45 * The specified {@code src} {@link Shape} is widened according
46 * to the specified attribute parameters as per the
47 * {@link BasicStroke} specification.
48 *
49 * @param src the source path to be widened
50 * @param width the width of the widened path as per {@code BasicStroke}
51 * @param caps the end cap decorations as per {@code BasicStroke}
52 * @param join the segment join decorations as per {@code BasicStroke}
53 * @param miterlimit the miter limit as per {@code BasicStroke}
54 * @param dashes the dash length array as per {@code BasicStroke}
55 * @param dashphase the initial dash phase as per {@code BasicStroke}
56 * @return the widened path stored in a new {@code Shape} object
57 * @since 1.7
58 */
59 public Shape createStrokedShape(Shape src,
60 float width,
61 int caps,
62 int join,
63 float miterlimit,
64 float dashes[],
65 float dashphase)
66 {
67 final Path2D p2d = new Path2D.Float();
68
69 strokeTo(src,
70 null,
71 width,
72 NormMode.OFF,
73 caps,
74 join,
75 miterlimit,
76 dashes,
77 dashphase,
78 new PathConsumer2D() {
79 public void moveTo(float x0, float y0) {
80 p2d.moveTo(x0, y0);
81 }
82 public void lineTo(float x1, float y1) {
83 p2d.lineTo(x1, y1);
84 }
85 public void closePath() {
86 p2d.closePath();
87 }
88 public void pathDone() {}
89 public void curveTo(float x1, float y1,
90 float x2, float y2,
91 float x3, float y3) {
92 p2d.curveTo(x1, y1, x2, y2, x3, y3);
93 }
94 public void quadTo(float x1, float y1, float x2, float y2) {
95 p2d.quadTo(x1, y1, x2, y2);
96 }
97 public long getNativeConsumer() {
98 throw new InternalError("Not using a native peer");
99 }
100 });
101 return p2d;
102 }
103
104 /**
105 * Sends the geometry for a widened path as specified by the parameters
106 * to the specified consumer.
107 * <p>
108 * The specified {@code src} {@link Shape} is widened according
109 * to the parameters specified by the {@link BasicStroke} object.
110 * Adjustments are made to the path as appropriate for the
111 * {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize}
112 * boolean parameter is true.
113 * Adjustments are made to the path as appropriate for the
114 * {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias}
115 * boolean parameter is true.
116 * <p>
117 * The geometry of the widened path is forwarded to the indicated
118 * {@link PathConsumer2D} object as it is calculated.
119 *
120 * @param src the source path to be widened
121 * @param bs the {@code BasicSroke} object specifying the
122 * decorations to be applied to the widened path
123 * @param normalize indicates whether stroke normalization should
124 * be applied
125 * @param antialias indicates whether or not adjustments appropriate
126 * to antialiased rendering should be applied
127 * @param consumer the {@code PathConsumer2D} instance to forward
128 * the widened geometry to
129 * @since 1.7
130 */
131 public void strokeTo(Shape src,
132 AffineTransform at,
133 BasicStroke bs,
134 boolean thin,
135 boolean normalize,
136 boolean antialias,
137 final PathConsumer2D consumer)
138 {
139 NormMode norm = (normalize) ?
140 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
141 : NormMode.OFF;
142 strokeTo(src, at, bs, thin, norm, antialias, consumer);
143 }
144
145 void strokeTo(Shape src,
146 AffineTransform at,
147 BasicStroke bs,
148 boolean thin,
149 NormMode normalize,
150 boolean antialias,
151 PathConsumer2D pc2d)
152 {
153 float lw;
154 if (thin) {
155 if (antialias) {
156 lw = userSpaceLineWidth(at, 0.5f);
157 } else {
158 lw = userSpaceLineWidth(at, 1.0f);
159 }
160 } else {
161 lw = bs.getLineWidth();
162 }
163 strokeTo(src,
164 at,
165 lw,
166 normalize,
167 bs.getEndCap(),
168 bs.getLineJoin(),
169 bs.getMiterLimit(),
170 bs.getDashArray(),
171 bs.getDashPhase(),
172 pc2d);
173 }
174
175 private float userSpaceLineWidth(AffineTransform at, float lw) {
176
177 double widthScale;
178
179 if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM |
180 AffineTransform.TYPE_GENERAL_SCALE)) != 0) {
181 widthScale = Math.sqrt(at.getDeterminant());
182 } else {
183 /* First calculate the "maximum scale" of this transform. */
184 double A = at.getScaleX(); // m00
185 double C = at.getShearX(); // m01
186 double B = at.getShearY(); // m10
187 double D = at.getScaleY(); // m11
188
189 /*
190 * Given a 2 x 2 affine matrix [ A B ] such that
191 * [ C D ]
192 * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
193 * find the maximum magnitude (norm) of the vector v'
194 * with the constraint (x^2 + y^2 = 1).
195 * The equation to maximize is
196 * |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
197 * or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
198 * Since sqrt is monotonic we can maximize |v'|^2
199 * instead and plug in the substitution y = sqrt(1 - x^2).
200 * Trigonometric equalities can then be used to get
201 * rid of most of the sqrt terms.
202 */
203
204 double EA = A*A + B*B; // x^2 coefficient
205 double EB = 2*(A*C + B*D); // xy coefficient
206 double EC = C*C + D*D; // y^2 coefficient
207
208 /*
209 * There is a lot of calculus omitted here.
210 *
211 * Conceptually, in the interests of understanding the
212 * terms that the calculus produced we can consider
213 * that EA and EC end up providing the lengths along
214 * the major axes and the hypot term ends up being an
215 * adjustment for the additional length along the off-axis
216 * angle of rotated or sheared ellipses as well as an
217 * adjustment for the fact that the equation below
218 * averages the two major axis lengths. (Notice that
219 * the hypot term contains a part which resolves to the
220 * difference of these two axis lengths in the absence
221 * of rotation.)
222 *
223 * In the calculus, the ratio of the EB and (EA-EC) terms
224 * ends up being the tangent of 2*theta where theta is
225 * the angle that the long axis of the ellipse makes
226 * with the horizontal axis. Thus, this equation is
227 * calculating the length of the hypotenuse of a triangle
228 * along that axis.
229 */
230
231 double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
232 /* sqrt omitted, compare to squared limits below. */
233 double widthsquared = ((EA + EC + hypot)/2.0);
234
235 widthScale = Math.sqrt(widthsquared);
236 }
237
238 return (float) (lw / widthScale);
239 }
240
241 void strokeTo(Shape src,
242 AffineTransform at,
243 float width,
244 NormMode normalize,
245 int caps,
246 int join,
247 float miterlimit,
248 float dashes[],
249 float dashphase,
250 PathConsumer2D pc2d)
251 {
252 // We use strokerat and outat so that in Stroker and Dasher we can work only
253 // with the pre-transformation coordinates. This will repeat a lot of
254 // computations done in the path iterator, but the alternative is to
255 // work with transformed paths and compute untransformed coordinates
256 // as needed. This would be faster but I do not think the complexity
257 // of working with both untransformed and transformed coordinates in
258 // the same code is worth it.
259 // However, if a path's width is constant after a transformation,
260 // we can skip all this untransforming.
261
262 // If normalization is off we save some transformations by not
263 // transforming the input to pisces. Instead, we apply the
264 // transformation after the path processing has been done.
265 // We can't do this if normalization is on, because it isn't a good
266 // idea to normalize before the transformation is applied.
267 AffineTransform strokerat = null;
268 AffineTransform outat = null;
269
270 PathIterator pi = null;
271
272 if (at != null && !at.isIdentity()) {
273 final double a = at.getScaleX();
274 final double b = at.getShearX();
275 final double c = at.getShearY();
276 final double d = at.getScaleY();
277 final double det = a * d - c * b;
278 if (Math.abs(det) <= 2 * Float.MIN_VALUE) {
279 // this rendering engine takes one dimensional curves and turns
280 // them into 2D shapes by giving them width.
281 // However, if everything is to be passed through a singular
282 // transformation, these 2D shapes will be squashed down to 1D
283 // again so, nothing can be drawn.
284
285 // Every path needs an initial moveTo and a pathDone. If these
286 // are not there this causes a SIGSEGV in libawt.so (at the time
287 // of writing of this comment (September 16, 2010)). Actually,
288 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
289 // but the pathDone is definitely needed.
290 pc2d.moveTo(0, 0);
291 pc2d.pathDone();
292 return;
293 }
294
295 // If the transform is a constant multiple of an orthogonal transformation
296 // then every length is just multiplied by a constant, so we just
297 // need to transform input paths to stroker and tell stroker
298 // the scaled width. This condition is satisfied if
299 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
300 // leave a bit of room for error.
301 if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) {
302 double scale = Math.sqrt(a*a + c*c);
303 if (dashes != null) {
304 dashes = java.util.Arrays.copyOf(dashes, dashes.length);
305 for (int i = 0; i < dashes.length; i++) {
306 dashes[i] = (float)(scale * dashes[i]);
307 }
308 dashphase = (float)(scale * dashphase);
309 }
310 width = (float)(scale * width);
311 pi = src.getPathIterator(at);
312 if (normalize != NormMode.OFF) {
313 pi = new NormalizingPathIterator(pi, normalize);
314 }
315 // by now strokerat == null && outat == null. Input paths to
316 // stroker (and maybe dasher) will have the full transform at
317 // applied to them and nothing will happen to the output paths.
318 } else {
319 if (normalize != NormMode.OFF) {
320 strokerat = at;
321 pi = src.getPathIterator(at);
322 pi = new NormalizingPathIterator(pi, normalize);
323 // by now strokerat == at && outat == null. Input paths to
324 // stroker (and maybe dasher) will have the full transform at
325 // applied to them, then they will be normalized, and then
326 // the inverse of *only the non translation part of at* will
327 // be applied to the normalized paths. This won't cause problems
328 // in stroker, because, suppose at = T*A, where T is just the
329 // translation part of at, and A is the rest. T*A has already
330 // been applied to Stroker/Dasher's input. Then Ainv will be
331 // applied. Ainv*T*A is not equal to T, but it is a translation,
332 // which means that none of stroker's assumptions about its
333 // input will be violated. After all this, A will be applied
334 // to stroker's output.
335 } else {
336 outat = at;
337 pi = src.getPathIterator(null);
338 // outat == at && strokerat == null. This is because if no
339 // normalization is done, we can just apply all our
340 // transformations to stroker's output.
341 }
342 }
343 } else {
344 // either at is null or it's the identity. In either case
345 // we don't transform the path.
346 pi = src.getPathIterator(null);
347 if (normalize != NormMode.OFF) {
348 pi = new NormalizingPathIterator(pi, normalize);
349 }
350 }
351
352 // by now, at least one of outat and strokerat will be null. Unless at is not
353 // a constant multiple of an orthogonal transformation, they will both be
354 // null. In other cases, outat == at if normalization is off, and if
355 // normalization is on, strokerat == at.
356 pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat);
357 pc2d = TransformingPathConsumer2D.deltaTransformConsumer(pc2d, strokerat);
358 pc2d = new Stroker(pc2d, width, caps, join, miterlimit);
359 if (dashes != null) {
360 pc2d = new Dasher(pc2d, dashes, dashphase);
361 }
362 pc2d = TransformingPathConsumer2D.inverseDeltaTransformConsumer(pc2d, strokerat);
363 pathTo(pi, pc2d);
364 }
365
366 private static boolean nearZero(double num, int nulps) {
367 return Math.abs(num) < nulps * Math.ulp(num);
368 }
369
370 private static class NormalizingPathIterator implements PathIterator {
371
372 private final PathIterator src;
373
374 // the adjustment applied to the current position.
375 private float curx_adjust, cury_adjust;
376 // the adjustment applied to the last moveTo position.
377 private float movx_adjust, movy_adjust;
378
379 // constants used in normalization computations
380 private final float lval, rval;
381
382 NormalizingPathIterator(PathIterator src, NormMode mode) {
383 this.src = src;
384 switch (mode) {
385 case ON_NO_AA:
386 // round to nearest (0.25, 0.25) pixel
387 lval = rval = 0.25f;
388 break;
389 case ON_WITH_AA:
390 // round to nearest pixel center
391 lval = 0f;
392 rval = 0.5f;
393 break;
394 case OFF:
395 throw new InternalError("A NormalizingPathIterator should " +
396 "not be created if no normalization is being done");
397 default:
398 throw new InternalError("Unrecognized normalization mode");
399 }
400 }
401
402 public int currentSegment(float[] coords) {
403 int type = src.currentSegment(coords);
404
405 int lastCoord;
406 switch(type) {
407 case PathIterator.SEG_CUBICTO:
408 lastCoord = 4;
409 break;
410 case PathIterator.SEG_QUADTO:
411 lastCoord = 2;
412 break;
413 case PathIterator.SEG_LINETO:
414 case PathIterator.SEG_MOVETO:
415 lastCoord = 0;
416 break;
417 case PathIterator.SEG_CLOSE:
418 // we don't want to deal with this case later. We just exit now
419 curx_adjust = movx_adjust;
420 cury_adjust = movy_adjust;
421 return type;
422 default:
423 throw new InternalError("Unrecognized curve type");
424 }
425
426 // normalize endpoint
427 float x_adjust = (float)Math.floor(coords[lastCoord] + lval) +
428 rval - coords[lastCoord];
429 float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) +
430 rval - coords[lastCoord + 1];
431
432 coords[lastCoord ] += x_adjust;
433 coords[lastCoord + 1] += y_adjust;
434
435 // now that the end points are done, normalize the control points
436 switch(type) {
437 case PathIterator.SEG_CUBICTO:
438 coords[0] += curx_adjust;
439 coords[1] += cury_adjust;
440 coords[2] += x_adjust;
441 coords[3] += y_adjust;
442 break;
443 case PathIterator.SEG_QUADTO:
444 coords[0] += (curx_adjust + x_adjust) / 2;
445 coords[1] += (cury_adjust + y_adjust) / 2;
446 break;
447 case PathIterator.SEG_LINETO:
448 break;
449 case PathIterator.SEG_MOVETO:
450 movx_adjust = x_adjust;
451 movy_adjust = y_adjust;
452 break;
453 case PathIterator.SEG_CLOSE:
454 throw new InternalError("This should be handled earlier.");
455 }
456 curx_adjust = x_adjust;
457 cury_adjust = y_adjust;
458 return type;
459 }
460
461 public int currentSegment(double[] coords) {
462 float[] tmp = new float[6];
463 int type = this.currentSegment(tmp);
464 for (int i = 0; i < 6; i++) {
465 coords[i] = (float) tmp[i];
466 }
467 return type;
468 }
469
470 public int getWindingRule() {
471 return src.getWindingRule();
472 }
473
474 public boolean isDone() {
475 return src.isDone();
476 }
477
478 public void next() {
479 src.next();
480 }
481 }
482
483 static void pathTo(PathIterator pi, PathConsumer2D pc2d) {
484 RenderingEngine.feedConsumer(pi, pc2d);
485 pc2d.pathDone();
486 }
487
488 /**
489 * Construct an antialiased tile generator for the given shape with
490 * the given rendering attributes and store the bounds of the tile
491 * iteration in the bbox parameter.
492 * The {@code at} parameter specifies a transform that should affect
493 * both the shape and the {@code BasicStroke} attributes.
494 * The {@code clip} parameter specifies the current clip in effect
495 * in device coordinates and can be used to prune the data for the
496 * operation, but the renderer is not required to perform any
497 * clipping.
498 * If the {@code BasicStroke} parameter is null then the shape
499 * should be filled as is, otherwise the attributes of the
500 * {@code BasicStroke} should be used to specify a draw operation.
501 * The {@code thin} parameter indicates whether or not the
502 * transformed {@code BasicStroke} represents coordinates smaller
503 * than the minimum resolution of the antialiasing rasterizer as
504 * specified by the {@code getMinimumAAPenWidth()} method.
505 * <p>
506 * Upon returning, this method will fill the {@code bbox} parameter
507 * with 4 values indicating the bounds of the iteration of the
508 * tile generator.
509 * The iteration order of the tiles will be as specified by the
510 * pseudo-code:
511 * <pre>
512 * for (y = bbox[1]; y < bbox[3]; y += tileheight) {
513 * for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
514 * }
515 * }
516 * </pre>
517 * If there is no output to be rendered, this method may return
518 * null.
519 *
520 * @param s the shape to be rendered (fill or draw)
521 * @param at the transform to be applied to the shape and the
522 * stroke attributes
523 * @param clip the current clip in effect in device coordinates
524 * @param bs if non-null, a {@code BasicStroke} whose attributes
525 * should be applied to this operation
526 * @param thin true if the transformed stroke attributes are smaller
527 * than the minimum dropout pen width
528 * @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
529 * {@code RenderingHint} is in effect
530 * @param bbox returns the bounds of the iteration
531 * @return the {@code AATileGenerator} instance to be consulted
532 * for tile coverages, or null if there is no output to render
533 * @since 1.7
534 */
535 public AATileGenerator getAATileGenerator(Shape s,
536 AffineTransform at,
537 Region clip,
538 BasicStroke bs,
539 boolean thin,
540 boolean normalize,
541 int bbox[])
542 {
543 Renderer r;
544 NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
545 if (bs == null) {
546 PathIterator pi;
547 if (normalize) {
548 pi = new NormalizingPathIterator(s.getPathIterator(at), norm);
549 } else {
550 pi = s.getPathIterator(at);
551 }
552 r = new Renderer(3, 3,
553 clip.getLoX(), clip.getLoY(),
554 clip.getWidth(), clip.getHeight(),
555 pi.getWindingRule());
556 pathTo(pi, r);
557 } else {
558 r = new Renderer(3, 3,
559 clip.getLoX(), clip.getLoY(),
560 clip.getWidth(), clip.getHeight(),
561 PathIterator.WIND_NON_ZERO);
562 strokeTo(s, at, bs, thin, norm, true, r);
563 }
564 r.endRendering();
565 PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA);
566 ptg.getBbox(bbox);
567 return ptg;
568 }
569
570 public AATileGenerator getAATileGenerator(double x, double y,
571 double dx1, double dy1,
572 double dx2, double dy2,
573 double lw1, double lw2,
574 Region clip,
575 int bbox[])
576 {
577 // REMIND: Deal with large coordinates!
578 double ldx1, ldy1, ldx2, ldy2;
579 boolean innerpgram = (lw1 > 0 && lw2 > 0);
580
581 if (innerpgram) {
582 ldx1 = dx1 * lw1;
583 ldy1 = dy1 * lw1;
584 ldx2 = dx2 * lw2;
585 ldy2 = dy2 * lw2;
586 x -= (ldx1 + ldx2) / 2.0;
587 y -= (ldy1 + ldy2) / 2.0;
588 dx1 += ldx1;
589 dy1 += ldy1;
590 dx2 += ldx2;
591 dy2 += ldy2;
592 if (lw1 > 1 && lw2 > 1) {
593 // Inner parallelogram was entirely consumed by stroke...
594 innerpgram = false;
595 }
596 } else {
597 ldx1 = ldy1 = ldx2 = ldy2 = 0;
598 }
599
600 Renderer r = new Renderer(3, 3,
601 clip.getLoX(), clip.getLoY(),
602 clip.getWidth(), clip.getHeight(),
603 PathIterator.WIND_EVEN_ODD);
604
605 r.moveTo((float) x, (float) y);
606 r.lineTo((float) (x+dx1), (float) (y+dy1));
607 r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
608 r.lineTo((float) (x+dx2), (float) (y+dy2));
609 r.closePath();
610
611 if (innerpgram) {
612 x += ldx1 + ldx2;
613 y += ldy1 + ldy2;
614 dx1 -= 2.0 * ldx1;
615 dy1 -= 2.0 * ldy1;
616 dx2 -= 2.0 * ldx2;
617 dy2 -= 2.0 * ldy2;
618 r.moveTo((float) x, (float) y);
619 r.lineTo((float) (x+dx1), (float) (y+dy1));
620 r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
621 r.lineTo((float) (x+dx2), (float) (y+dy2));
622 r.closePath();
623 }
624
625 r.pathDone();
626
627 r.endRendering();
628 PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA);
629 ptg.getBbox(bbox);
630 return ptg;
631 }
632
633 /**
634 * Returns the minimum pen width that the antialiasing rasterizer
635 * can represent without dropouts occuring.
636 * @since 1.7
637 */
638 public float getMinimumAAPenSize() {
639 return 0.5f;
640 }
641
642 static {
643 if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO ||
644 PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD ||
645 BasicStroke.JOIN_MITER != Stroker.JOIN_MITER ||
646 BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND ||
647 BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL ||
648 BasicStroke.CAP_BUTT != Stroker.CAP_BUTT ||
649 BasicStroke.CAP_ROUND != Stroker.CAP_ROUND ||
650 BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE)
651 {
652 throw new InternalError("mismatched renderer constants");
653 }
654 }
655}
656
java/pkg/sun/java2d/pisces/PiscesTileGenerator.java+252−0
@@ -0,0 +1,252 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import java.util.Map;
29import java.util.concurrent.ConcurrentHashMap;
30
31import sun.java2d.pipe.AATileGenerator;
32
33final class PiscesTileGenerator implements AATileGenerator {
34 public static final int TILE_SIZE = PiscesCache.TILE_SIZE;
35
36 // perhaps we should be using weak references here, but right now
37 // that's not necessary. The way the renderer is, this map will
38 // never contain more than one element - the one with key 64, since
39 // we only do 8x8 supersampling.
40 private static final Map<Integer, byte[]> alphaMapsCache = new
41 ConcurrentHashMap<Integer, byte[]>();
42
43 PiscesCache cache;
44 int x, y;
45 final int maxalpha;
46 private final int maxTileAlphaSum;
47
48 // The alpha map used by this object (taken out of our map cache) to convert
49 // pixel coverage counts gotten from PiscesCache (which are in the range
50 // [0, maxalpha]) into alpha values, which are in [0,256).
51 byte alphaMap[];
52
53 public PiscesTileGenerator(Renderer r, int maxalpha) {
54 this.cache = r.getCache();
55 this.x = cache.bboxX0;
56 this.y = cache.bboxY0;
57 this.alphaMap = getAlphaMap(maxalpha);
58 this.maxalpha = maxalpha;
59 this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha;
60 }
61
62 private static byte[] buildAlphaMap(int maxalpha) {
63 byte[] alMap = new byte[maxalpha+1];
64 int halfmaxalpha = maxalpha>>2;
65 for (int i = 0; i <= maxalpha; i++) {
66 alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
67 }
68 return alMap;
69 }
70
71 public static byte[] getAlphaMap(int maxalpha) {
72 if (!alphaMapsCache.containsKey(maxalpha)) {
73 alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha));
74 }
75 return alphaMapsCache.get(maxalpha);
76 }
77
78 public void getBbox(int bbox[]) {
79 bbox[0] = cache.bboxX0;
80 bbox[1] = cache.bboxY0;
81 bbox[2] = cache.bboxX1;
82 bbox[3] = cache.bboxY1;
83 //System.out.println("bbox["+bbox[0]+", "+bbox[1]+" => "+bbox[2]+", "+bbox[3]+"]");
84 }
85
86 /**
87 * Gets the width of the tiles that the generator batches output into.
88 * @return the width of the standard alpha tile
89 */
90 public int getTileWidth() {
91 return TILE_SIZE;
92 }
93
94 /**
95 * Gets the height of the tiles that the generator batches output into.
96 * @return the height of the standard alpha tile
97 */
98 public int getTileHeight() {
99 return TILE_SIZE;
100 }
101
102 /**
103 * Gets the typical alpha value that will characterize the current
104 * tile.
105 * The answer may be 0x00 to indicate that the current tile has
106 * no coverage in any of its pixels, or it may be 0xff to indicate
107 * that the current tile is completely covered by the path, or any
108 * other value to indicate non-trivial coverage cases.
109 * @return 0x00 for no coverage, 0xff for total coverage, or any other
110 * value for partial coverage of the tile
111 */
112 public int getTypicalAlpha() {
113 int al = cache.alphaSumInTile(x, y);
114 // Note: if we have a filled rectangle that doesn't end on a tile
115 // border, we could still return 0xff, even though al!=maxTileAlphaSum
116 // This is because if we return 0xff, our users will fill a rectangle
117 // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
118 // and height min(TILE_SIZE,bboxY1-y), which is what should happen.
119 // However, to support this, we would have to use 2 Math.min's
120 // and 2 multiplications per tile, instead of just 2 multiplications
121 // to compute maxTileAlphaSum. The savings offered would probably
122 // not be worth it, considering how rare this case is.
123 // Note: I have not tested this, so in the future if it is determined
124 // that it is worth it, it should be implemented. Perhaps this method's
125 // interface should be changed to take arguments the width and height
126 // of the current tile. This would eliminate the 2 Math.min calls that
127 // would be needed here, since our caller needs to compute these 2
128 // values anyway.
129 return (al == 0x00 ? 0x00 :
130 (al == maxTileAlphaSum ? 0xff : 0x80));
131 }
132
133 /**
134 * Skips the current tile and moves on to the next tile.
135 * Either this method, or the getAlpha() method should be called
136 * once per tile, but not both.
137 */
138 public void nextTile() {
139 if ((x += TILE_SIZE) >= cache.bboxX1) {
140 x = cache.bboxX0;
141 y += TILE_SIZE;
142 }
143 }
144
145 /**
146 * Gets the alpha coverage values for the current tile.
147 * Either this method, or the nextTile() method should be called
148 * once per tile, but not both.
149 */
150 public void getAlpha(byte tile[], int offset, int rowstride) {
151 // Decode run-length encoded alpha mask data
152 // The data for row j begins at cache.rowOffsetsRLE[j]
153 // and is encoded as a set of 2-byte pairs (val, runLen)
154 // terminated by a (0, 0) pair.
155
156 int x0 = this.x;
157 int x1 = x0 + TILE_SIZE;
158 int y0 = this.y;
159 int y1 = y0 + TILE_SIZE;
160 if (x1 > cache.bboxX1) x1 = cache.bboxX1;
161 if (y1 > cache.bboxY1) y1 = cache.bboxY1;
162 y0 -= cache.bboxY0;
163 y1 -= cache.bboxY0;
164
165 int idx = offset;
166 for (int cy = y0; cy < y1; cy++) {
167 int[] row = cache.rowAARLE[cy];
168 assert row != null;
169 int cx = cache.minTouched(cy);
170 if (cx > x1) cx = x1;
171
172 for (int i = x0; i < cx; i++) {
173 tile[idx++] = 0x00;
174 }
175
176 int pos = 2;
177 while (cx < x1 && pos < row[1]) {
178 byte val;
179 int runLen = 0;
180 assert row[1] > 2;
181 try {
182 val = alphaMap[row[pos]];
183 runLen = row[pos + 1];
184 assert runLen > 0;
185 } catch (RuntimeException e0) {
186 System.out.println("maxalpha = "+maxalpha);
187 System.out.println("tile["+x0+", "+y0+
188 " => "+x1+", "+y1+"]");
189 System.out.println("cx = "+cx+", cy = "+cy);
190 System.out.println("idx = "+idx+", pos = "+pos);
191 System.out.println("len = "+runLen);
192 System.out.print(cache.toString());
193 e0.printStackTrace();
194 throw e0;
195 }
196
197 int rx0 = cx;
198 cx += runLen;
199 int rx1 = cx;
200 if (rx0 < x0) rx0 = x0;
201 if (rx1 > x1) rx1 = x1;
202 runLen = rx1 - rx0;
203 //System.out.println("M["+runLen+"]");
204 while (--runLen >= 0) {
205 try {
206 tile[idx++] = val;
207 } catch (RuntimeException e) {
208 System.out.println("maxalpha = "+maxalpha);
209 System.out.println("tile["+x0+", "+y0+
210 " => "+x1+", "+y1+"]");
211 System.out.println("cx = "+cx+", cy = "+cy);
212 System.out.println("idx = "+idx+", pos = "+pos);
213 System.out.println("rx0 = "+rx0+", rx1 = "+rx1);
214 System.out.println("len = "+runLen);
215 System.out.print(cache.toString());
216 e.printStackTrace();
217 throw e;
218 }
219 }
220 pos += 2;
221 }
222 if (cx < x0) { cx = x0; }
223 while (cx < x1) {
224 tile[idx++] = 0x00;
225 cx++;
226 }
227 /*
228 for (int i = idx - (x1-x0); i < idx; i++) {
229 System.out.print(hex(tile[i], 2));
230 }
231 System.out.println();
232 */
233 idx += (rowstride - (x1-x0));
234 }
235 nextTile();
236 }
237
238 static String hex(int v, int d) {
239 String s = Integer.toHexString(v);
240 while (s.length() < d) {
241 s = "0"+s;
242 }
243 return s.substring(0, d);
244 }
245
246 /**
247 * Disposes this tile generator.
248 * No further calls will be made on this instance.
249 */
250 public void dispose() {}
251}
252
java/pkg/sun/java2d/pisces/Renderer.java+571−0
@@ -0,0 +1,571 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import sun.awt.geom.PathConsumer2D;
29
30final class Renderer implements PathConsumer2D {
31
32 private class ScanlineIterator {
33
34 private int[] crossings;
35
36 // crossing bounds. The bounds are not necessarily tight (the scan line
37 // at minY, for example, might have no crossings). The x bounds will
38 // be accumulated as crossings are computed.
39 private final int maxY;
40 private int nextY;
41
42 // indices into the segment pointer lists. They indicate the "active"
43 // sublist in the segment lists (the portion of the list that contains
44 // all the segments that cross the next scan line).
45 private int edgeCount;
46 private int[] edgePtrs;
47
48 private static final int INIT_CROSSINGS_SIZE = 10;
49
50 // Preconditions: Only subpixel scanlines in the range
51 // (start <= subpixel_y <= end) will be evaluated. No
52 // edge may have a valid (i.e. inside the supplied clip)
53 // crossing that would be generated outside that range.
54 private ScanlineIterator(int start, int end) {
55 crossings = new int[INIT_CROSSINGS_SIZE];
56 edgePtrs = new int[INIT_CROSSINGS_SIZE];
57
58 nextY = start;
59 maxY = end;
60 edgeCount = 0;
61 }
62
63 private int next() {
64 int cury = nextY++;
65 int bucket = cury - boundsMinY;
66 int count = this.edgeCount;
67 int ptrs[] = this.edgePtrs;
68 int bucketcount = edgeBucketCounts[bucket];
69 if ((bucketcount & 0x1) != 0) {
70 int newCount = 0;
71 for (int i = 0; i < count; i++) {
72 int ecur = ptrs[i];
73 if (edges[ecur+YMAX] > cury) {
74 ptrs[newCount++] = ecur;
75 }
76 }
77 count = newCount;
78 }
79 ptrs = Helpers.widenArray(ptrs, count, bucketcount >> 1);
80 for (int ecur = edgeBuckets[bucket]; ecur != NULL; ecur = (int)edges[ecur+NEXT]) {
81 ptrs[count++] = ecur;
82 // REMIND: Adjust start Y if necessary
83 }
84 this.edgePtrs = ptrs;
85 this.edgeCount = count;
86// if ((count & 0x1) != 0) {
87// System.out.println("ODD NUMBER OF EDGES!!!!");
88// }
89 int xings[] = this.crossings;
90 if (xings.length < count) {
91 this.crossings = xings = new int[ptrs.length];
92 }
93 for (int i = 0; i < count; i++) {
94 int ecur = ptrs[i];
95 float curx = edges[ecur+CURX];
96 int cross = ((int) curx) << 1;
97 edges[ecur+CURX] = curx + edges[ecur+SLOPE];
98 if (edges[ecur+OR] > 0) {
99 cross |= 1;
100 }
101 int j = i;
102 while (--j >= 0) {
103 int jcross = xings[j];
104 if (jcross <= cross) {
105 break;
106 }
107 xings[j+1] = jcross;
108 ptrs[j+1] = ptrs[j];
109 }
110 xings[j+1] = cross;
111 ptrs[j+1] = ecur;
112 }
113 return count;
114 }
115
116 private boolean hasNext() {
117 return nextY < maxY;
118 }
119
120 private int curY() {
121 return nextY - 1;
122 }
123 }
124
125
126//////////////////////////////////////////////////////////////////////////////
127// EDGE LIST
128//////////////////////////////////////////////////////////////////////////////
129// TODO(maybe): very tempting to use fixed point here. A lot of opportunities
130// for shifts and just removing certain operations altogether.
131
132 // common to all types of input path segments.
133 private static final int YMAX = 0;
134 private static final int CURX = 1;
135 // NEXT and OR are meant to be indices into "int" fields, but arrays must
136 // be homogenous, so every field is a float. However floats can represent
137 // exactly up to 26 bit ints, so we're ok.
138 private static final int OR = 2;
139 private static final int SLOPE = 3;
140 private static final int NEXT = 4;
141
142 private float edgeMinY = Float.POSITIVE_INFINITY;
143 private float edgeMaxY = Float.NEGATIVE_INFINITY;
144 private float edgeMinX = Float.POSITIVE_INFINITY;
145 private float edgeMaxX = Float.NEGATIVE_INFINITY;
146
147 private static final int SIZEOF_EDGE = 5;
148 // don't just set NULL to -1, because we want NULL+NEXT to be negative.
149 private static final int NULL = -SIZEOF_EDGE;
150 private float[] edges = null;
151 private static final int INIT_NUM_EDGES = 8;
152 private int[] edgeBuckets = null;
153 private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed)
154 private int numEdges;
155
156 private static final float DEC_BND = 20f;
157 private static final float INC_BND = 8f;
158
159 // each bucket is a linked list. this method adds eptr to the
160 // start of the "bucket"th linked list.
161 private void addEdgeToBucket(final int eptr, final int bucket) {
162 edges[eptr+NEXT] = edgeBuckets[bucket];
163 edgeBuckets[bucket] = eptr;
164 edgeBucketCounts[bucket] += 2;
165 }
166
167 // Flattens using adaptive forward differencing. This only carries out
168 // one iteration of the AFD loop. All it does is update AFD variables (i.e.
169 // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
170 private void quadBreakIntoLinesAndAdd(float x0, float y0,
171 final Curve c,
172 final float x2, final float y2)
173 {
174 final float QUAD_DEC_BND = 32;
175 final int countlg = 4;
176 int count = 1 << countlg;
177 int countsq = count * count;
178 float maxDD = Math.max(c.dbx / countsq, c.dby / countsq);
179 while (maxDD > QUAD_DEC_BND) {
180 maxDD /= 4;
181 count <<= 1;
182 }
183
184 countsq = count * count;
185 final float ddx = c.dbx / countsq;
186 final float ddy = c.dby / countsq;
187 float dx = c.bx / countsq + c.cx / count;
188 float dy = c.by / countsq + c.cy / count;
189
190 while (count-- > 1) {
191 float x1 = x0 + dx;
192 dx += ddx;
193 float y1 = y0 + dy;
194 dy += ddy;
195 addLine(x0, y0, x1, y1);
196 x0 = x1;
197 y0 = y1;
198 }
199 addLine(x0, y0, x2, y2);
200 }
201
202 // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these
203 // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce
204 // numerical errors, and our callers already have the exact values.
205 // Another alternative would be to pass all the control points, and call c.set
206 // here, but then too many numbers are passed around.
207 private void curveBreakIntoLinesAndAdd(float x0, float y0,
208 final Curve c,
209 final float x3, final float y3)
210 {
211 final int countlg = 3;
212 int count = 1 << countlg;
213
214 // the dx and dy refer to forward differencing variables, not the last
215 // coefficients of the "points" polynomial
216 float dddx, dddy, ddx, ddy, dx, dy;
217 dddx = 2f * c.dax / (1 << (3 * countlg));
218 dddy = 2f * c.day / (1 << (3 * countlg));
219
220 ddx = dddx + c.dbx / (1 << (2 * countlg));
221 ddy = dddy + c.dby / (1 << (2 * countlg));
222 dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
223 dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
224
225 // we use x0, y0 to walk the line
226 float x1 = x0, y1 = y0;
227 while (count > 0) {
228 while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
229 dddx /= 8;
230 dddy /= 8;
231 ddx = ddx/4 - dddx;
232 ddy = ddy/4 - dddy;
233 dx = (dx - ddx) / 2;
234 dy = (dy - ddy) / 2;
235 count <<= 1;
236 }
237 // can only do this on even "count" values, because we must divide count by 2
238 while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
239 dx = 2 * dx + ddx;
240 dy = 2 * dy + ddy;
241 ddx = 4 * (ddx + dddx);
242 ddy = 4 * (ddy + dddy);
243 dddx = 8 * dddx;
244 dddy = 8 * dddy;
245 count >>= 1;
246 }
247 count--;
248 if (count > 0) {
249 x1 += dx;
250 dx += ddx;
251 ddx += dddx;
252 y1 += dy;
253 dy += ddy;
254 ddy += dddy;
255 } else {
256 x1 = x3;
257 y1 = y3;
258 }
259 addLine(x0, y0, x1, y1);
260 x0 = x1;
261 y0 = y1;
262 }
263 }
264
265 private void addLine(float x1, float y1, float x2, float y2) {
266 float or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
267 if (y2 < y1) {
268 or = y2; // no need to declare a temp variable. We have or.
269 y2 = y1;
270 y1 = or;
271 or = x2;
272 x2 = x1;
273 x1 = or;
274 or = 0;
275 }
276 final int firstCrossing = Math.max((int)Math.ceil(y1), boundsMinY);
277 final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY);
278 if (firstCrossing >= lastCrossing) {
279 return;
280 }
281 if (y1 < edgeMinY) { edgeMinY = y1; }
282 if (y2 > edgeMaxY) { edgeMaxY = y2; }
283
284 final float slope = (x2 - x1) / (y2 - y1);
285
286 if (slope > 0) { // <==> x1 < x2
287 if (x1 < edgeMinX) { edgeMinX = x1; }
288 if (x2 > edgeMaxX) { edgeMaxX = x2; }
289 } else {
290 if (x2 < edgeMinX) { edgeMinX = x2; }
291 if (x1 > edgeMaxX) { edgeMaxX = x1; }
292 }
293
294 final int ptr = numEdges * SIZEOF_EDGE;
295 edges = Helpers.widenArray(edges, ptr, SIZEOF_EDGE);
296 numEdges++;
297 edges[ptr+OR] = or;
298 edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope;
299 edges[ptr+SLOPE] = slope;
300 edges[ptr+YMAX] = lastCrossing;
301 final int bucketIdx = firstCrossing - boundsMinY;
302 addEdgeToBucket(ptr, bucketIdx);
303 edgeBucketCounts[lastCrossing - boundsMinY] |= 1;
304 }
305
306// END EDGE LIST
307//////////////////////////////////////////////////////////////////////////////
308
309
310 public static final int WIND_EVEN_ODD = 0;
311 public static final int WIND_NON_ZERO = 1;
312
313 // Antialiasing
314 final private int SUBPIXEL_LG_POSITIONS_X;
315 final private int SUBPIXEL_LG_POSITIONS_Y;
316 final private int SUBPIXEL_POSITIONS_X;
317 final private int SUBPIXEL_POSITIONS_Y;
318 final private int SUBPIXEL_MASK_X;
319 final private int SUBPIXEL_MASK_Y;
320 final int MAX_AA_ALPHA;
321
322 // Cache to store RLE-encoded coverage mask of the current primitive
323 PiscesCache cache;
324
325 // Bounds of the drawing region, at subpixel precision.
326 private final int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
327
328 // Current winding rule
329 private final int windingRule;
330
331 // Current drawing position, i.e., final point of last segment
332 private float x0, y0;
333
334 // Position of most recent 'moveTo' command
335 private float pix_sx0, pix_sy0;
336
337 public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY,
338 int pix_boundsX, int pix_boundsY,
339 int pix_boundsWidth, int pix_boundsHeight,
340 int windingRule)
341 {
342 this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX;
343 this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY;
344 this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;
345 this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;
346 this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
347 this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
348 this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);
349
350 this.windingRule = windingRule;
351
352 this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X;
353 this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y;
354 this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X;
355 this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y;
356
357 edges = new float[INIT_NUM_EDGES * SIZEOF_EDGE];
358 numEdges = 0;
359 edgeBuckets = new int[boundsMaxY - boundsMinY];
360 java.util.Arrays.fill(edgeBuckets, NULL);
361 edgeBucketCounts = new int[edgeBuckets.length + 1];
362 }
363
364 private float tosubpixx(float pix_x) {
365 return pix_x * SUBPIXEL_POSITIONS_X;
366 }
367 private float tosubpixy(float pix_y) {
368 return pix_y * SUBPIXEL_POSITIONS_Y;
369 }
370
371 public void moveTo(float pix_x0, float pix_y0) {
372 closePath();
373 this.pix_sx0 = pix_x0;
374 this.pix_sy0 = pix_y0;
375 this.y0 = tosubpixy(pix_y0);
376 this.x0 = tosubpixx(pix_x0);
377 }
378
379 public void lineTo(float pix_x1, float pix_y1) {
380 float x1 = tosubpixx(pix_x1);
381 float y1 = tosubpixy(pix_y1);
382 addLine(x0, y0, x1, y1);
383 x0 = x1;
384 y0 = y1;
385 }
386
387 private Curve c = new Curve();
388 @Override public void curveTo(float x1, float y1,
389 float x2, float y2,
390 float x3, float y3)
391 {
392 final float xe = tosubpixx(x3);
393 final float ye = tosubpixy(y3);
394 c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), tosubpixx(x2), tosubpixy(y2), xe, ye);
395 curveBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
396 x0 = xe;
397 y0 = ye;
398 }
399
400 @Override public void quadTo(float x1, float y1, float x2, float y2) {
401 final float xe = tosubpixx(x2);
402 final float ye = tosubpixy(y2);
403 c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
404 quadBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
405 x0 = xe;
406 y0 = ye;
407 }
408
409 public void closePath() {
410 // lineTo expects its input in pixel coordinates.
411 lineTo(pix_sx0, pix_sy0);
412 }
413
414 public void pathDone() {
415 closePath();
416 }
417
418
419 @Override
420 public long getNativeConsumer() {
421 throw new InternalError("Renderer does not use a native consumer.");
422 }
423
424 private void _endRendering(final int pix_bboxx0, final int pix_bboxx1,
425 int ymin, int ymax)
426 {
427 // Mask to determine the relevant bit of the crossing sum
428 // 0x1 if EVEN_ODD, all bits if NON_ZERO
429 int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
430
431 // add 2 to better deal with the last pixel in a pixel row.
432 int width = pix_bboxx1 - pix_bboxx0;
433 int[] alpha = new int[width+2];
434
435 int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X;
436 int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X;
437
438 // Now we iterate through the scanlines. We must tell emitRow the coord
439 // of the first non-transparent pixel, so we must keep accumulators for
440 // the first and last pixels of the section of the current pixel row
441 // that we will emit.
442 // We also need to accumulate pix_bbox*, but the iterator does it
443 // for us. We will just get the values from it once this loop is done
444 int pix_maxX = Integer.MIN_VALUE;
445 int pix_minX = Integer.MAX_VALUE;
446
447 int y = boundsMinY; // needs to be declared here so we emit the last row properly.
448 ScanlineIterator it = this.new ScanlineIterator(ymin, ymax);
449 for ( ; it.hasNext(); ) {
450 int numCrossings = it.next();
451 int[] crossings = it.crossings;
452 y = it.curY();
453
454 if (numCrossings > 0) {
455 int lowx = crossings[0] >> 1;
456 int highx = crossings[numCrossings - 1] >> 1;
457 int x0 = Math.max(lowx, bboxx0);
458 int x1 = Math.min(highx, bboxx1);
459
460 pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
461 pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
462 }
463
464 int sum = 0;
465 int prev = bboxx0;
466 for (int i = 0; i < numCrossings; i++) {
467 int curxo = crossings[i];
468 int curx = curxo >> 1;
469 // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
470 int crorientation = ((curxo & 0x1) << 1) - 1;
471 if ((sum & mask) != 0) {
472 int x0 = Math.max(prev, bboxx0);
473 int x1 = Math.min(curx, bboxx1);
474 if (x0 < x1) {
475 x0 -= bboxx0; // turn x0, x1 from coords to indeces
476 x1 -= bboxx0; // in the alpha array.
477
478 int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X;
479 int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
480
481 if (pix_x == pix_xmaxm1) {
482 // Start and end in same pixel
483 alpha[pix_x] += (x1 - x0);
484 alpha[pix_x+1] -= (x1 - x0);
485 } else {
486 int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
487 alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X);
488 alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X);
489 alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X);
490 alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X);
491 }
492 }
493 }
494 sum += crorientation;
495 prev = curx;
496 }
497
498 // even if this last row had no crossings, alpha will be zeroed
499 // from the last emitRow call. But this doesn't matter because
500 // maxX < minX, so no row will be emitted to the cache.
501 if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) {
502 emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
503 pix_minX = Integer.MAX_VALUE;
504 pix_maxX = Integer.MIN_VALUE;
505 }
506 }
507
508 // Emit final row
509 if (pix_maxX >= pix_minX) {
510 emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
511 }
512 }
513
514 public void endRendering() {
515 int spminX = Math.max((int)Math.ceil(edgeMinX), boundsMinX);
516 int spmaxX = Math.min((int)Math.ceil(edgeMaxX), boundsMaxX);
517 int spminY = Math.max((int)Math.ceil(edgeMinY), boundsMinY);
518 int spmaxY = Math.min((int)Math.ceil(edgeMaxY), boundsMaxY);
519
520 int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
521 int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
522 int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
523 int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
524
525 if (pminX > pmaxX || pminY > pmaxY) {
526 this.cache = new PiscesCache(boundsMinX >> SUBPIXEL_LG_POSITIONS_X,
527 boundsMinY >> SUBPIXEL_LG_POSITIONS_Y,
528 boundsMaxX >> SUBPIXEL_LG_POSITIONS_X,
529 boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y);
530 return;
531 }
532
533 this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY);
534 _endRendering(pminX, pmaxX, spminY, spmaxY);
535 }
536
537 public PiscesCache getCache() {
538 if (cache == null) {
539 throw new InternalError("cache not yet initialized");
540 }
541 return cache;
542 }
543
544 private void emitRow(int[] alphaRow, int pix_y, int pix_from, int pix_to) {
545 // Copy rowAA data into the cache if one is present
546 if (cache != null) {
547 if (pix_to >= pix_from) {
548 cache.startRow(pix_y, pix_from);
549
550 // Perform run-length encoding and store results in the cache
551 int from = pix_from - cache.bboxX0;
552 int to = pix_to - cache.bboxX0;
553
554 int runLen = 1;
555 int startVal = alphaRow[from];
556 for (int i = from + 1; i <= to; i++) {
557 int nextVal = startVal + alphaRow[i];
558 if (nextVal == startVal) {
559 runLen++;
560 } else {
561 cache.addRLERun(startVal, runLen);
562 runLen = 1;
563 startVal = nextVal;
564 }
565 }
566 cache.addRLERun(startVal, runLen);
567 }
568 }
569 java.util.Arrays.fill(alphaRow, 0);
570 }
571}
java/pkg/sun/java2d/pisces/Stroker.java+1223−0
@@ -0,0 +1,1223 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import java.util.Arrays;
29import java.util.Iterator;
30import static java.lang.Math.ulp;
31import static java.lang.Math.sqrt;
32
33import sun.awt.geom.PathConsumer2D;
34
35// TODO: some of the arithmetic here is too verbose and prone to hard to
36// debug typos. We should consider making a small Point/Vector class that
37// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
38final class Stroker implements PathConsumer2D {
39
40 private static final int MOVE_TO = 0;
41 private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
42 private static final int CLOSE = 2;
43
44 /**
45 * Constant value for join style.
46 */
47 public static final int JOIN_MITER = 0;
48
49 /**
50 * Constant value for join style.
51 */
52 public static final int JOIN_ROUND = 1;
53
54 /**
55 * Constant value for join style.
56 */
57 public static final int JOIN_BEVEL = 2;
58
59 /**
60 * Constant value for end cap style.
61 */
62 public static final int CAP_BUTT = 0;
63
64 /**
65 * Constant value for end cap style.
66 */
67 public static final int CAP_ROUND = 1;
68
69 /**
70 * Constant value for end cap style.
71 */
72 public static final int CAP_SQUARE = 2;
73
74 private final PathConsumer2D out;
75
76 private final int capStyle;
77 private final int joinStyle;
78
79 private final float lineWidth2;
80
81 private final float[][] offset = new float[3][2];
82 private final float[] miter = new float[2];
83 private final float miterLimitSq;
84
85 private int prev;
86
87 // The starting point of the path, and the slope there.
88 private float sx0, sy0, sdx, sdy;
89 // the current point and the slope there.
90 private float cx0, cy0, cdx, cdy; // c stands for current
91 // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
92 // first and last points on the left parallel path. Since this path is
93 // parallel, it's slope at any point is parallel to the slope of the
94 // original path (thought they may have different directions), so these
95 // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
96 // would be error prone and hard to read, so we keep these anyway.
97 private float smx, smy, cmx, cmy;
98
99 private final PolyStack reverse = new PolyStack();
100
101 /**
102 * Constructs a <code>Stroker</code>.
103 *
104 * @param pc2d an output <code>PathConsumer2D</code>.
105 * @param lineWidth the desired line width in pixels
106 * @param capStyle the desired end cap style, one of
107 * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
108 * <code>CAP_SQUARE</code>.
109 * @param joinStyle the desired line join style, one of
110 * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
111 * <code>JOIN_BEVEL</code>.
112 * @param miterLimit the desired miter limit
113 */
114 public Stroker(PathConsumer2D pc2d,
115 float lineWidth,
116 int capStyle,
117 int joinStyle,
118 float miterLimit)
119 {
120 this.out = pc2d;
121
122 this.lineWidth2 = lineWidth / 2;
123 this.capStyle = capStyle;
124 this.joinStyle = joinStyle;
125
126 float limit = miterLimit * lineWidth2;
127 this.miterLimitSq = limit*limit;
128
129 this.prev = CLOSE;
130 }
131
132 private static void computeOffset(final float lx, final float ly,
133 final float w, final float[] m)
134 {
135 final float len = (float) sqrt(lx*lx + ly*ly);
136 if (len == 0) {
137 m[0] = m[1] = 0;
138 } else {
139 m[0] = (ly * w)/len;
140 m[1] = -(lx * w)/len;
141 }
142 }
143
144 // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are
145 // clockwise (if dx1,dy1 needs to be rotated clockwise to close
146 // the smallest angle between it and dx2,dy2).
147 // This is equivalent to detecting whether a point q is on the right side
148 // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and
149 // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
150 // clockwise order.
151 // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
152 private static boolean isCW(final float dx1, final float dy1,
153 final float dx2, final float dy2)
154 {
155 return dx1 * dy2 <= dy1 * dx2;
156 }
157
158 // pisces used to use fixed point arithmetic with 16 decimal digits. I
159 // didn't want to change the values of the constant below when I converted
160 // it to floating point, so that's why the divisions by 2^16 are there.
161 private static final float ROUND_JOIN_THRESHOLD = 1000/65536f;
162
163 private void drawRoundJoin(float x, float y,
164 float omx, float omy, float mx, float my,
165 boolean rev,
166 float threshold)
167 {
168 if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) {
169 return;
170 }
171
172 float domx = omx - mx;
173 float domy = omy - my;
174 float len = domx*domx + domy*domy;
175 if (len < threshold) {
176 return;
177 }
178
179 if (rev) {
180 omx = -omx;
181 omy = -omy;
182 mx = -mx;
183 my = -my;
184 }
185 drawRoundJoin(x, y, omx, omy, mx, my, rev);
186 }
187
188 private void drawRoundJoin(float cx, float cy,
189 float omx, float omy,
190 float mx, float my,
191 boolean rev)
192 {
193 // The sign of the dot product of mx,my and omx,omy is equal to the
194 // the sign of the cosine of ext
195 // (ext is the angle between omx,omy and mx,my).
196 double cosext = omx * mx + omy * my;
197 // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
198 // need 1 curve to approximate the circle section that joins omx,omy
199 // and mx,my.
200 final int numCurves = cosext >= 0 ? 1 : 2;
201
202 switch (numCurves) {
203 case 1:
204 drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
205 break;
206 case 2:
207 // we need to split the arc into 2 arcs spanning the same angle.
208 // The point we want will be one of the 2 intersections of the
209 // perpendicular bisector of the chord (omx,omy)->(mx,my) and the
210 // circle. We could find this by scaling the vector
211 // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies
212 // on the circle), but that can have numerical problems when the angle
213 // between omx,omy and mx,my is close to 180 degrees. So we compute a
214 // normal of (omx,omy)-(mx,my). This will be the direction of the
215 // perpendicular bisector. To get one of the intersections, we just scale
216 // this vector that its length is lineWidth2 (this works because the
217 // perpendicular bisector goes through the origin). This scaling doesn't
218 // have numerical problems because we know that lineWidth2 divided by
219 // this normal's length is at least 0.5 and at most sqrt(2)/2 (because
220 // we know the angle of the arc is > 90 degrees).
221 float nx = my - omy, ny = omx - mx;
222 float nlen = (float) sqrt(nx*nx + ny*ny);
223 float scale = lineWidth2/nlen;
224 float mmx = nx * scale, mmy = ny * scale;
225
226 // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
227 // computed the wrong intersection so we get the other one.
228 // The test above is equivalent to if (rev).
229 if (rev) {
230 mmx = -mmx;
231 mmy = -mmy;
232 }
233 drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
234 drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
235 break;
236 }
237 }
238
239 // the input arc defined by omx,omy and mx,my must span <= 90 degrees.
240 private void drawBezApproxForArc(final float cx, final float cy,
241 final float omx, final float omy,
242 final float mx, final float my,
243 boolean rev)
244 {
245 float cosext2 = (omx * mx + omy * my) / (2 * lineWidth2 * lineWidth2);
246 // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc
247 // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that
248 // define the bezier curve we're computing.
249 // It is computed using the constraints that P1-P0 and P3-P2 are parallel
250 // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
251 float cv = (float) ((4.0 / 3.0) * sqrt(0.5-cosext2) /
252 (1.0 + sqrt(cosext2+0.5)));
253 // if clockwise, we need to negate cv.
254 if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
255 cv = -cv;
256 }
257 final float x1 = cx + omx;
258 final float y1 = cy + omy;
259 final float x2 = x1 - cv * omy;
260 final float y2 = y1 + cv * omx;
261
262 final float x4 = cx + mx;
263 final float y4 = cy + my;
264 final float x3 = x4 + cv * my;
265 final float y3 = y4 - cv * mx;
266
267 emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
268 }
269
270 private void drawRoundCap(float cx, float cy, float mx, float my) {
271 final float C = 0.5522847498307933f;
272 // the first and second arguments of the following two calls
273 // are really will be ignored by emitCurveTo (because of the false),
274 // but we put them in anyway, as opposed to just giving it 4 zeroes,
275 // because it's just 4 additions and it's not good to rely on this
276 // sort of assumption (right now it's true, but that may change).
277 emitCurveTo(cx+mx, cy+my,
278 cx+mx-C*my, cy+my+C*mx,
279 cx-my+C*mx, cy+mx+C*my,
280 cx-my, cy+mx,
281 false);
282 emitCurveTo(cx-my, cy+mx,
283 cx-my-C*mx, cy+mx-C*my,
284 cx-mx-C*my, cy-my+C*mx,
285 cx-mx, cy-my,
286 false);
287 }
288
289 // Put the intersection point of the lines (x0, y0) -> (x1, y1)
290 // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1].
291 // If the lines are parallel, it will put a non finite number in m.
292 private void computeIntersection(final float x0, final float y0,
293 final float x1, final float y1,
294 final float x0p, final float y0p,
295 final float x1p, final float y1p,
296 final float[] m, int off)
297 {
298 float x10 = x1 - x0;
299 float y10 = y1 - y0;
300 float x10p = x1p - x0p;
301 float y10p = y1p - y0p;
302
303 float den = x10*y10p - x10p*y10;
304 float t = x10p*(y0-y0p) - y10p*(x0-x0p);
305 t /= den;
306 m[off++] = x0 + t*x10;
307 m[off] = y0 + t*y10;
308 }
309
310 private void drawMiter(final float pdx, final float pdy,
311 final float x0, final float y0,
312 final float dx, final float dy,
313 float omx, float omy, float mx, float my,
314 boolean rev)
315 {
316 if ((mx == omx && my == omy) ||
317 (pdx == 0 && pdy == 0) ||
318 (dx == 0 && dy == 0))
319 {
320 return;
321 }
322
323 if (rev) {
324 omx = -omx;
325 omy = -omy;
326 mx = -mx;
327 my = -my;
328 }
329
330 computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
331 (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
332 miter, 0);
333
334 float lenSq = (miter[0]-x0)*(miter[0]-x0) + (miter[1]-y0)*(miter[1]-y0);
335
336 // If the lines are parallel, lenSq will be either NaN or +inf
337 // (actually, I'm not sure if the latter is possible. The important
338 // thing is that -inf is not possible, because lenSq is a square).
339 // For both of those values, the comparison below will fail and
340 // no miter will be drawn, which is correct.
341 if (lenSq < miterLimitSq) {
342 emitLineTo(miter[0], miter[1], rev);
343 }
344 }
345
346 public void moveTo(float x0, float y0) {
347 if (prev == DRAWING_OP_TO) {
348 finish();
349 }
350 this.sx0 = this.cx0 = x0;
351 this.sy0 = this.cy0 = y0;
352 this.cdx = this.sdx = 1;
353 this.cdy = this.sdy = 0;
354 this.prev = MOVE_TO;
355 }
356
357 public void lineTo(float x1, float y1) {
358 float dx = x1 - cx0;
359 float dy = y1 - cy0;
360 if (dx == 0f && dy == 0f) {
361 dx = 1;
362 }
363 computeOffset(dx, dy, lineWidth2, offset[0]);
364 float mx = offset[0][0];
365 float my = offset[0][1];
366
367 drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
368
369 emitLineTo(cx0 + mx, cy0 + my);
370 emitLineTo(x1 + mx, y1 + my);
371
372 emitLineTo(cx0 - mx, cy0 - my, true);
373 emitLineTo(x1 - mx, y1 - my, true);
374
375 this.cmx = mx;
376 this.cmy = my;
377 this.cdx = dx;
378 this.cdy = dy;
379 this.cx0 = x1;
380 this.cy0 = y1;
381 this.prev = DRAWING_OP_TO;
382 }
383
384 public void closePath() {
385 if (prev != DRAWING_OP_TO) {
386 if (prev == CLOSE) {
387 return;
388 }
389 emitMoveTo(cx0, cy0 - lineWidth2);
390 this.cmx = this.smx = 0;
391 this.cmy = this.smy = -lineWidth2;
392 this.cdx = this.sdx = 1;
393 this.cdy = this.sdy = 0;
394 finish();
395 return;
396 }
397
398 if (cx0 != sx0 || cy0 != sy0) {
399 lineTo(sx0, sy0);
400 }
401
402 drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
403
404 emitLineTo(sx0 + smx, sy0 + smy);
405
406 emitMoveTo(sx0 - smx, sy0 - smy);
407 emitReverse();
408
409 this.prev = CLOSE;
410 emitClose();
411 }
412
413 private void emitReverse() {
414 while(!reverse.isEmpty()) {
415 reverse.pop(out);
416 }
417 }
418
419 public void pathDone() {
420 if (prev == DRAWING_OP_TO) {
421 finish();
422 }
423
424 out.pathDone();
425 // this shouldn't matter since this object won't be used
426 // after the call to this method.
427 this.prev = CLOSE;
428 }
429
430 private void finish() {
431 if (capStyle == CAP_ROUND) {
432 drawRoundCap(cx0, cy0, cmx, cmy);
433 } else if (capStyle == CAP_SQUARE) {
434 emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
435 emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
436 }
437
438 emitReverse();
439
440 if (capStyle == CAP_ROUND) {
441 drawRoundCap(sx0, sy0, -smx, -smy);
442 } else if (capStyle == CAP_SQUARE) {
443 emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
444 emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
445 }
446
447 emitClose();
448 }
449
450 private void emitMoveTo(final float x0, final float y0) {
451 out.moveTo(x0, y0);
452 }
453
454 private void emitLineTo(final float x1, final float y1) {
455 out.lineTo(x1, y1);
456 }
457
458 private void emitLineTo(final float x1, final float y1,
459 final boolean rev)
460 {
461 if (rev) {
462 reverse.pushLine(x1, y1);
463 } else {
464 emitLineTo(x1, y1);
465 }
466 }
467
468 private void emitQuadTo(final float x0, final float y0,
469 final float x1, final float y1,
470 final float x2, final float y2, final boolean rev)
471 {
472 if (rev) {
473 reverse.pushQuad(x0, y0, x1, y1);
474 } else {
475 out.quadTo(x1, y1, x2, y2);
476 }
477 }
478
479 private void emitCurveTo(final float x0, final float y0,
480 final float x1, final float y1,
481 final float x2, final float y2,
482 final float x3, final float y3, final boolean rev)
483 {
484 if (rev) {
485 reverse.pushCubic(x0, y0, x1, y1, x2, y2);
486 } else {
487 out.curveTo(x1, y1, x2, y2, x3, y3);
488 }
489 }
490
491 private void emitClose() {
492 out.closePath();
493 }
494
495 private void drawJoin(float pdx, float pdy,
496 float x0, float y0,
497 float dx, float dy,
498 float omx, float omy,
499 float mx, float my)
500 {
501 if (prev != DRAWING_OP_TO) {
502 emitMoveTo(x0 + mx, y0 + my);
503 this.sdx = dx;
504 this.sdy = dy;
505 this.smx = mx;
506 this.smy = my;
507 } else {
508 boolean cw = isCW(pdx, pdy, dx, dy);
509 if (joinStyle == JOIN_MITER) {
510 drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
511 } else if (joinStyle == JOIN_ROUND) {
512 drawRoundJoin(x0, y0,
513 omx, omy,
514 mx, my, cw,
515 ROUND_JOIN_THRESHOLD);
516 }
517 emitLineTo(x0, y0, !cw);
518 }
519 prev = DRAWING_OP_TO;
520 }
521
522 private static boolean within(final float x1, final float y1,
523 final float x2, final float y2,
524 final float ERR)
525 {
526 assert ERR > 0 : "";
527 // compare taxicab distance. ERR will always be small, so using
528 // true distance won't give much benefit
529 return (Helpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs
530 Helpers.within(y1, y2, ERR)); // this is just as good.
531 }
532
533 private void getLineOffsets(float x1, float y1,
534 float x2, float y2,
535 float[] left, float[] right) {
536 computeOffset(x2 - x1, y2 - y1, lineWidth2, offset[0]);
537 left[0] = x1 + offset[0][0];
538 left[1] = y1 + offset[0][1];
539 left[2] = x2 + offset[0][0];
540 left[3] = y2 + offset[0][1];
541 right[0] = x1 - offset[0][0];
542 right[1] = y1 - offset[0][1];
543 right[2] = x2 - offset[0][0];
544 right[3] = y2 - offset[0][1];
545 }
546
547 private int computeOffsetCubic(float[] pts, final int off,
548 float[] leftOff, float[] rightOff)
549 {
550 // if p1=p2 or p3=p4 it means that the derivative at the endpoint
551 // vanishes, which creates problems with computeOffset. Usually
552 // this happens when this stroker object is trying to winden
553 // a curve with a cusp. What happens is that curveTo splits
554 // the input curve at the cusp, and passes it to this function.
555 // because of inaccuracies in the splitting, we consider points
556 // equal if they're very close to each other.
557 final float x1 = pts[off + 0], y1 = pts[off + 1];
558 final float x2 = pts[off + 2], y2 = pts[off + 3];
559 final float x3 = pts[off + 4], y3 = pts[off + 5];
560 final float x4 = pts[off + 6], y4 = pts[off + 7];
561
562 float dx4 = x4 - x3;
563 float dy4 = y4 - y3;
564 float dx1 = x2 - x1;
565 float dy1 = y2 - y1;
566
567 // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
568 // in which case ignore if p1 == p2
569 final boolean p1eqp2 = within(x1,y1,x2,y2, 6 * ulp(y2));
570 final boolean p3eqp4 = within(x3,y3,x4,y4, 6 * ulp(y4));
571 if (p1eqp2 && p3eqp4) {
572 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
573 return 4;
574 } else if (p1eqp2) {
575 dx1 = x3 - x1;
576 dy1 = y3 - y1;
577 } else if (p3eqp4) {
578 dx4 = x4 - x2;
579 dy4 = y4 - y2;
580 }
581
582 // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
583 float dotsq = (dx1 * dx4 + dy1 * dy4);
584 dotsq = dotsq * dotsq;
585 float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
586 if (Helpers.within(dotsq, l1sq * l4sq, 4 * ulp(dotsq))) {
587 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
588 return 4;
589 }
590
591// What we're trying to do in this function is to approximate an ideal
592// offset curve (call it I) of the input curve B using a bezier curve Bp.
593// The constraints I use to get the equations are:
594//
595// 1. The computed curve Bp should go through I(0) and I(1). These are
596// x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find
597// 4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p).
598//
599// 2. Bp should have slope equal in absolute value to I at the endpoints. So,
600// (by the way, the operator || in the comments below means "aligned with".
601// It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that
602// vectors I'(0) and Bp'(0) are aligned, which is the same as saying
603// that the tangent lines of I and Bp at 0 are parallel. Mathematically
604// this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some
605// nonzero constant.)
606// I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and
607// I'(1) || B'(1); therefore, Bp'(0) || B'(0) and Bp'(1) || B'(1).
608// We know that Bp'(0) || (p2p-p1p) and Bp'(1) || (p4p-p3p) and the same
609// is true for any bezier curve; therefore, we get the equations
610// (1) p2p = c1 * (p2-p1) + p1p
611// (2) p3p = c2 * (p4-p3) + p4p
612// We know p1p, p4p, p2, p1, p3, and p4; therefore, this reduces the number
613// of unknowns from 4 to 2 (i.e. just c1 and c2).
614// To eliminate these 2 unknowns we use the following constraint:
615//
616// 3. Bp(0.5) == I(0.5). Bp(0.5)=(x,y) and I(0.5)=(xi,yi), and I should note
617// that I(0.5) is *the only* reason for computing dxm,dym. This gives us
618// (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to
619// (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3
620// We can substitute (1) and (2) from above into (4) and we get:
621// (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p
622// which is equivalent to
623// (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p)
624//
625// The right side of this is a 2D vector, and we know I(0.5), which gives us
626// Bp(0.5), which gives us the value of the right side.
627// The left side is just a matrix vector multiplication in disguise. It is
628//
629// [x2-x1, x4-x3][c1]
630// [y2-y1, y4-y3][c2]
631// which, is equal to
632// [dx1, dx4][c1]
633// [dy1, dy4][c2]
634// At this point we are left with a simple linear system and we solve it by
635// getting the inverse of the matrix above. Then we use [c1,c2] to compute
636// p2p and p3p.
637
638 float x = 0.125f * (x1 + 3 * (x2 + x3) + x4);
639 float y = 0.125f * (y1 + 3 * (y2 + y3) + y4);
640 // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
641 // c*B'(0.5) for some constant c.
642 float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
643
644 // this computes the offsets at t=0, 0.5, 1, using the property that
645 // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
646 // the (dx/dt, dy/dt) vectors at the endpoints.
647 computeOffset(dx1, dy1, lineWidth2, offset[0]);
648 computeOffset(dxm, dym, lineWidth2, offset[1]);
649 computeOffset(dx4, dy4, lineWidth2, offset[2]);
650 float x1p = x1 + offset[0][0]; // start
651 float y1p = y1 + offset[0][1]; // point
652 float xi = x + offset[1][0]; // interpolation
653 float yi = y + offset[1][1]; // point
654 float x4p = x4 + offset[2][0]; // end
655 float y4p = y4 + offset[2][1]; // point
656
657 float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4));
658
659 float two_pi_m_p1_m_p4x = 2*xi - x1p - x4p;
660 float two_pi_m_p1_m_p4y = 2*yi - y1p - y4p;
661 float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
662 float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
663
664 float x2p, y2p, x3p, y3p;
665 x2p = x1p + c1*dx1;
666 y2p = y1p + c1*dy1;
667 x3p = x4p + c2*dx4;
668 y3p = y4p + c2*dy4;
669
670 leftOff[0] = x1p; leftOff[1] = y1p;
671 leftOff[2] = x2p; leftOff[3] = y2p;
672 leftOff[4] = x3p; leftOff[5] = y3p;
673 leftOff[6] = x4p; leftOff[7] = y4p;
674
675 x1p = x1 - offset[0][0]; y1p = y1 - offset[0][1];
676 xi = xi - 2 * offset[1][0]; yi = yi - 2 * offset[1][1];
677 x4p = x4 - offset[2][0]; y4p = y4 - offset[2][1];
678
679 two_pi_m_p1_m_p4x = 2*xi - x1p - x4p;
680 two_pi_m_p1_m_p4y = 2*yi - y1p - y4p;
681 c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
682 c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
683
684 x2p = x1p + c1*dx1;
685 y2p = y1p + c1*dy1;
686 x3p = x4p + c2*dx4;
687 y3p = y4p + c2*dy4;
688
689 rightOff[0] = x1p; rightOff[1] = y1p;
690 rightOff[2] = x2p; rightOff[3] = y2p;
691 rightOff[4] = x3p; rightOff[5] = y3p;
692 rightOff[6] = x4p; rightOff[7] = y4p;
693 return 8;
694 }
695
696 // return the kind of curve in the right and left arrays.
697 private int computeOffsetQuad(float[] pts, final int off,
698 float[] leftOff, float[] rightOff)
699 {
700 final float x1 = pts[off + 0], y1 = pts[off + 1];
701 final float x2 = pts[off + 2], y2 = pts[off + 3];
702 final float x3 = pts[off + 4], y3 = pts[off + 5];
703
704 final float dx3 = x3 - x2;
705 final float dy3 = y3 - y2;
706 final float dx1 = x2 - x1;
707 final float dy1 = y2 - y1;
708
709 // this computes the offsets at t = 0, 1
710 computeOffset(dx1, dy1, lineWidth2, offset[0]);
711 computeOffset(dx3, dy3, lineWidth2, offset[1]);
712
713 leftOff[0] = x1 + offset[0][0]; leftOff[1] = y1 + offset[0][1];
714 leftOff[4] = x3 + offset[1][0]; leftOff[5] = y3 + offset[1][1];
715 rightOff[0] = x1 - offset[0][0]; rightOff[1] = y1 - offset[0][1];
716 rightOff[4] = x3 - offset[1][0]; rightOff[5] = y3 - offset[1][1];
717
718 float x1p = leftOff[0]; // start
719 float y1p = leftOff[1]; // point
720 float x3p = leftOff[4]; // end
721 float y3p = leftOff[5]; // point
722
723 // Corner cases:
724 // 1. If the two control vectors are parallel, we'll end up with NaN's
725 // in leftOff (and rightOff in the body of the if below), so we'll
726 // do getLineOffsets, which is right.
727 // 2. If the first or second two points are equal, then (dx1,dy1)==(0,0)
728 // or (dx3,dy3)==(0,0), so (x1p, y1p)==(x1p+dx1, y1p+dy1)
729 // or (x3p, y3p)==(x3p-dx3, y3p-dy3), which means that
730 // computeIntersection will put NaN's in leftOff and right off, and
731 // we will do getLineOffsets, which is right.
732 computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
733 float cx = leftOff[2];
734 float cy = leftOff[3];
735
736 if (!(isFinite(cx) && isFinite(cy))) {
737 // maybe the right path is not degenerate.
738 x1p = rightOff[0];
739 y1p = rightOff[1];
740 x3p = rightOff[4];
741 y3p = rightOff[5];
742 computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
743 cx = rightOff[2];
744 cy = rightOff[3];
745 if (!(isFinite(cx) && isFinite(cy))) {
746 // both are degenerate. This curve is a line.
747 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
748 return 4;
749 }
750 // {left,right}Off[0,1,4,5] are already set to the correct values.
751 leftOff[2] = 2*x2 - cx;
752 leftOff[3] = 2*y2 - cy;
753 return 6;
754 }
755
756 // rightOff[2,3] = (x2,y2) - ((left_x2, left_y2) - (x2, y2))
757 // == 2*(x2, y2) - (left_x2, left_y2)
758 rightOff[2] = 2*x2 - cx;
759 rightOff[3] = 2*y2 - cy;
760 return 6;
761 }
762
763 private static boolean isFinite(float x) {
764 return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY);
765 }
766
767 // This is where the curve to be processed is put. We give it
768 // enough room to store 2 curves: one for the current subdivision, the
769 // other for the rest of the curve.
770 private float[] middle = new float[2*8];
771 private float[] lp = new float[8];
772 private float[] rp = new float[8];
773 private static final int MAX_N_CURVES = 11;
774 private float[] subdivTs = new float[MAX_N_CURVES - 1];
775
776 // If this class is compiled with ecj, then Hotspot crashes when OSR
777 // compiling this function. See bugs 7004570 and 6675699
778 // TODO: until those are fixed, we should work around that by
779 // manually inlining this into curveTo and quadTo.
780/******************************* WORKAROUND **********************************
781 private void somethingTo(final int type) {
782 // need these so we can update the state at the end of this method
783 final float xf = middle[type-2], yf = middle[type-1];
784 float dxs = middle[2] - middle[0];
785 float dys = middle[3] - middle[1];
786 float dxf = middle[type - 2] - middle[type - 4];
787 float dyf = middle[type - 1] - middle[type - 3];
788 switch(type) {
789 case 6:
790 if ((dxs == 0f && dys == 0f) ||
791 (dxf == 0f && dyf == 0f)) {
792 dxs = dxf = middle[4] - middle[0];
793 dys = dyf = middle[5] - middle[1];
794 }
795 break;
796 case 8:
797 boolean p1eqp2 = (dxs == 0f && dys == 0f);
798 boolean p3eqp4 = (dxf == 0f && dyf == 0f);
799 if (p1eqp2) {
800 dxs = middle[4] - middle[0];
801 dys = middle[5] - middle[1];
802 if (dxs == 0f && dys == 0f) {
803 dxs = middle[6] - middle[0];
804 dys = middle[7] - middle[1];
805 }
806 }
807 if (p3eqp4) {
808 dxf = middle[6] - middle[2];
809 dyf = middle[7] - middle[3];
810 if (dxf == 0f && dyf == 0f) {
811 dxf = middle[6] - middle[0];
812 dyf = middle[7] - middle[1];
813 }
814 }
815 }
816 if (dxs == 0f && dys == 0f) {
817 // this happens iff the "curve" is just a point
818 lineTo(middle[0], middle[1]);
819 return;
820 }
821 // if these vectors are too small, normalize them, to avoid future
822 // precision problems.
823 if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
824 float len = (float) sqrt(dxs*dxs + dys*dys);
825 dxs /= len;
826 dys /= len;
827 }
828 if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
829 float len = (float) sqrt(dxf*dxf + dyf*dyf);
830 dxf /= len;
831 dyf /= len;
832 }
833
834 computeOffset(dxs, dys, lineWidth2, offset[0]);
835 final float mx = offset[0][0];
836 final float my = offset[0][1];
837 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
838
839 int nSplits = findSubdivPoints(middle, subdivTs, type, lineWidth2);
840
841 int kind = 0;
842 Iterator<Integer> it = Curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
843 while(it.hasNext()) {
844 int curCurveOff = it.next();
845
846 switch (type) {
847 case 8:
848 kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
849 break;
850 case 6:
851 kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
852 break;
853 }
854 emitLineTo(lp[0], lp[1]);
855 switch(kind) {
856 case 8:
857 emitCurveTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], lp[6], lp[7], false);
858 emitCurveTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7], true);
859 break;
860 case 6:
861 emitQuadTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], false);
862 emitQuadTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], true);
863 break;
864 case 4:
865 emitLineTo(lp[2], lp[3]);
866 emitLineTo(rp[0], rp[1], true);
867 break;
868 }
869 emitLineTo(rp[kind - 2], rp[kind - 1], true);
870 }
871
872 this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
873 this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
874 this.cdx = dxf;
875 this.cdy = dyf;
876 this.cx0 = xf;
877 this.cy0 = yf;
878 this.prev = DRAWING_OP_TO;
879 }
880****************************** END WORKAROUND *******************************/
881
882 // finds values of t where the curve in pts should be subdivided in order
883 // to get good offset curves a distance of w away from the middle curve.
884 // Stores the points in ts, and returns how many of them there were.
885 private static Curve c = new Curve();
886 private static int findSubdivPoints(float[] pts, float[] ts, final int type, final float w)
887 {
888 final float x12 = pts[2] - pts[0];
889 final float y12 = pts[3] - pts[1];
890 // if the curve is already parallel to either axis we gain nothing
891 // from rotating it.
892 if (y12 != 0f && x12 != 0f) {
893 // we rotate it so that the first vector in the control polygon is
894 // parallel to the x-axis. This will ensure that rotated quarter
895 // circles won't be subdivided.
896 final float hypot = (float) sqrt(x12 * x12 + y12 * y12);
897 final float cos = x12 / hypot;
898 final float sin = y12 / hypot;
899 final float x1 = cos * pts[0] + sin * pts[1];
900 final float y1 = cos * pts[1] - sin * pts[0];
901 final float x2 = cos * pts[2] + sin * pts[3];
902 final float y2 = cos * pts[3] - sin * pts[2];
903 final float x3 = cos * pts[4] + sin * pts[5];
904 final float y3 = cos * pts[5] - sin * pts[4];
905 switch(type) {
906 case 8:
907 final float x4 = cos * pts[6] + sin * pts[7];
908 final float y4 = cos * pts[7] - sin * pts[6];
909 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
910 break;
911 case 6:
912 c.set(x1, y1, x2, y2, x3, y3);
913 break;
914 }
915 } else {
916 c.set(pts, type);
917 }
918
919 int ret = 0;
920 // we subdivide at values of t such that the remaining rotated
921 // curves are monotonic in x and y.
922 ret += c.dxRoots(ts, ret);
923 ret += c.dyRoots(ts, ret);
924 // subdivide at inflection points.
925 if (type == 8) {
926 // quadratic curves can't have inflection points
927 ret += c.infPoints(ts, ret);
928 }
929
930 // now we must subdivide at points where one of the offset curves will have
931 // a cusp. This happens at ts where the radius of curvature is equal to w.
932 ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
933
934 ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
935 Helpers.isort(ts, 0, ret);
936 return ret;
937 }
938
939 @Override public void curveTo(float x1, float y1,
940 float x2, float y2,
941 float x3, float y3)
942 {
943 middle[0] = cx0; middle[1] = cy0;
944 middle[2] = x1; middle[3] = y1;
945 middle[4] = x2; middle[5] = y2;
946 middle[6] = x3; middle[7] = y3;
947
948 // inlined version of somethingTo(8);
949 // See the TODO on somethingTo
950
951 // need these so we can update the state at the end of this method
952 final float xf = middle[6], yf = middle[7];
953 float dxs = middle[2] - middle[0];
954 float dys = middle[3] - middle[1];
955 float dxf = middle[6] - middle[4];
956 float dyf = middle[7] - middle[5];
957
958 boolean p1eqp2 = (dxs == 0f && dys == 0f);
959 boolean p3eqp4 = (dxf == 0f && dyf == 0f);
960 if (p1eqp2) {
961 dxs = middle[4] - middle[0];
962 dys = middle[5] - middle[1];
963 if (dxs == 0f && dys == 0f) {
964 dxs = middle[6] - middle[0];
965 dys = middle[7] - middle[1];
966 }
967 }
968 if (p3eqp4) {
969 dxf = middle[6] - middle[2];
970 dyf = middle[7] - middle[3];
971 if (dxf == 0f && dyf == 0f) {
972 dxf = middle[6] - middle[0];
973 dyf = middle[7] - middle[1];
974 }
975 }
976 if (dxs == 0f && dys == 0f) {
977 // this happens iff the "curve" is just a point
978 lineTo(middle[0], middle[1]);
979 return;
980 }
981
982 // if these vectors are too small, normalize them, to avoid future
983 // precision problems.
984 if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
985 float len = (float) sqrt(dxs*dxs + dys*dys);
986 dxs /= len;
987 dys /= len;
988 }
989 if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
990 float len = (float) sqrt(dxf*dxf + dyf*dyf);
991 dxf /= len;
992 dyf /= len;
993 }
994
995 computeOffset(dxs, dys, lineWidth2, offset[0]);
996 final float mx = offset[0][0];
997 final float my = offset[0][1];
998 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
999
1000 int nSplits = findSubdivPoints(middle, subdivTs, 8, lineWidth2);
1001
1002 int kind = 0;
1003 Iterator<Integer> it = Curve.breakPtsAtTs(middle, 8, subdivTs, nSplits);
1004 while(it.hasNext()) {
1005 int curCurveOff = it.next();
1006
1007 kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
1008 emitLineTo(lp[0], lp[1]);
1009 switch(kind) {
1010 case 8:
1011 emitCurveTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], lp[6], lp[7], false);
1012 emitCurveTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7], true);
1013 break;
1014 case 4:
1015 emitLineTo(lp[2], lp[3]);
1016 emitLineTo(rp[0], rp[1], true);
1017 break;
1018 }
1019 emitLineTo(rp[kind - 2], rp[kind - 1], true);
1020 }
1021
1022 this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
1023 this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
1024 this.cdx = dxf;
1025 this.cdy = dyf;
1026 this.cx0 = xf;
1027 this.cy0 = yf;
1028 this.prev = DRAWING_OP_TO;
1029 }
1030
1031 @Override public void quadTo(float x1, float y1, float x2, float y2) {
1032 middle[0] = cx0; middle[1] = cy0;
1033 middle[2] = x1; middle[3] = y1;
1034 middle[4] = x2; middle[5] = y2;
1035
1036 // inlined version of somethingTo(8);
1037 // See the TODO on somethingTo
1038
1039 // need these so we can update the state at the end of this method
1040 final float xf = middle[4], yf = middle[5];
1041 float dxs = middle[2] - middle[0];
1042 float dys = middle[3] - middle[1];
1043 float dxf = middle[4] - middle[2];
1044 float dyf = middle[5] - middle[3];
1045 if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
1046 dxs = dxf = middle[4] - middle[0];
1047 dys = dyf = middle[5] - middle[1];
1048 }
1049 if (dxs == 0f && dys == 0f) {
1050 // this happens iff the "curve" is just a point
1051 lineTo(middle[0], middle[1]);
1052 return;
1053 }
1054 // if these vectors are too small, normalize them, to avoid future
1055 // precision problems.
1056 if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
1057 float len = (float) sqrt(dxs*dxs + dys*dys);
1058 dxs /= len;
1059 dys /= len;
1060 }
1061 if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
1062 float len = (float) sqrt(dxf*dxf + dyf*dyf);
1063 dxf /= len;
1064 dyf /= len;
1065 }
1066
1067 computeOffset(dxs, dys, lineWidth2, offset[0]);
1068 final float mx = offset[0][0];
1069 final float my = offset[0][1];
1070 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
1071
1072 int nSplits = findSubdivPoints(middle, subdivTs, 6, lineWidth2);
1073
1074 int kind = 0;
1075 Iterator<Integer> it = Curve.breakPtsAtTs(middle, 6, subdivTs, nSplits);
1076 while(it.hasNext()) {
1077 int curCurveOff = it.next();
1078
1079 kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
1080 emitLineTo(lp[0], lp[1]);
1081 switch(kind) {
1082 case 6:
1083 emitQuadTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], false);
1084 emitQuadTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], true);
1085 break;
1086 case 4:
1087 emitLineTo(lp[2], lp[3]);
1088 emitLineTo(rp[0], rp[1], true);
1089 break;
1090 }
1091 emitLineTo(rp[kind - 2], rp[kind - 1], true);
1092 }
1093
1094 this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
1095 this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
1096 this.cdx = dxf;
1097 this.cdy = dyf;
1098 this.cx0 = xf;
1099 this.cy0 = yf;
1100 this.prev = DRAWING_OP_TO;
1101 }
1102
1103 @Override public long getNativeConsumer() {
1104 throw new InternalError("Stroker doesn't use a native consumer");
1105 }
1106
1107 // a stack of polynomial curves where each curve shares endpoints with
1108 // adjacent ones.
1109 private static final class PolyStack {
1110 float[] curves;
1111 int end;
1112 int[] curveTypes;
1113 int numCurves;
1114
1115 private static final int INIT_SIZE = 50;
1116
1117 PolyStack() {
1118 curves = new float[8 * INIT_SIZE];
1119 curveTypes = new int[INIT_SIZE];
1120 end = 0;
1121 numCurves = 0;
1122 }
1123
1124 public boolean isEmpty() {
1125 return numCurves == 0;
1126 }
1127
1128 private void ensureSpace(int n) {
1129 if (end + n >= curves.length) {
1130 int newSize = (end + n) * 2;
1131 curves = Arrays.copyOf(curves, newSize);
1132 }
1133 if (numCurves >= curveTypes.length) {
1134 int newSize = numCurves * 2;
1135 curveTypes = Arrays.copyOf(curveTypes, newSize);
1136 }
1137 }
1138
1139 public void pushCubic(float x0, float y0,
1140 float x1, float y1,
1141 float x2, float y2)
1142 {
1143 ensureSpace(6);
1144 curveTypes[numCurves++] = 8;
1145 // assert(x0 == lastX && y0 == lastY)
1146
1147 // we reverse the coordinate order to make popping easier
1148 curves[end++] = x2; curves[end++] = y2;
1149 curves[end++] = x1; curves[end++] = y1;
1150 curves[end++] = x0; curves[end++] = y0;
1151 }
1152
1153 public void pushQuad(float x0, float y0,
1154 float x1, float y1)
1155 {
1156 ensureSpace(4);
1157 curveTypes[numCurves++] = 6;
1158 // assert(x0 == lastX && y0 == lastY)
1159 curves[end++] = x1; curves[end++] = y1;
1160 curves[end++] = x0; curves[end++] = y0;
1161 }
1162
1163 public void pushLine(float x, float y) {
1164 ensureSpace(2);
1165 curveTypes[numCurves++] = 4;
1166 // assert(x0 == lastX && y0 == lastY)
1167 curves[end++] = x; curves[end++] = y;
1168 }
1169
1170 @SuppressWarnings("unused")
1171 public int pop(float[] pts) {
1172 int ret = curveTypes[numCurves - 1];
1173 numCurves--;
1174 end -= (ret - 2);
1175 System.arraycopy(curves, end, pts, 0, ret - 2);
1176 return ret;
1177 }
1178
1179 public void pop(PathConsumer2D io) {
1180 numCurves--;
1181 int type = curveTypes[numCurves];
1182 end -= (type - 2);
1183 switch(type) {
1184 case 8:
1185 io.curveTo(curves[end+0], curves[end+1],
1186 curves[end+2], curves[end+3],
1187 curves[end+4], curves[end+5]);
1188 break;
1189 case 6:
1190 io.quadTo(curves[end+0], curves[end+1],
1191 curves[end+2], curves[end+3]);
1192 break;
1193 case 4:
1194 io.lineTo(curves[end], curves[end+1]);
1195 }
1196 }
1197
1198 @Override
1199 public String toString() {
1200 String ret = "";
1201 int nc = numCurves;
1202 int end = this.end;
1203 while (nc > 0) {
1204 nc--;
1205 int type = curveTypes[numCurves];
1206 end -= (type - 2);
1207 switch(type) {
1208 case 8:
1209 ret += "cubic: ";
1210 break;
1211 case 6:
1212 ret += "quad: ";
1213 break;
1214 case 4:
1215 ret += "line: ";
1216 break;
1217 }
1218 ret += Arrays.toString(Arrays.copyOfRange(curves, end, end+type-2)) + "\n";
1219 }
1220 return ret;
1221 }
1222 }
1223}
java/pkg/sun/java2d/pisces/TransformingPathConsumer2D.java+393−0
@@ -0,0 +1,393 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
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
26package sun.java2d.pisces;
27
28import sun.awt.geom.PathConsumer2D;
29import java.awt.geom.AffineTransform;
30
31final class TransformingPathConsumer2D {
32 public static PathConsumer2D
33 transformConsumer(PathConsumer2D out,
34 AffineTransform at)
35 {
36 if (at == null) {
37 return out;
38 }
39 float Mxx = (float) at.getScaleX();
40 float Mxy = (float) at.getShearX();
41 float Mxt = (float) at.getTranslateX();
42 float Myx = (float) at.getShearY();
43 float Myy = (float) at.getScaleY();
44 float Myt = (float) at.getTranslateY();
45 if (Mxy == 0f && Myx == 0f) {
46 if (Mxx == 1f && Myy == 1f) {
47 if (Mxt == 0f && Myt == 0f) {
48 return out;
49 } else {
50 return new TranslateFilter(out, Mxt, Myt);
51 }
52 } else {
53 if (Mxt == 0f && Myt == 0f) {
54 return new DeltaScaleFilter(out, Mxx, Myy);
55 } else {
56 return new ScaleFilter(out, Mxx, Myy, Mxt, Myt);
57 }
58 }
59 } else if (Mxt == 0f && Myt == 0f) {
60 return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy);
61 } else {
62 return new TransformFilter(out, Mxx, Mxy, Mxt, Myx, Myy, Myt);
63 }
64 }
65
66 public static PathConsumer2D
67 deltaTransformConsumer(PathConsumer2D out,
68 AffineTransform at)
69 {
70 if (at == null) {
71 return out;
72 }
73 float Mxx = (float) at.getScaleX();
74 float Mxy = (float) at.getShearX();
75 float Myx = (float) at.getShearY();
76 float Myy = (float) at.getScaleY();
77 if (Mxy == 0f && Myx == 0f) {
78 if (Mxx == 1f && Myy == 1f) {
79 return out;
80 } else {
81 return new DeltaScaleFilter(out, Mxx, Myy);
82 }
83 } else {
84 return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy);
85 }
86 }
87
88 public static PathConsumer2D
89 inverseDeltaTransformConsumer(PathConsumer2D out,
90 AffineTransform at)
91 {
92 if (at == null) {
93 return out;
94 }
95 float Mxx = (float) at.getScaleX();
96 float Mxy = (float) at.getShearX();
97 float Myx = (float) at.getShearY();
98 float Myy = (float) at.getScaleY();
99 if (Mxy == 0f && Myx == 0f) {
100 if (Mxx == 1f && Myy == 1f) {
101 return out;
102 } else {
103 return new DeltaScaleFilter(out, 1.0f/Mxx, 1.0f/Myy);
104 }
105 } else {
106 float det = Mxx * Myy - Mxy * Myx;
107 return new DeltaTransformFilter(out,
108 Myy / det,
109 -Mxy / det,
110 -Myx / det,
111 Mxx / det);
112 }
113 }
114
115 static final class TranslateFilter implements PathConsumer2D {
116 private final PathConsumer2D out;
117 private final float tx;
118 private final float ty;
119
120 TranslateFilter(PathConsumer2D out,
121 float tx, float ty)
122 {
123 this.out = out;
124 this.tx = tx;
125 this.ty = ty;
126 }
127
128 public void moveTo(float x0, float y0) {
129 out.moveTo(x0 + tx, y0 + ty);
130 }
131
132 public void lineTo(float x1, float y1) {
133 out.lineTo(x1 + tx, y1 + ty);
134 }
135
136 public void quadTo(float x1, float y1,
137 float x2, float y2)
138 {
139 out.quadTo(x1 + tx, y1 + ty,
140 x2 + tx, y2 + ty);
141 }
142
143 public void curveTo(float x1, float y1,
144 float x2, float y2,
145 float x3, float y3)
146 {
147 out.curveTo(x1 + tx, y1 + ty,
148 x2 + tx, y2 + ty,
149 x3 + tx, y3 + ty);
150 }
151
152 public void closePath() {
153 out.closePath();
154 }
155
156 public void pathDone() {
157 out.pathDone();
158 }
159
160 public long getNativeConsumer() {
161 return 0;
162 }
163 }
164
165 static final class ScaleFilter implements PathConsumer2D {
166 private final PathConsumer2D out;
167 private final float sx;
168 private final float sy;
169 private final float tx;
170 private final float ty;
171
172 ScaleFilter(PathConsumer2D out,
173 float sx, float sy, float tx, float ty)
174 {
175 this.out = out;
176 this.sx = sx;
177 this.sy = sy;
178 this.tx = tx;
179 this.ty = ty;
180 }
181
182 public void moveTo(float x0, float y0) {
183 out.moveTo(x0 * sx + tx, y0 * sy + ty);
184 }
185
186 public void lineTo(float x1, float y1) {
187 out.lineTo(x1 * sx + tx, y1 * sy + ty);
188 }
189
190 public void quadTo(float x1, float y1,
191 float x2, float y2)
192 {
193 out.quadTo(x1 * sx + tx, y1 * sy + ty,
194 x2 * sx + tx, y2 * sy + ty);
195 }
196
197 public void curveTo(float x1, float y1,
198 float x2, float y2,
199 float x3, float y3)
200 {
201 out.curveTo(x1 * sx + tx, y1 * sy + ty,
202 x2 * sx + tx, y2 * sy + ty,
203 x3 * sx + tx, y3 * sy + ty);
204 }
205
206 public void closePath() {
207 out.closePath();
208 }
209
210 public void pathDone() {
211 out.pathDone();
212 }
213
214 public long getNativeConsumer() {
215 return 0;
216 }
217 }
218
219 static final class TransformFilter implements PathConsumer2D {
220 private final PathConsumer2D out;
221 private final float Mxx;
222 private final float Mxy;
223 private final float Mxt;
224 private final float Myx;
225 private final float Myy;
226 private final float Myt;
227
228 TransformFilter(PathConsumer2D out,
229 float Mxx, float Mxy, float Mxt,
230 float Myx, float Myy, float Myt)
231 {
232 this.out = out;
233 this.Mxx = Mxx;
234 this.Mxy = Mxy;
235 this.Mxt = Mxt;
236 this.Myx = Myx;
237 this.Myy = Myy;
238 this.Myt = Myt;
239 }
240
241 public void moveTo(float x0, float y0) {
242 out.moveTo(x0 * Mxx + y0 * Mxy + Mxt,
243 x0 * Myx + y0 * Myy + Myt);
244 }
245
246 public void lineTo(float x1, float y1) {
247 out.lineTo(x1 * Mxx + y1 * Mxy + Mxt,
248 x1 * Myx + y1 * Myy + Myt);
249 }
250
251 public void quadTo(float x1, float y1,
252 float x2, float y2)
253 {
254 out.quadTo(x1 * Mxx + y1 * Mxy + Mxt,
255 x1 * Myx + y1 * Myy + Myt,
256 x2 * Mxx + y2 * Mxy + Mxt,
257 x2 * Myx + y2 * Myy + Myt);
258 }
259
260 public void curveTo(float x1, float y1,
261 float x2, float y2,
262 float x3, float y3)
263 {
264 out.curveTo(x1 * Mxx + y1 * Mxy + Mxt,
265 x1 * Myx + y1 * Myy + Myt,
266 x2 * Mxx + y2 * Mxy + Mxt,
267 x2 * Myx + y2 * Myy + Myt,
268 x3 * Mxx + y3 * Mxy + Mxt,
269 x3 * Myx + y3 * Myy + Myt);
270 }
271
272 public void closePath() {
273 out.closePath();
274 }
275
276 public void pathDone() {
277 out.pathDone();
278 }
279
280 public long getNativeConsumer() {
281 return 0;
282 }
283 }
284
285 static final class DeltaScaleFilter implements PathConsumer2D {
286 private final float sx, sy;
287 private final PathConsumer2D out;
288
289 public DeltaScaleFilter(PathConsumer2D out, float Mxx, float Myy) {
290 sx = Mxx;
291 sy = Myy;
292 this.out = out;
293 }
294
295 public void moveTo(float x0, float y0) {
296 out.moveTo(x0 * sx, y0 * sy);
297 }
298
299 public void lineTo(float x1, float y1) {
300 out.lineTo(x1 * sx, y1 * sy);
301 }
302
303 public void quadTo(float x1, float y1,
304 float x2, float y2)
305 {
306 out.quadTo(x1 * sx, y1 * sy,
307 x2 * sx, y2 * sy);
308 }
309
310 public void curveTo(float x1, float y1,
311 float x2, float y2,
312 float x3, float y3)
313 {
314 out.curveTo(x1 * sx, y1 * sy,
315 x2 * sx, y2 * sy,
316 x3 * sx, y3 * sy);
317 }
318
319 public void closePath() {
320 out.closePath();
321 }
322
323 public void pathDone() {
324 out.pathDone();
325 }
326
327 public long getNativeConsumer() {
328 return 0;
329 }
330 }
331
332 static final class DeltaTransformFilter implements PathConsumer2D {
333 private PathConsumer2D out;
334 private final float Mxx;
335 private final float Mxy;
336 private final float Myx;
337 private final float Myy;
338
339 DeltaTransformFilter(PathConsumer2D out,
340 float Mxx, float Mxy,
341 float Myx, float Myy)
342 {
343 this.out = out;
344 this.Mxx = Mxx;
345 this.Mxy = Mxy;
346 this.Myx = Myx;
347 this.Myy = Myy;
348 }
349
350 public void moveTo(float x0, float y0) {
351 out.moveTo(x0 * Mxx + y0 * Mxy,
352 x0 * Myx + y0 * Myy);
353 }
354
355 public void lineTo(float x1, float y1) {
356 out.lineTo(x1 * Mxx + y1 * Mxy,
357 x1 * Myx + y1 * Myy);
358 }
359
360 public void quadTo(float x1, float y1,
361 float x2, float y2)
362 {
363 out.quadTo(x1 * Mxx + y1 * Mxy,
364 x1 * Myx + y1 * Myy,
365 x2 * Mxx + y2 * Mxy,
366 x2 * Myx + y2 * Myy);
367 }
368
369 public void curveTo(float x1, float y1,
370 float x2, float y2,
371 float x3, float y3)
372 {
373 out.curveTo(x1 * Mxx + y1 * Mxy,
374 x1 * Myx + y1 * Myy,
375 x2 * Mxx + y2 * Mxy,
376 x2 * Myx + y2 * Myy,
377 x3 * Mxx + y3 * Mxy,
378 x3 * Myx + y3 * Myy);
379 }
380
381 public void closePath() {
382 out.closePath();
383 }
384
385 public void pathDone() {
386 out.pathDone();
387 }
388
389 public long getNativeConsumer() {
390 return 0;
391 }
392 }
393}
java/pkg/sun/java2d/pisces/mkfile+20−0
@@ -0,0 +1,20 @@
1<../../../../../mkconfig
2
3DIRS=\
4 META-INF\
5
6TARG=\
7 Curve.dis\
8 Dasher.dis\
9 Helpers.dis\
10 PiscesCache.dis\
11 PiscesRenderingEngine.dis\
12 PiscesTileGenerator.dis\
13 Renderer.dis\
14 Stroker.dis\
15 TransformingPathConsumer2D.dis\
16
17DISBIN=$ROOT/java/dis/java/java/sun/java2d/pisces
18
19<$ROOT/mkfiles/mkjava
20<$ROOT/mkfiles/mksubdirs