Showing the changes to java/pkg/sun/java2d/pisces (13 of 18352 files). Show all
Unified · Split
| @@ -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 | |||
| @@ -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 | |||
| @@ -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> + 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 | } | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | <../../../../../../mkconfig | ||
| 2 | |||
| 3 | DIRS=\ | ||
| 4 | services\ | ||
| 5 | |||
| 6 | <$ROOT/mkfiles/mksubdirs | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | <../../../../../../../mkconfig | ||
| 2 | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | # Pisces Rendering Engine module | ||
| 2 | sun.java2d.pisces.PiscesRenderingEngine | ||
| @@ -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 | } | ||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | } | ||
| @@ -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 | } | ||
| @@ -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 | } | ||
| @@ -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 | ||