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+
26+package sun.java2d.pisces;
27+
28+import java.util.Iterator;
29+
30+final 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+
26+package sun.java2d.pisces;
27+
28+import 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+ */
41+final 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+
26+package sun.java2d.pisces;
27+
28+import java.util.Arrays;
29+import static java.lang.Math.PI;
30+import static java.lang.Math.cos;
31+import static java.lang.Math.sqrt;
32+import static java.lang.Math.cbrt;
33+import static java.lang.Math.acos;
34+
35+
36+final 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+
3+DIRS=\
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
2+sun.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+
26+package sun.java2d.pisces;
27+
28+import java.util.Arrays;
29+
30+/**
31+ * An object used to cache pre-rendered complex paths.
32+ *
33+ * @see PiscesRenderer#render
34+ */
35+final 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+
26+package sun.java2d.pisces;
27+
28+import java.awt.Shape;
29+import java.awt.BasicStroke;
30+import java.awt.geom.Path2D;
31+import java.awt.geom.AffineTransform;
32+import java.awt.geom.PathIterator;
33+
34+import sun.awt.geom.PathConsumer2D;
35+import sun.java2d.pipe.Region;
36+import sun.java2d.pipe.RenderingEngine;
37+import sun.java2d.pipe.AATileGenerator;
38+
39+public 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+
26+package sun.java2d.pisces;
27+
28+import java.util.Map;
29+import java.util.concurrent.ConcurrentHashMap;
30+
31+import sun.java2d.pipe.AATileGenerator;
32+
33+final 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+
26+package sun.java2d.pisces;
27+
28+import sun.awt.geom.PathConsumer2D;
29+
30+final 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+
26+package sun.java2d.pisces;
27+
28+import java.util.Arrays;
29+import java.util.Iterator;
30+import static java.lang.Math.ulp;
31+import static java.lang.Math.sqrt;
32+
33+import 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
38+final 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+
26+package sun.java2d.pisces;
27+
28+import sun.awt.geom.PathConsumer2D;
29+import java.awt.geom.AffineTransform;
30+
31+final 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+
3+DIRS=\
4+ META-INF\
5+
6+TARG=\
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+
17+DISBIN=$ROOT/java/dis/java/java/sun/java2d/pisces
18+
19+<$ROOT/mkfiles/mkjava
20+<$ROOT/mkfiles/mksubdirs