changeset 3600:5e624003e622

7016856: dashing performance was reduced during latest changes to the OpenJDK rasterizer Summary: Optimized dashing, rasterizing, and the flow of transformed coordinates Reviewed-by: flar
author dlila
date Tue, 08 Feb 2011 09:22:49 -0500
parents 21621a756b32
children b5fc02e1a944 7905b047a475
files src/share/classes/sun/java2d/pisces/Curve.java src/share/classes/sun/java2d/pisces/Dasher.java src/share/classes/sun/java2d/pisces/Helpers.java src/share/classes/sun/java2d/pisces/PiscesCache.java src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java src/share/classes/sun/java2d/pisces/Renderer.java src/share/classes/sun/java2d/pisces/Stroker.java src/share/classes/sun/java2d/pisces/TransformingPathConsumer2D.java
diffstat 9 files changed, 727 insertions(+), 793 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/java2d/pisces/Curve.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/Curve.java	Tue Feb 08 09:22:49 2011 -0500
@@ -27,7 +27,7 @@
 
 import java.util.Iterator;
 
-class Curve {
+final class Curve {
 
     float ax, ay, bx, by, cx, cy, dx, dy;
     float dax, day, dbx, dby;
@@ -101,14 +101,6 @@
         return t * (t * day + dby) + cy;
     }
 
-    private float ddxat(float t) {
-        return 2 * dax * t + dbx;
-    }
-
-    private float ddyat(float t) {
-        return 2 * day * t + dby;
-    }
-
     int dxRoots(float[] roots, int off) {
         return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
     }
@@ -131,17 +123,17 @@
     // finds points where the first and second derivative are
     // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
     // * is a dot product). Unfortunately, we have to solve a cubic.
-    private int perpendiculardfddf(float[] pts, int off, final float err) {
+    private int perpendiculardfddf(float[] pts, int off) {
         assert pts.length >= off + 4;
 
-        // these are the coefficients of g(t):
+        // these are the coefficients of some multiple of g(t) (not g(t),
+        // because the roots of a polynomial are not changed after multiplication
+        // by a constant, and this way we save a few multiplications).
         final float a = 2*(dax*dax + day*day);
         final float b = 3*(dax*dbx + day*dby);
         final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby;
         final float d = dbx*cx + dby*cy;
-        // TODO: We might want to divide the polynomial by a to make the
-        // coefficients smaller. This won't change the roots.
-        return Helpers.cubicRootsInAB(a, b, c, d, pts, off, err, 0f, 1f);
+        return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
     }
 
     // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
@@ -161,7 +153,7 @@
         // no OOB exception, because by now off<=6, and roots.length >= 10
         assert off <= 6 && roots.length >= 10;
         int ret = off;
-        int numPerpdfddf = perpendiculardfddf(roots, off, err);
+        int numPerpdfddf = perpendiculardfddf(roots, off);
         float t0 = 0, ft0 = ROCsq(t0) - w*w;
         roots[off + numPerpdfddf] = 1f; // always check interval end points
         numPerpdfddf++;
@@ -189,8 +181,9 @@
     // A slight modification of the false position algorithm on wikipedia.
     // This only works for the ROCsq-x functions. It might be nice to have
     // the function as an argument, but that would be awkward in java6.
-    // It is something to consider for java7, depending on how closures
-    // and function objects turn out. Same goes for the newton's method
+    // TODO: It is something to consider for java8 (or whenever lambda
+    // expressions make it into the language), depending on how closures
+    // and turn out. Same goes for the newton's method
     // algorithm in Helpers.java
     private float falsePositionROCsqMinusX(float x0, float x1,
                                            final float x, final float err)
@@ -203,7 +196,7 @@
         for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
             r = (fs * t - ft * s) / (fs - ft);
             fr = ROCsq(r) - x;
-            if (fr * ft > 0) {// have the same sign
+            if (sameSign(fr, ft)) {
                 ft = fr; t = r;
                 if (side < 0) {
                     fs /= (1 << (-side));
@@ -226,55 +219,65 @@
         return r;
     }
 
+    private static boolean sameSign(double x, double y) {
+        // another way is to test if x*y > 0. This is bad for small x, y.
+        return (x < 0 && y < 0) || (x > 0 && y > 0);
+    }
+
     // returns the radius of curvature squared at t of this curve
     // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
     private float ROCsq(final float t) {
-        final float dx = dxat(t);
-        final float dy = dyat(t);
-        final float ddx = ddxat(t);
-        final float ddy = ddyat(t);
+        // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
+        final float dx = t * (t * dax + dbx) + cx;
+        final float dy = t * (t * day + dby) + cy;
+        final float ddx = 2 * dax * t + dbx;
+        final float ddy = 2 * day * t + dby;
         final float dx2dy2 = dx*dx + dy*dy;
         final float ddx2ddy2 = ddx*ddx + ddy*ddy;
         final float ddxdxddydy = ddx*dx + ddy*dy;
-        float ret = ((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy))*dx2dy2;
-        return ret;
+        return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
     }
 
-    // curve to be broken should be in pts[0]
-    // this will change the contents of both pts and Ts
+    // curve to be broken should be in pts
+    // this will change the contents of pts but not Ts
     // TODO: There's no reason for Ts to be an array. All we need is a sequence
     // of t values at which to subdivide. An array statisfies this condition,
     // but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.
     // Doing this will also make dashing easier, since we could easily make
     // LengthIterator an Iterator<Float> and feed it to this function to simplify
     // the loop in Dasher.somethingTo.
-    static Iterator<float[]> breakPtsAtTs(final float[][] pts, final int type,
+    static Iterator<Integer> breakPtsAtTs(final float[] pts, final int type,
                                           final float[] Ts, final int numTs)
     {
-        assert pts.length >= 2 && pts[0].length >= 8 && numTs <= Ts.length;
-        return new Iterator<float[]>() {
-            int nextIdx = 0;
+        assert pts.length >= 2*type && numTs <= Ts.length;
+        return new Iterator<Integer>() {
+            // these prevent object creation and destruction during autoboxing.
+            // Because of this, the compiler should be able to completely
+            // eliminate the boxing costs.
+            final Integer i0 = 0;
+            final Integer itype = type;
             int nextCurveIdx = 0;
+            Integer curCurveOff = i0;
             float prevT = 0;
 
             @Override public boolean hasNext() {
                 return nextCurveIdx < numTs + 1;
             }
 
-            @Override public float[] next() {
-                float[] ret;
+            @Override public Integer next() {
+                Integer ret;
                 if (nextCurveIdx < numTs) {
                     float curT = Ts[nextCurveIdx];
                     float splitT = (curT - prevT) / (1 - prevT);
                     Helpers.subdivideAt(splitT,
-                                        pts[nextIdx], 0,
-                                        pts[nextIdx], 0,
-                                        pts[1-nextIdx], 0, type);
-                    updateTs(Ts, Ts[nextCurveIdx], nextCurveIdx + 1, numTs - nextCurveIdx - 1);
-                    ret = pts[nextIdx];
-                    nextIdx = 1 - nextIdx;
+                                        pts, curCurveOff,
+                                        pts, 0,
+                                        pts, type, type);
+                    prevT = curT;
+                    ret = i0;
+                    curCurveOff = itype;
                 } else {
-                    ret = pts[nextIdx];
+                    ret = curCurveOff;
                 }
                 nextCurveIdx++;
                 return ret;
@@ -283,12 +286,5 @@
             @Override public void remove() {}
         };
     }
-
-    // precondition: ts[off]...ts[off+len-1] must all be greater than t.
-    private static void updateTs(float[] ts, final float t, final int off, final int len) {
-        for (int i = off; i < off + len; i++) {
-            ts[i] = (ts[i] - t) / (1 - t);
-        }
-    }
 }
 
--- a/src/share/classes/sun/java2d/pisces/Dasher.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/Dasher.java	Tue Feb 08 09:22:49 2011 -0500
@@ -38,7 +38,7 @@
  * semantics are unclear.
  *
  */
-public class Dasher implements sun.awt.geom.PathConsumer2D {
+final class Dasher implements sun.awt.geom.PathConsumer2D {
 
     private final PathConsumer2D out;
     private final float[] dash;
@@ -169,7 +169,7 @@
         float dx = x1 - x0;
         float dy = y1 - y0;
 
-        float len = (float) Math.hypot(dx, dy);
+        float len = (float) Math.sqrt(dx*dx + dy*dy);
 
         if (len == 0) {
             return;
@@ -226,7 +226,7 @@
             return;
         }
         if (li == null) {
-            li = new LengthIterator(4, 0.0001f);
+            li = new LengthIterator(4, 0.01f);
         }
         li.initializeIterationOnCurve(curCurvepts, type);
 
@@ -237,9 +237,9 @@
         while ((t = li.next(leftInThisDashSegment)) < 1) {
             if (t != 0) {
                 Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT),
-                        curCurvepts, curCurveoff,
-                        curCurvepts, 0,
-                        curCurvepts, type, type);
+                                    curCurvepts, curCurveoff,
+                                    curCurvepts, 0,
+                                    curCurvepts, type, type);
                 lastSplitT = t;
                 goTo(curCurvepts, 2, type);
                 curCurveoff = type;
@@ -307,6 +307,11 @@
         private int recLevel;
         private boolean done;
 
+        // the lengths of the lines of the control polygon. Only its first
+        // curveType/2 - 1 elements are valid. This is an optimization. See
+        // next(float) for more detail.
+        private float[] curLeafCtrlPolyLengths = new float[3];
+
         public LengthIterator(int reclimit, float err) {
             this.limit = reclimit;
             this.minTincrement = 1f / (1 << limit);
@@ -344,11 +349,52 @@
             this.lastSegLen = 0;
         }
 
+        // 0 == false, 1 == true, -1 == invalid cached value.
+        private int cachedHaveLowAcceleration = -1;
+
+        private boolean haveLowAcceleration(float err) {
+            if (cachedHaveLowAcceleration == -1) {
+                final float len1 = curLeafCtrlPolyLengths[0];
+                final float len2 = curLeafCtrlPolyLengths[1];
+                // the test below is equivalent to !within(len1/len2, 1, err).
+                // It is using a multiplication instead of a division, so it
+                // should be a bit faster.
+                if (!Helpers.within(len1, len2, err*len2)) {
+                    cachedHaveLowAcceleration = 0;
+                    return false;
+                }
+                if (curveType == 8) {
+                    final float len3 = curLeafCtrlPolyLengths[2];
+                    // if len1 is close to 2 and 2 is close to 3, that probably
+                    // means 1 is close to 3 so the second part of this test might
+                    // not be needed, but it doesn't hurt to include it.
+                    if (!(Helpers.within(len2, len3, err*len3) &&
+                          Helpers.within(len1, len3, err*len3))) {
+                        cachedHaveLowAcceleration = 0;
+                        return false;
+                    }
+                }
+                cachedHaveLowAcceleration = 1;
+                return true;
+            }
+
+            return (cachedHaveLowAcceleration == 1);
+        }
+
+        // we want to avoid allocations/gc so we keep this array so we
+        // can put roots in it,
+        private float[] nextRoots = new float[4];
+
+        // caches the coefficients of the current leaf in its flattened
+        // form (see inside next() for what that means). The cache is
+        // invalid when it's third element is negative, since in any
+        // valid flattened curve, this would be >= 0.
+        private float[] flatLeafCoefCache = new float[] {0, 0, -1, 0};
         // returns the t value where the remaining curve should be split in
         // order for the left subdivided curve to have length len. If len
         // is >= than the length of the uniterated curve, it returns 1.
-        public float next(float len) {
-            float targetLength = lenAtLastSplit + len;
+        public float next(final float len) {
+            final float targetLength = lenAtLastSplit + len;
             while(lenAtNextT < targetLength) {
                 if (done) {
                     lastSegLen = lenAtNextT - lenAtLastSplit;
@@ -357,8 +403,46 @@
                 goToNextLeaf();
             }
             lenAtLastSplit = targetLength;
-            float t = binSearchForLen(lenAtLastSplit - lenAtLastT,
-                    recCurveStack[recLevel], curveType, lenAtNextT - lenAtLastT, ERR);
+            final float leaflen = lenAtNextT - lenAtLastT;
+            float t = (targetLength - lenAtLastT) / leaflen;
+
+            // cubicRootsInAB is a fairly expensive call, so we just don't do it
+            // if the acceleration in this section of the curve is small enough.
+            if (!haveLowAcceleration(0.05f)) {
+                // We flatten the current leaf along the x axis, so that we're
+                // left with a, b, c which define a 1D Bezier curve. We then
+                // solve this to get the parameter of the original leaf that
+                // gives us the desired length.
+
+                if (flatLeafCoefCache[2] < 0) {
+                    float x = 0+curLeafCtrlPolyLengths[0],
+                          y = x+curLeafCtrlPolyLengths[1];
+                    if (curveType == 8) {
+                        float z = y + curLeafCtrlPolyLengths[2];
+                        flatLeafCoefCache[0] = 3*(x - y) + z;
+                        flatLeafCoefCache[1] = 3*(y - 2*x);
+                        flatLeafCoefCache[2] = 3*x;
+                        flatLeafCoefCache[3] = -z;
+                    } else if (curveType == 6) {
+                        flatLeafCoefCache[0] = 0f;
+                        flatLeafCoefCache[1] = y - 2*x;
+                        flatLeafCoefCache[2] = 2*x;
+                        flatLeafCoefCache[3] = -y;
+                    }
+                }
+                float a = flatLeafCoefCache[0];
+                float b = flatLeafCoefCache[1];
+                float c = flatLeafCoefCache[2];
+                float d = t*flatLeafCoefCache[3];
+
+                // we use cubicRootsInAB here, because we want only roots in 0, 1,
+                // and our quadratic root finder doesn't filter, so it's just a
+                // matter of convenience.
+                int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1);
+                if (n == 1 && !Float.isNaN(nextRoots[0])) {
+                    t = nextRoots[0];
+                }
+            }
             // t is relative to the current leaf, so we must make it a valid parameter
             // of the original curve.
             t = t * (nextT - lastT) + lastT;
@@ -379,36 +463,6 @@
             return lastSegLen;
         }
 
-        // Returns t such that if leaf is subdivided at t the left
-        // curve will have length len. leafLen must be the length of leaf.
-        private static Curve bsc = new Curve();
-        private static float binSearchForLen(float len, float[] leaf, int type,
-                                             float leafLen, float err)
-        {
-            assert len <= leafLen;
-            bsc.set(leaf, type);
-            float errBound = err*len;
-            float left = 0, right = 1;
-            while (left < right) {
-                float m = (left + right) / 2;
-                if (m == left || m == right) {
-                    return m;
-                }
-                float x = bsc.xat(m);
-                float y = bsc.yat(m);
-                float leftLen = Helpers.linelen(leaf[0], leaf[1], x, y);
-                if (Math.abs(leftLen - len) < errBound) {
-                    return m;
-                }
-                if (leftLen < len) {
-                    left = m;
-                } else {
-                    right = m;
-                }
-            }
-            return left;
-        }
-
         // go to the next leaf (in an inorder traversal) in the recursion tree
         // preconditions: must be on a leaf, and that leaf must not be the root.
         private void goToNextLeaf() {
@@ -437,6 +491,9 @@
                 lenAtLastT = lenAtNextT;
                 nextT += (1 << (limit - recLevel)) * minTincrement;
                 lenAtNextT += len;
+                // invalidate caches
+                flatLeafCoefCache[2] = -1;
+                cachedHaveLowAcceleration = -1;
             } else {
                 Helpers.subdivide(recCurveStack[recLevel], 0,
                                   recCurveStack[recLevel+1], 0,
@@ -450,11 +507,24 @@
         // this is a bit of a hack. It returns -1 if we're not on a leaf, and
         // the length of the leaf if we are on a leaf.
         private float onLeaf() {
-            float polylen = Helpers.polyLineLength(recCurveStack[recLevel], 0, curveType);
-            float linelen = Helpers.linelen(recCurveStack[recLevel][0], recCurveStack[recLevel][1],
-                    recCurveStack[recLevel][curveType - 2], recCurveStack[recLevel][curveType - 1]);
-            return (polylen - linelen < ERR || recLevel == limit) ?
-                   (polylen + linelen)/2 : -1;
+            float[] curve = recCurveStack[recLevel];
+            float polyLen = 0;
+
+            float x0 = curve[0], y0 = curve[1];
+            for (int i = 2; i < curveType; i += 2) {
+                final float x1 = curve[i], y1 = curve[i+1];
+                final float len = Helpers.linelen(x0, y0, x1, y1);
+                polyLen += len;
+                curLeafCtrlPolyLengths[i/2 - 1] = len;
+                x0 = x1;
+                y0 = y1;
+            }
+
+            final float lineLen = Helpers.linelen(curve[0], curve[1], curve[curveType-2], curve[curveType-1]);
+            if (polyLen - lineLen < ERR || recLevel == limit) {
+                return (polyLen + lineLen)/2;
+            }
+            return -1;
         }
     }
 
--- a/src/share/classes/sun/java2d/pisces/Helpers.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/Helpers.java	Tue Feb 08 09:22:49 2011 -0500
@@ -26,6 +26,12 @@
 package sun.java2d.pisces;
 
 import java.util.Arrays;
+import static java.lang.Math.PI;
+import static java.lang.Math.cos;
+import static java.lang.Math.sqrt;
+import static java.lang.Math.cbrt;
+import static java.lang.Math.acos;
+
 
 final class Helpers {
     private Helpers() {
@@ -75,100 +81,74 @@
         return ret - off;
     }
 
-    // find the roots of g(t) = a*t^3 + b*t^2 + c*t + d in [A,B)
-    // We will not use Cardano's method, since it is complicated and
-    // involves too many square and cubic roots. We will use Newton's method.
-    // TODO: this should probably return ALL roots. Then the user can do
-    // his own filtering of roots outside [A,B).
-    static int cubicRootsInAB(final float a, final float b,
-                              final float c, final float d,
-                              float[] pts, final int off, final float E,
+    // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
+    static int cubicRootsInAB(float d, float a, float b, float c,
+                              float[] pts, final int off,
                               final float A, final float B)
     {
-        if (a == 0) {
-            return quadraticRoots(b, c, d, pts, off);
+        if (d == 0) {
+            int num = quadraticRoots(a, b, c, pts, off);
+            return filterOutNotInAB(pts, off, num, A, B) - off;
         }
-        // the coefficients of g'(t). no dc variable because dc=c
-        // we use these to get the critical points of g(t), which
-        // we then use to chose starting points for Newton's method. These
-        // should be very close to the actual roots.
-        final float da = 3 * a;
-        final float db = 2 * b;
-        int numCritPts = quadraticRoots(da, db, c, pts, off+1);
-        numCritPts = filterOutNotInAB(pts, off+1, numCritPts, A, B) - off - 1;
-        // need them sorted.
-        if (numCritPts == 2 && pts[off+1] > pts[off+2]) {
-            float tmp = pts[off+1];
-            pts[off+1] = pts[off+2];
-            pts[off+2] = tmp;
+        // From Graphics Gems:
+        // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
+        // (also from awt.geom.CubicCurve2D. But here we don't need as
+        // much accuracy and we don't want to create arrays so we use
+        // our own customized version).
+
+        /* normal form: x^3 + ax^2 + bx + c = 0 */
+        a /= d;
+        b /= d;
+        c /= d;
+
+        //  substitute x = y - A/3 to eliminate quadratic term:
+        //     x^3 +Px + Q = 0
+        //
+        // Since we actually need P/3 and Q/2 for all of the
+        // calculations that follow, we will calculate
+        // p = P/3
+        // q = Q/2
+        // instead and use those values for simplicity of the code.
+        double sq_A = a * a;
+        double p = 1.0/3 * (-1.0/3 * sq_A + b);
+        double q = 1.0/2 * (2.0/27 * a * sq_A - 1.0/3 * a * b + c);
+
+        /* use Cardano's formula */
+
+        double cb_p = p * p * p;
+        double D = q * q + cb_p;
+
+        int num;
+        if (D < 0) {
+            // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
+            final double phi = 1.0/3 * acos(-q / sqrt(-cb_p));
+            final double t = 2 * sqrt(-p);
+
+            pts[ off+0 ] =  (float)( t * cos(phi));
+            pts[ off+1 ] =  (float)(-t * cos(phi + PI / 3));
+            pts[ off+2 ] =  (float)(-t * cos(phi - PI / 3));
+            num = 3;
+        } else {
+            final double sqrt_D = sqrt(D);
+            final double u = cbrt(sqrt_D - q);
+            final double v = - cbrt(sqrt_D + q);
+
+            pts[ off ] = (float)(u + v);
+            num = 1;
+
+            if (within(D, 0, 1e-8)) {
+                pts[off+1] = -(pts[off] / 2);
+                num = 2;
+            }
         }
 
-        int ret = off;
+        final float sub = 1.0f/3 * a;
 
-        // we don't actually care much about the extrema themselves. We
-        // only use them to ensure that g(t) is monotonic in each
-        // interval [pts[i],pts[i+1] (for i in off...off+numCritPts+1).
-        // This will allow us to determine intervals containing exactly
-        // one root.
-        // The end points of the interval are always local extrema.
-        pts[off] = A;
-        pts[off + numCritPts + 1] = B;
-        numCritPts += 2;
+        for (int i = 0; i < num; ++i) {
+            pts[ off+i ] -= sub;
+        }
 
-        float x0 = pts[off], fx0 = evalCubic(a, b, c, d, x0);
-        for (int i = off; i < off + numCritPts - 1; i++) {
-            float x1 = pts[i+1], fx1 = evalCubic(a, b, c, d, x1);
-            if (fx0 == 0f) {
-                pts[ret++] = x0;
-            } else if (fx1 * fx0 < 0f) { // have opposite signs
-                pts[ret++] = CubicNewton(a, b, c, d,
-                        x0 + fx0 * (x1 - x0) / (fx0 - fx1), E);
-            }
-            x0 = x1;
-            fx0 = fx1;
-        }
-        return ret - off;
-    }
-
-    // precondition: the polynomial to be evaluated must not be 0 at x0.
-    static float CubicNewton(final float a, final float b,
-                             final float c, final float d,
-                             float x0, final float err)
-    {
-        // considering how this function is used, 10 should be more than enough
-        final int itlimit = 10;
-        float fx0 = evalCubic(a, b, c, d, x0);
-        float x1;
-        int count = 0;
-        while(true) {
-            x1 = x0 - (fx0 / evalCubic(0, 3 * a, 2 * b, c, x0));
-            if (Math.abs(x1 - x0) < err * Math.abs(x1 + x0) || count == itlimit) {
-                break;
-            }
-            x0 = x1;
-            fx0 = evalCubic(a, b, c, d, x0);
-            count++;
-        }
-        return x1;
-    }
-
-    // fills the input array with numbers 0, INC, 2*INC, ...
-    static void fillWithIdxes(final float[] data, final int[] idxes) {
-        if (idxes.length > 0) {
-            idxes[0] = 0;
-            for (int i = 1; i < idxes.length; i++) {
-                idxes[i] = idxes[i-1] + (int)data[idxes[i-1]];
-            }
-        }
-    }
-
-    static void fillWithIdxes(final int[] idxes, final int inc) {
-        if (idxes.length > 0) {
-            idxes[0] = 0;
-            for (int i = 1; i < idxes.length; i++) {
-                idxes[i] = idxes[i-1] + inc;
-            }
-        }
+        return filterOutNotInAB(pts, off, num, A, B) - off;
     }
 
     // These use a hardcoded factor of 2 for increasing sizes. Perhaps this
@@ -182,6 +162,7 @@
         }
         return Arrays.copyOf(in, 2 * (cursize + numToAdd));
     }
+
     static int[] widenArray(int[] in, final int cursize, final int numToAdd) {
         if (in.length >= cursize + numToAdd) {
             return in;
@@ -208,7 +189,7 @@
     {
         int ret = off;
         for (int i = off; i < off + len; i++) {
-            if (nums[i] > a && nums[i] < b) {
+            if (nums[i] >= a && nums[i] < b) {
                 nums[ret++] = nums[i];
             }
         }
@@ -225,7 +206,9 @@
     }
 
     static float linelen(float x1, float y1, float x2, float y2) {
-        return (float)Math.hypot(x2 - x1, y2 - y1);
+        final float dx = x2 - x1;
+        final float dy = y2 - y1;
+        return (float)Math.sqrt(dx*dx + dy*dy);
     }
 
     static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
--- a/src/share/classes/sun/java2d/pisces/PiscesCache.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/PiscesCache.java	Tue Feb 08 09:22:49 2011 -0500
@@ -32,7 +32,7 @@
  *
  * @see PiscesRenderer#render
  */
-public final class PiscesCache {
+final class PiscesCache {
 
     final int bboxX0, bboxY0, bboxX1, bboxY1;
 
--- a/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Tue Feb 08 09:22:49 2011 -0500
@@ -27,7 +27,6 @@
 
 import java.awt.Shape;
 import java.awt.BasicStroke;
-import java.awt.geom.NoninvertibleTransformException;
 import java.awt.geom.Path2D;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.PathIterator;
@@ -250,7 +249,7 @@
                   float dashphase,
                   PathConsumer2D pc2d)
     {
-        // We use inat and outat so that in Stroker and Dasher we can work only
+        // We use strokerat and outat so that in Stroker and Dasher we can work only
         // with the pre-transformation coordinates. This will repeat a lot of
         // computations done in the path iterator, but the alternative is to
         // work with transformed paths and compute untransformed coordinates
@@ -265,7 +264,7 @@
         // transformation after the path processing has been done.
         // We can't do this if normalization is on, because it isn't a good
         // idea to normalize before the transformation is applied.
-        AffineTransform inat = null;
+        AffineTransform strokerat = null;
         AffineTransform outat = null;
 
         PathIterator pi = null;
@@ -284,9 +283,9 @@
                 // again so, nothing can be drawn.
 
                 // Every path needs an initial moveTo and a pathDone. If these
-                // aren't there this causes a SIGSEV in libawt.so (at the time
+                // are not there this causes a SIGSEGV in libawt.so (at the time
                 // of writing of this comment (September 16, 2010)). Actually,
-                // I'm not sure if the moveTo is necessary to avoid the SIGSEV
+                // I am not sure if the moveTo is necessary to avoid the SIGSEGV
                 // but the pathDone is definitely needed.
                 pc2d.moveTo(0, 0);
                 pc2d.pathDone();
@@ -313,25 +312,32 @@
                 if (normalize != NormMode.OFF) {
                     pi = new NormalizingPathIterator(pi, normalize);
                 }
-                // leave inat and outat null.
+                // by now strokerat == null && outat == null. Input paths to
+                // stroker (and maybe dasher) will have the full transform at
+                // applied to them and nothing will happen to the output paths.
             } else {
-                // We only need the inverse if normalization is on. Otherwise
-                // we just don't transform the input paths, do all the stroking
-                // and then transform out output (instead of making PathIterator
-                // apply the transformation, us applying the inverse, and then
-                // us applying the transform again to our output).
-                outat = at;
                 if (normalize != NormMode.OFF) {
-                    try {
-                        inat = outat.createInverse();
-                    } catch (NoninvertibleTransformException e) {
-                        // we made sure this can't happen
-                        e.printStackTrace();
-                    }
+                    strokerat = at;
                     pi = src.getPathIterator(at);
                     pi = new NormalizingPathIterator(pi, normalize);
+                    // by now strokerat == at && outat == null. Input paths to
+                    // stroker (and maybe dasher) will have the full transform at
+                    // applied to them, then they will be normalized, and then
+                    // the inverse of *only the non translation part of at* will
+                    // be applied to the normalized paths. This won't cause problems
+                    // in stroker, because, suppose at = T*A, where T is just the
+                    // translation part of at, and A is the rest. T*A has already
+                    // been applied to Stroker/Dasher's input. Then Ainv will be
+                    // applied. Ainv*T*A is not equal to T, but it is a translation,
+                    // which means that none of stroker's assumptions about its
+                    // input will be violated. After all this, A will be applied
+                    // to stroker's output.
                 } else {
+                    outat = at;
                     pi = src.getPathIterator(null);
+                    // outat == at && strokerat == null. This is because if no
+                    // normalization is done, we can just apply all our
+                    // transformations to stroker's output.
                 }
             }
         } else {
@@ -343,13 +349,17 @@
             }
         }
 
+        // by now, at least one of outat and strokerat will be null. Unless at is not
+        // a constant multiple of an orthogonal transformation, they will both be
+        // null. In other cases, outat == at if normalization is off, and if
+        // normalization is on, strokerat == at.
         pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat);
+        pc2d = TransformingPathConsumer2D.deltaTransformConsumer(pc2d, strokerat);
         pc2d = new Stroker(pc2d, width, caps, join, miterlimit);
         if (dashes != null) {
             pc2d = new Dasher(pc2d, dashes, dashphase);
         }
-        pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, inat);
-
+        pc2d = TransformingPathConsumer2D.inverseDeltaTransformConsumer(pc2d, strokerat);
         pathTo(pi, pc2d);
     }
 
@@ -588,9 +598,9 @@
         }
 
         Renderer r = new Renderer(3, 3,
-                                  clip.getLoX(), clip.getLoY(),
-                                  clip.getWidth(), clip.getHeight(),
-                                  PathIterator.WIND_EVEN_ODD);
+                clip.getLoX(), clip.getLoY(),
+                clip.getWidth(), clip.getHeight(),
+                PathIterator.WIND_EVEN_ODD);
 
         r.moveTo((float) x, (float) y);
         r.lineTo((float) (x+dx1), (float) (y+dy1));
--- a/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java	Tue Feb 08 09:22:49 2011 -0500
@@ -30,7 +30,7 @@
 
 import sun.java2d.pipe.AATileGenerator;
 
-public final class PiscesTileGenerator implements AATileGenerator {
+final class PiscesTileGenerator implements AATileGenerator {
     public static final int TILE_SIZE = PiscesCache.TILE_SIZE;
 
     // perhaps we should be using weak references here, but right now
--- a/src/share/classes/sun/java2d/pisces/Renderer.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/Renderer.java	Tue Feb 08 09:22:49 2011 -0500
@@ -25,12 +25,9 @@
 
 package sun.java2d.pisces;
 
-import java.util.Arrays;
-import java.util.Iterator;
-
 import sun.awt.geom.PathConsumer2D;
 
-public class Renderer implements PathConsumer2D {
+final class Renderer implements PathConsumer2D {
 
     private class ScanlineIterator {
 
@@ -39,115 +36,81 @@
         // crossing bounds. The bounds are not necessarily tight (the scan line
         // at minY, for example, might have no crossings). The x bounds will
         // be accumulated as crossings are computed.
-        private int minY, maxY;
+        private final int maxY;
         private int nextY;
 
         // indices into the segment pointer lists. They indicate the "active"
         // sublist in the segment lists (the portion of the list that contains
         // all the segments that cross the next scan line).
-        private int elo, ehi;
-        private final int[] edgePtrs;
-        private int qlo, qhi;
-        private final int[] quadPtrs;
-        private int clo, chi;
-        private final int[] curvePtrs;
+        private int edgeCount;
+        private int[] edgePtrs;
 
         private static final int INIT_CROSSINGS_SIZE = 10;
 
         private ScanlineIterator() {
             crossings = new int[INIT_CROSSINGS_SIZE];
-
-            edgePtrs = new int[numEdges];
-            Helpers.fillWithIdxes(edgePtrs, SIZEOF_EDGE);
-            qsort(edges, edgePtrs, YMIN, 0, numEdges - 1);
-
-            quadPtrs = new int[numQuads];
-            Helpers.fillWithIdxes(quadPtrs, SIZEOF_QUAD);
-            qsort(quads, quadPtrs, YMIN, 0, numQuads - 1);
-
-            curvePtrs = new int[numCurves];
-            Helpers.fillWithIdxes(curvePtrs, SIZEOF_CURVE);
-            qsort(curves, curvePtrs, YMIN, 0, numCurves - 1);
+            edgePtrs = new int[INIT_CROSSINGS_SIZE];
 
             // We don't care if we clip some of the line off with ceil, since
             // no scan line crossings will be eliminated (in fact, the ceil is
             // the y of the first scan line crossing).
-            nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY));
-            maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY));
-
-            for (elo = 0; elo < numEdges && edges[edgePtrs[elo]+YMAX] <= minY; elo++)
-                ;
-            // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive)
-            for (ehi = elo; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= minY; ehi++)
-                edgeSetCurY(edgePtrs[ehi], minY);// TODO: make minY a float to avoid casts
-
-            for (qlo = 0; qlo < numQuads && quads[quadPtrs[qlo]+YMAX] <= minY; qlo++)
-                ;
-            for (qhi = qlo; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= minY; qhi++)
-                quadSetCurY(quadPtrs[qhi], minY);
-
-            for (clo = 0; clo < numCurves && curves[curvePtrs[clo]+YMAX] <= minY; clo++)
-                ;
-            for (chi = clo; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= minY; chi++)
-                curveSetCurY(curvePtrs[chi], minY);
+            final int minY = getFirstScanLineCrossing();
+            nextY = minY;
+            maxY = getScanLineCrossingEnd()-1;
+            edgeCount = 0;
         }
 
         private int next() {
-            // we go through the active lists and remove segments that don't cross
-            // the nextY scanline.
-            int crossingIdx = 0;
-            for (int i = elo; i < ehi; i++) {
-                if (edges[edgePtrs[i]+YMAX] <= nextY) {
-                    edgePtrs[i] = edgePtrs[elo++];
+            int cury = nextY++;
+            int bucket = cury - boundsMinY;
+            int count = this.edgeCount;
+            int ptrs[] = this.edgePtrs;
+            int bucketcount = edgeBucketCounts[bucket];
+            if ((bucketcount & 0x1) != 0) {
+                int newCount = 0;
+                for (int i = 0; i < count; i++) {
+                    int ecur = ptrs[i];
+                    if (edges[ecur+YMAX] > cury) {
+                        ptrs[newCount++] = ecur;
+                    }
                 }
+                count = newCount;
             }
-            for (int i = qlo; i < qhi; i++) {
-                if (quads[quadPtrs[i]+YMAX] <= nextY) {
-                    quadPtrs[i] = quadPtrs[qlo++];
+            ptrs = Helpers.widenArray(ptrs, count, bucketcount >> 1);
+            for (int ecur = edgeBuckets[bucket]; ecur != NULL; ecur = (int)edges[ecur+NEXT]) {
+                ptrs[count++] = ecur;
+                // REMIND: Adjust start Y if necessary
+            }
+            this.edgePtrs = ptrs;
+            this.edgeCount = count;
+//            if ((count & 0x1) != 0) {
+//                System.out.println("ODD NUMBER OF EDGES!!!!");
+//            }
+            int xings[] = this.crossings;
+            if (xings.length < count) {
+                this.crossings = xings = new int[ptrs.length];
+            }
+            for (int i = 0; i < count; i++) {
+                int ecur = ptrs[i];
+                float curx = edges[ecur+CURX];
+                int cross = ((int) curx) << 1;
+                edges[ecur+CURX] = curx + edges[ecur+SLOPE];
+                if (edges[ecur+OR] > 0) {
+                    cross |= 1;
                 }
+                int j = i;
+                while (--j >= 0) {
+                    int jcross = xings[j];
+                    if (jcross <= cross) {
+                        break;
+                    }
+                    xings[j+1] = jcross;
+                    ptrs[j+1] = ptrs[j];
+                }
+                xings[j+1] = cross;
+                ptrs[j+1] = ecur;
             }
-            for (int i = clo; i < chi; i++) {
-                if (curves[curvePtrs[i]+YMAX] <= nextY) {
-                    curvePtrs[i] = curvePtrs[clo++];
-                }
-            }
-
-            crossings = Helpers.widenArray(crossings, 0, ehi-elo+qhi-qlo+chi-clo);
-
-            // Now every edge between lo and hi crosses nextY. Compute it's
-            // crossing and put it in the crossings array.
-            for (int i = elo; i < ehi; i++) {
-                int ptr = edgePtrs[i];
-                addCrossing(nextY, (int)edges[ptr+CURX], edges[ptr+OR], crossingIdx);
-                edgeGoToNextY(ptr);
-                crossingIdx++;
-            }
-            for (int i = qlo; i < qhi; i++) {
-                int ptr = quadPtrs[i];
-                addCrossing(nextY, (int)quads[ptr+CURX], quads[ptr+OR], crossingIdx);
-                quadGoToNextY(ptr);
-                crossingIdx++;
-            }
-            for (int i = clo; i < chi; i++) {
-                int ptr = curvePtrs[i];
-                addCrossing(nextY, (int)curves[ptr+CURX], curves[ptr+OR], crossingIdx);
-                curveGoToNextY(ptr);
-                crossingIdx++;
-            }
-
-            nextY++;
-            // Expand active lists to include new edges.
-            for (; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= nextY; ehi++) {
-                edgeSetCurY(edgePtrs[ehi], nextY);
-            }
-            for (; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= nextY; qhi++) {
-                quadSetCurY(quadPtrs[qhi], nextY);
-            }
-            for (; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= nextY; chi++) {
-                curveSetCurY(curvePtrs[chi], nextY);
-            }
-            Arrays.sort(crossings, 0, crossingIdx);
-            return crossingIdx;
+            return count;
         }
 
         private boolean hasNext() {
@@ -157,51 +120,7 @@
         private int curY() {
             return nextY - 1;
         }
-
-        private void addCrossing(int y, int x, float or, int idx) {
-            x <<= 1;
-            crossings[idx] = ((or > 0) ? (x | 0x1) : x);
-        }
     }
-    // quicksort implementation for sorting the edge indices ("pointers")
-    // by increasing y0. first, last are indices into the "pointer" array
-    // It sorts the pointer array from first (inclusive) to last (inclusive)
-    private static void qsort(final float[] data, final int[] ptrs,
-                              final int fieldForCmp, int first, int last)
-    {
-        if (last > first) {
-            int p = partition(data, ptrs, fieldForCmp, first, last);
-            if (first < p - 1) {
-                qsort(data, ptrs, fieldForCmp, first, p - 1);
-            }
-            if (p < last) {
-                qsort(data, ptrs, fieldForCmp, p, last);
-            }
-        }
-    }
-
-    // i, j are indices into edgePtrs.
-    private static int partition(final float[] data, final int[] ptrs,
-                                 final int fieldForCmp, int i, int j)
-    {
-        int pivotValFieldForCmp = ptrs[i]+fieldForCmp;
-        while (i <= j) {
-            // edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C
-            while (data[ptrs[i]+fieldForCmp] < data[pivotValFieldForCmp])
-                i++;
-            while (data[ptrs[j]+fieldForCmp] > data[pivotValFieldForCmp])
-                j--;
-            if (i <= j) {
-                int tmp = ptrs[i];
-                ptrs[i] = ptrs[j];
-                ptrs[j] = tmp;
-                i++;
-                j--;
-            }
-        }
-        return i;
-    }
-//============================================================================
 
 
 //////////////////////////////////////////////////////////////////////////////
@@ -209,269 +128,89 @@
 //////////////////////////////////////////////////////////////////////////////
 // TODO(maybe): very tempting to use fixed point here. A lot of opportunities
 // for shifts and just removing certain operations altogether.
-// TODO: it might be worth it to make an EdgeList class. It would probably
-// clean things up a bit and not impact performance much.
 
     // common to all types of input path segments.
-    private static final int YMIN = 0;
-    private static final int YMAX = 1;
-    private static final int CURX = 2;
-    // this and OR are meant to be indeces into "int" fields, but arrays must
+    private static final int YMAX = 0;
+    private static final int CURX = 1;
+    // NEXT and OR are meant to be indices into "int" fields, but arrays must
     // be homogenous, so every field is a float. However floats can represent
     // exactly up to 26 bit ints, so we're ok.
-    private static final int CURY = 3;
-    private static final int OR   = 4;
-
-    // for straight lines only:
-    private static final int SLOPE = 5;
-
-    // for quads and cubics:
-    private static final int X0 = 5;
-    private static final int Y0 = 6;
-    private static final int XL = 7;
-    private static final int COUNT = 8;
-    private static final int CURSLOPE = 9;
-    private static final int DX = 10;
-    private static final int DY = 11;
-    private static final int DDX = 12;
-    private static final int DDY = 13;
-
-    // for cubics only
-    private static final int DDDX = 14;
-    private static final int DDDY = 15;
+    private static final int OR   = 2;
+    private static final int SLOPE = 3;
+    private static final int NEXT = 4;
 
     private float edgeMinY = Float.POSITIVE_INFINITY;
     private float edgeMaxY = Float.NEGATIVE_INFINITY;
     private float edgeMinX = Float.POSITIVE_INFINITY;
     private float edgeMaxX = Float.NEGATIVE_INFINITY;
 
-    private static final int SIZEOF_EDGE = 6;
+    private static final int SIZEOF_EDGE = 5;
+    // don't just set NULL to -1, because we want NULL+NEXT to be negative.
+    private static final int NULL = -SIZEOF_EDGE;
     private float[] edges = null;
+    private int[] edgeBuckets = null;
+    private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed)
     private int numEdges;
-    // these are static because we need them to be usable from ScanlineIterator
-    private void edgeSetCurY(final int idx, int y) {
-        edges[idx+CURX] += (y - edges[idx+CURY]) * edges[idx+SLOPE];
-        edges[idx+CURY] = y;
-    }
-    private void edgeGoToNextY(final int idx) {
-        edges[idx+CURY] += 1;
-        edges[idx+CURX] += edges[idx+SLOPE];
-    }
-
-
-    private static final int SIZEOF_QUAD = 14;
-    private float[] quads = null;
-    private int numQuads;
-    // This function should be called exactly once, to set the first scanline
-    // of the curve. Before it is called, the curve should think its first
-    // scanline is CEIL(YMIN).
-    private void quadSetCurY(final int idx, final int y) {
-        assert y < quads[idx+YMAX];
-        assert (quads[idx+CURY] > y);
-        assert (quads[idx+CURY] == Math.ceil(quads[idx+CURY]));
-
-        while (quads[idx+CURY] < ((float)y)) {
-            quadGoToNextY(idx);
-        }
-    }
-    private void quadGoToNextY(final int idx) {
-        quads[idx+CURY] += 1;
-        // this will get overriden if the while executes.
-        quads[idx+CURX] += quads[idx+CURSLOPE];
-        int count = (int)quads[idx+COUNT];
-        // this loop should never execute more than once because our
-        // curve is monotonic in Y. Still we put it in because you can
-        // never be too sure when dealing with floating point.
-        while(quads[idx+CURY] >= quads[idx+Y0] && count > 0) {
-            float x0 = quads[idx+X0], y0 = quads[idx+Y0];
-            count = executeQuadAFDIteration(idx);
-            float x1 = quads[idx+X0], y1 = quads[idx+Y0];
-            // our quads are monotonic, so this shouldn't happen, but
-            // it is conceivable that for very flat quads with different
-            // y values at their endpoints AFD might give us a horizontal
-            // segment.
-            if (y1 == y0) {
-                continue;
-            }
-            quads[idx+CURSLOPE] = (x1 - x0) / (y1 - y0);
-            quads[idx+CURX] = x0 + (quads[idx+CURY] - y0) * quads[idx+CURSLOPE];
-        }
-    }
-
-
-    private static final int SIZEOF_CURVE = 16;
-    private float[] curves = null;
-    private int numCurves;
-    private void curveSetCurY(final int idx, final int y) {
-        assert y < curves[idx+YMAX];
-        assert (curves[idx+CURY] > y);
-        assert (curves[idx+CURY] == Math.ceil(curves[idx+CURY]));
-
-        while (curves[idx+CURY] < ((float)y)) {
-            curveGoToNextY(idx);
-        }
-    }
-    private void curveGoToNextY(final int idx) {
-        curves[idx+CURY] += 1;
-        // this will get overriden if the while executes.
-        curves[idx+CURX] += curves[idx+CURSLOPE];
-        int count = (int)curves[idx+COUNT];
-        // this loop should never execute more than once because our
-        // curve is monotonic in Y. Still we put it in because you can
-        // never be too sure when dealing with floating point.
-        while(curves[idx+CURY] >= curves[idx+Y0] && count > 0) {
-            float x0 = curves[idx+X0], y0 = curves[idx+Y0];
-            count = executeCurveAFDIteration(idx);
-            float x1 = curves[idx+X0], y1 = curves[idx+Y0];
-            // our curves are monotonic, so this shouldn't happen, but
-            // it is conceivable that for very flat curves with different
-            // y values at their endpoints AFD might give us a horizontal
-            // segment.
-            if (y1 == y0) {
-                continue;
-            }
-            curves[idx+CURSLOPE] = (x1 - x0) / (y1 - y0);
-            curves[idx+CURX] = x0 + (curves[idx+CURY] - y0) * curves[idx+CURSLOPE];
-        }
-    }
-
 
     private static final float DEC_BND = 20f;
     private static final float INC_BND = 8f;
+
+    // each bucket is a linked list. this method adds eptr to the
+    // start "bucket"th linked list.
+    private void addEdgeToBucket(final int eptr, final int bucket) {
+        edges[eptr+NEXT] = edgeBuckets[bucket];
+        edgeBuckets[bucket] = eptr;
+        edgeBucketCounts[bucket] += 2;
+    }
+
     // Flattens using adaptive forward differencing. This only carries out
     // one iteration of the AFD loop. All it does is update AFD variables (i.e.
     // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
-    private int executeQuadAFDIteration(int idx) {
-        int count = (int)quads[idx+COUNT];
-        float ddx = quads[idx+DDX];
-        float ddy = quads[idx+DDY];
-        float dx = quads[idx+DX];
-        float dy = quads[idx+DY];
-
-        while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
-            ddx = ddx / 4;
-            ddy = ddy / 4;
-            dx = (dx - ddx) / 2;
-            dy = (dy - ddy) / 2;
+    private void quadBreakIntoLinesAndAdd(float x0, float y0,
+                                          final Curve c,
+                                          final float x2, final float y2) {
+        final float QUAD_DEC_BND = 32;
+        final int countlg = 4;
+        int count = 1 << countlg;
+        int countsq = count * count;
+        float maxDD = Math.max(c.dbx / countsq, c.dby / countsq);
+        while (maxDD > QUAD_DEC_BND) {
+            maxDD /= 4;
             count <<= 1;
         }
-        // can only do this on even "count" values, because we must divide count by 2
-        while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
-            dx = 2 * dx + ddx;
-            dy = 2 * dy + ddy;
-            ddx = 4 * ddx;
-            ddy = 4 * ddy;
-            count >>= 1;
+
+        countsq = count * count;
+        final float ddx = c.dbx / countsq;
+        final float ddy = c.dby / countsq;
+        float dx = c.bx / countsq + c.cx / count;
+        float dy = c.by / countsq + c.cy / count;
+
+        while (count-- > 1) {
+            float x1 = x0 + dx;
+            dx += ddx;
+            float y1 = y0 + dy;
+            dy += ddy;
+            addLine(x0, y0, x1, y1);
+            x0 = x1;
+            y0 = y1;
         }
-        count--;
-        if (count > 0) {
-            quads[idx+X0] += dx;
-            dx += ddx;
-            quads[idx+Y0] += dy;
-            dy += ddy;
-        } else {
-            quads[idx+X0] = quads[idx+XL];
-            quads[idx+Y0] = quads[idx+YMAX];
-        }
-        quads[idx+COUNT] = count;
-        quads[idx+DDX] = ddx;
-        quads[idx+DDY] = ddy;
-        quads[idx+DX] = dx;
-        quads[idx+DY] = dy;
-        return count;
-    }
-    private int executeCurveAFDIteration(int idx) {
-        int count = (int)curves[idx+COUNT];
-        float ddx = curves[idx+DDX];
-        float ddy = curves[idx+DDY];
-        float dx = curves[idx+DX];
-        float dy = curves[idx+DY];
-        float dddx = curves[idx+DDDX];
-        float dddy = curves[idx+DDDY];
-
-        while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
-            dddx /= 8;
-            dddy /= 8;
-            ddx = ddx/4 - dddx;
-            ddy = ddy/4 - dddy;
-            dx = (dx - ddx) / 2;
-            dy = (dy - ddy) / 2;
-            count <<= 1;
-        }
-        // can only do this on even "count" values, because we must divide count by 2
-        while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
-            dx = 2 * dx + ddx;
-            dy = 2 * dy + ddy;
-            ddx = 4 * (ddx + dddx);
-            ddy = 4 * (ddy + dddy);
-            dddx = 8 * dddx;
-            dddy = 8 * dddy;
-            count >>= 1;
-        }
-        count--;
-        if (count > 0) {
-            curves[idx+X0] += dx;
-            dx += ddx;
-            ddx += dddx;
-            curves[idx+Y0] += dy;
-            dy += ddy;
-            ddy += dddy;
-        } else {
-            curves[idx+X0] = curves[idx+XL];
-            curves[idx+Y0] = curves[idx+YMAX];
-        }
-        curves[idx+COUNT] = count;
-        curves[idx+DDDX] = dddx;
-        curves[idx+DDDY] = dddy;
-        curves[idx+DDX] = ddx;
-        curves[idx+DDY] = ddy;
-        curves[idx+DX] = dx;
-        curves[idx+DY] = dy;
-        return count;
+        addLine(x0, y0, x2, y2);
     }
 
-
-    private void initLine(final int idx, float[] pts, int or) {
-        edges[idx+SLOPE] = (pts[2] - pts[0]) / (pts[3] - pts[1]);
-        edges[idx+CURX] = pts[0] + (edges[idx+CURY] - pts[1]) * edges[idx+SLOPE];
-    }
-
-    private void initQuad(final int idx, float[] points, int or) {
+    // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these
+    // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce
+    // numerical errors, and our callers already have the exact values.
+    // Another alternative would be to pass all the control points, and call c.set
+    // here, but then too many numbers are passed around.
+    private void curveBreakIntoLinesAndAdd(float x0, float y0,
+                                           final Curve c,
+                                           final float x3, final float y3) {
         final int countlg = 3;
-        final int count = 1 << countlg;
+        int count = 1 << countlg;
 
         // the dx and dy refer to forward differencing variables, not the last
         // coefficients of the "points" polynomial
-        final float ddx, ddy, dx, dy;
-        c.set(points, 6);
-
-        ddx = c.dbx / (1 << (2 * countlg));
-        ddy = c.dby / (1 << (2 * countlg));
-        dx = c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
-        dy = c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
-
-        quads[idx+DDX] = ddx;
-        quads[idx+DDY] = ddy;
-        quads[idx+DX] = dx;
-        quads[idx+DY] = dy;
-        quads[idx+COUNT] = count;
-        quads[idx+XL] = points[4];
-        quads[idx+X0] = points[0];
-        quads[idx+Y0] = points[1];
-        executeQuadAFDIteration(idx);
-        float x1 = quads[idx+X0], y1 = quads[idx+Y0];
-        quads[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]);
-        quads[idx+CURX] = points[0] + (quads[idx+CURY] - points[1])*quads[idx+CURSLOPE];
-    }
-
-    private void initCurve(final int idx, float[] points, int or) {
-        final int countlg = 3;
-        final int count = 1 << countlg;
-
-        // the dx and dy refer to forward differencing variables, not the last
-        // coefficients of the "points" polynomial
-        final float dddx, dddy, ddx, ddy, dx, dy;
-        c.set(points, 8);
+        float dddx, dddy, ddx, ddy, dx, dy;
         dddx = 2f * c.dax / (1 << (3 * countlg));
         dddy = 2f * c.day / (1 << (3 * countlg));
 
@@ -480,93 +219,100 @@
         dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
         dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
 
-        curves[idx+DDDX] = dddx;
-        curves[idx+DDDY] = dddy;
-        curves[idx+DDX] = ddx;
-        curves[idx+DDY] = ddy;
-        curves[idx+DX] = dx;
-        curves[idx+DY] = dy;
-        curves[idx+COUNT] = count;
-        curves[idx+XL] = points[6];
-        curves[idx+X0] = points[0];
-        curves[idx+Y0] = points[1];
-        executeCurveAFDIteration(idx);
-        float x1 = curves[idx+X0], y1 = curves[idx+Y0];
-        curves[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]);
-        curves[idx+CURX] = points[0] + (curves[idx+CURY] - points[1])*curves[idx+CURSLOPE];
-    }
-
-    private void addPathSegment(float[] pts, final int type, final int or) {
-        int idx;
-        float[] addTo;
-        switch (type) {
-        case 4:
-            idx = numEdges * SIZEOF_EDGE;
-            addTo = edges = Helpers.widenArray(edges, numEdges*SIZEOF_EDGE, SIZEOF_EDGE);
-            numEdges++;
-            break;
-        case 6:
-            idx = numQuads * SIZEOF_QUAD;
-            addTo = quads = Helpers.widenArray(quads, numQuads*SIZEOF_QUAD, SIZEOF_QUAD);
-            numQuads++;
-            break;
-        case 8:
-            idx = numCurves * SIZEOF_CURVE;
-            addTo = curves = Helpers.widenArray(curves, numCurves*SIZEOF_CURVE, SIZEOF_CURVE);
-            numCurves++;
-            break;
-        default:
-            throw new InternalError();
-        }
-        // set the common fields, except CURX, for which we must know the kind
-        // of curve. NOTE: this must be done before the type specific fields
-        // are initialized, because those depend on the common ones.
-        addTo[idx+YMIN] = pts[1];
-        addTo[idx+YMAX] = pts[type-1];
-        addTo[idx+OR] = or;
-        addTo[idx+CURY] = (float)Math.ceil(pts[1]);
-        switch (type) {
-        case 4:
-            initLine(idx, pts, or);
-            break;
-        case 6:
-            initQuad(idx, pts, or);
-            break;
-        case 8:
-            initCurve(idx, pts, or);
-            break;
-        default:
-            throw new InternalError();
+        // we use x0, y0 to walk the line
+        float x1 = x0, y1 = y0;
+        while (count > 0) {
+            while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
+                dddx /= 8;
+                dddy /= 8;
+                ddx = ddx/4 - dddx;
+                ddy = ddy/4 - dddy;
+                dx = (dx - ddx) / 2;
+                dy = (dy - ddy) / 2;
+                count <<= 1;
+            }
+            // can only do this on even "count" values, because we must divide count by 2
+            while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
+                dx = 2 * dx + ddx;
+                dy = 2 * dy + ddy;
+                ddx = 4 * (ddx + dddx);
+                ddy = 4 * (ddy + dddy);
+                dddx = 8 * dddx;
+                dddy = 8 * dddy;
+                count >>= 1;
+            }
+            count--;
+            if (count > 0) {
+                x1 += dx;
+                dx += ddx;
+                ddx += dddx;
+                y1 += dy;
+                dy += ddy;
+                ddy += dddy;
+            } else {
+                x1 = x3;
+                y1 = y3;
+            }
+            addLine(x0, y0, x1, y1);
+            x0 = x1;
+            y0 = y1;
         }
     }
 
-    // precondition: the curve in pts must be monotonic and increasing in y.
-    private void somethingTo(float[] pts, final int type, final int or) {
-        // NOTE: it's very important that we check for or >= 0 below (as
-        // opposed to or == 1, or or > 0, or anything else). That's
-        // because if we check for or==1, when the curve being added
-        // is a horizontal line, or will be 0 so or==1 will be false and
-        // x0 and y0 will be updated to pts[0] and pts[1] instead of pts[type-2]
-        // and pts[type-1], which is the correct thing to do.
-        this.x0 = or >= 0 ? pts[type - 2] : pts[0];
-        this.y0 = or >= 0 ? pts[type - 1] : pts[1];
-
-        float minY = pts[1], maxY = pts[type - 1];
-        if (Math.ceil(minY) >= Math.ceil(maxY) ||
-            Math.ceil(minY) >= boundsMaxY || maxY < boundsMinY)
-        {
+    // Preconditions: y2 > y1 and the curve must cross some scanline
+    // i.e.: y1 <= y < y2 for some y such that boundsMinY <= y < boundsMaxY
+    private void addLine(float x1, float y1, float x2, float y2) {
+        float or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
+        if (y2 < y1) {
+            or = y2; // no need to declare a temp variable. We have or.
+            y2 = y1;
+            y1 = or;
+            or = x2;
+            x2 = x1;
+            x1 = or;
+            or = 0;
+        }
+        final int firstCrossing = Math.max((int) Math.ceil(y1), boundsMinY);
+        final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY);
+        if (firstCrossing >= lastCrossing) {
             return;
         }
 
-        if (minY < edgeMinY) { edgeMinY = minY; }
-        if (maxY > edgeMaxY) { edgeMaxY = maxY; }
+        if (y1 < edgeMinY) { edgeMinY = y1; }
+        if (y2 > edgeMaxY) { edgeMaxY = y2; }
 
-        int minXidx = (pts[0] < pts[type-2] ? 0 : type - 2);
-        float minX = pts[minXidx];
-        float maxX = pts[type - 2 - minXidx];
-        if (minX < edgeMinX) { edgeMinX = minX; }
-        if (maxX > edgeMaxX) { edgeMaxX = maxX; }
-        addPathSegment(pts, type, or);
+        final float slope = (x2 - x1) / (y2 - y1);
+
+        if (slope > 0) { // <==> x1 < x2
+            if (x1 < edgeMinX) { edgeMinX = x1; }
+            if (x2 > edgeMaxX) { edgeMaxX = x2; }
+        } else {
+            if (x2 < edgeMinX) { edgeMinX = x2; }
+            if (x1 > edgeMaxX) { edgeMaxX = x1; }
+        }
+
+        final int ptr = numEdges * SIZEOF_EDGE;
+        edges = Helpers.widenArray(edges, ptr, SIZEOF_EDGE);
+        numEdges++;
+        edges[ptr+OR] = or;
+        edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope;
+        edges[ptr+SLOPE] = slope;
+        edges[ptr+YMAX] = y2;
+        final int bucketIdx = firstCrossing - boundsMinY;
+        addEdgeToBucket(ptr, bucketIdx);
+        if (lastCrossing < boundsMaxY) {
+            edgeBucketCounts[lastCrossing - boundsMinY] |= 1;
+        }
+    }
+
+    // preconditions: should not be called before the last line has been added
+    // to the edge list (even though it will return a correct answer at that
+    // point in time, it's not meant to be used that way).
+    private int getFirstScanLineCrossing() {
+        return Math.max(boundsMinY, (int)Math.ceil(edgeMinY));
+    }
+    private int getScanLineCrossingEnd() {
+        return Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY));
     }
 
 // END EDGE LIST
@@ -619,6 +365,10 @@
         this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y;
         this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X;
         this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y;
+
+        edgeBuckets = new int[boundsMaxY - boundsMinY];
+        java.util.Arrays.fill(edgeBuckets, NULL);
+        edgeBucketCounts = new int[edgeBuckets.length];
     }
 
     private float tosubpixx(float pix_x) {
@@ -636,74 +386,34 @@
         this.x0 = tosubpixx(pix_x0);
     }
 
-    public void lineJoin() { /* do nothing */ }
-
-    private final float[][] pts = new float[2][8];
-    private final float[] ts = new float[4];
-
-    private static void invertPolyPoints(float[] pts, int off, int type) {
-        for (int i = off, j = off + type - 2; i < j; i += 2, j -= 2) {
-            float tmp = pts[i];
-            pts[i] = pts[j];
-            pts[j] = tmp;
-            tmp = pts[i+1];
-            pts[i+1] = pts[j+1];
-            pts[j+1] = tmp;
-        }
-    }
-
-    // return orientation before making the curve upright.
-    private static int makeMonotonicCurveUpright(float[] pts, int off, int type) {
-        float y0 = pts[off + 1];
-        float y1 = pts[off + type - 1];
-        if (y0 > y1) {
-            invertPolyPoints(pts, off, type);
-            return -1;
-        } else if (y0 < y1) {
-            return 1;
-        }
-        return 0;
-    }
-
     public void lineTo(float pix_x1, float pix_y1) {
-        pts[0][0] = x0; pts[0][1] = y0;
-        pts[0][2] = tosubpixx(pix_x1); pts[0][3] = tosubpixy(pix_y1);
-        int or = makeMonotonicCurveUpright(pts[0], 0, 4);
-        somethingTo(pts[0], 4, or);
+        float x1 = tosubpixx(pix_x1);
+        float y1 = tosubpixy(pix_y1);
+        addLine(x0, y0, x1, y1);
+        x0 = x1;
+        y0 = y1;
     }
 
     Curve c = new Curve();
-    private void curveOrQuadTo(int type) {
-        c.set(pts[0], type);
-        int numTs = c.dxRoots(ts, 0);
-        numTs += c.dyRoots(ts, numTs);
-        numTs = Helpers.filterOutNotInAB(ts, 0, numTs, 0, 1);
-        Helpers.isort(ts, 0, numTs);
-
-        Iterator<float[]> it = Curve.breakPtsAtTs(pts, type, ts, numTs);
-        while(it.hasNext()) {
-            float[] curCurve = it.next();
-            int or = makeMonotonicCurveUpright(curCurve, 0, type);
-            somethingTo(curCurve, type, or);
-        }
-    }
-
     @Override public void curveTo(float x1, float y1,
                                   float x2, float y2,
                                   float x3, float y3)
     {
-        pts[0][0] = x0; pts[0][1] = y0;
-        pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1);
-        pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2);
-        pts[0][6] = tosubpixx(x3); pts[0][7] = tosubpixy(y3);
-        curveOrQuadTo(8);
+        final float xe = tosubpixx(x3);
+        final float ye = tosubpixy(y3);
+        c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), tosubpixx(x2), tosubpixy(y2), xe, ye);
+        curveBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
+        x0 = xe;
+        y0 = ye;
     }
 
     @Override public void quadTo(float x1, float y1, float x2, float y2) {
-        pts[0][0] = x0; pts[0][1] = y0;
-        pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1);
-        pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2);
-        curveOrQuadTo(6);
+        final float xe = tosubpixx(x2);
+        final float ye = tosubpixy(y2);
+        c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+        quadBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
+        x0 = xe;
+        y0 = ye;
     }
 
     public void closePath() {
@@ -728,9 +438,9 @@
         // 0x1 if EVEN_ODD, all bits if NON_ZERO
         int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
 
-        // add 1 to better deal with the last pixel in a pixel row.
-        int width = pix_bboxx1 - pix_bboxx0 + 1;
-        int[] alpha = new int[width+1];
+        // add 2 to better deal with the last pixel in a pixel row.
+        int width = pix_bboxx1 - pix_bboxx0;
+        int[] alpha = new int[width+2];
 
         int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X;
         int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X;
@@ -766,7 +476,8 @@
             for (int i = 0; i < numCrossings; i++) {
                 int curxo = crossings[i];
                 int curx = curxo >> 1;
-                int crorientation = ((curxo & 0x1) == 0x1) ? 1 : -1;
+                // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
+                int crorientation = ((curxo & 0x1) << 1) -1;
                 if ((sum & mask) != 0) {
                     int x0 = Math.max(prev, bboxx0);
                     int x1 = Math.min(curx, bboxx1);
@@ -811,26 +522,26 @@
     }
 
     public void endRendering() {
-        final int bminx = boundsMinX >> SUBPIXEL_LG_POSITIONS_X;
-        final int bmaxx = boundsMaxX >> SUBPIXEL_LG_POSITIONS_X;
-        final int bminy = boundsMinY >> SUBPIXEL_LG_POSITIONS_Y;
-        final int bmaxy = boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y;
-        final int eminx = ((int)Math.floor(edgeMinX)) >> SUBPIXEL_LG_POSITIONS_X;
-        final int emaxx = ((int)Math.ceil(edgeMaxX)) >> SUBPIXEL_LG_POSITIONS_X;
-        final int eminy = ((int)Math.floor(edgeMinY)) >> SUBPIXEL_LG_POSITIONS_Y;
-        final int emaxy = ((int)Math.ceil(edgeMaxY)) >> SUBPIXEL_LG_POSITIONS_Y;
+        int spminX = Math.max((int)Math.ceil(edgeMinX), boundsMinX);
+        int spmaxX = Math.min((int)Math.ceil(edgeMaxX), boundsMaxX);
+        int spminY = Math.max((int)Math.ceil(edgeMinY), boundsMinY);
+        int spmaxY = Math.min((int)Math.ceil(edgeMaxY), boundsMaxY);
 
-        final int minX = Math.max(bminx, eminx);
-        final int maxX = Math.min(bmaxx, emaxx);
-        final int minY = Math.max(bminy, eminy);
-        final int maxY = Math.min(bmaxy, emaxy);
-        if (minX > maxX || minY > maxY) {
-            this.cache = new PiscesCache(bminx, bminy, bmaxx, bmaxy);
+        int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
+        int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
+        int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
+        int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
+
+        if (pminX > pmaxX || pminY > pmaxY) {
+            this.cache = new PiscesCache(boundsMinX >> SUBPIXEL_LG_POSITIONS_X,
+                                         boundsMinY >> SUBPIXEL_LG_POSITIONS_Y,
+                                         boundsMaxX >> SUBPIXEL_LG_POSITIONS_X,
+                                         boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y);
             return;
         }
 
-        this.cache = new PiscesCache(minX, minY, maxX, maxY);
-        _endRendering(minX, minY, maxX, maxY);
+        this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY);
+        _endRendering(pminX, pminY, pmaxX, pmaxY);
     }
 
     public PiscesCache getCache() {
--- a/src/share/classes/sun/java2d/pisces/Stroker.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/Stroker.java	Tue Feb 08 09:22:49 2011 -0500
@@ -33,7 +33,7 @@
 // TODO: some of the arithmetic here is too verbose and prone to hard to
 // debug typos. We should consider making a small Point/Vector class that
 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
-public class Stroker implements PathConsumer2D {
+final class Stroker implements PathConsumer2D {
 
     private static final int MOVE_TO = 0;
     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
@@ -130,7 +130,7 @@
     private static void computeOffset(final float lx, final float ly,
                                       final float w, final float[] m)
     {
-        final float len = (float)Math.hypot(lx, ly);
+        final float len = (float)Math.sqrt(lx*lx + ly*ly);
         if (len == 0) {
             m[0] = m[1] = 0;
         } else {
@@ -758,7 +758,7 @@
     // This is where the curve to be processed is put. We give it
     // enough room to store 2 curves: one for the current subdivision, the
     // other for the rest of the curve.
-    private float[][] middle = new float[2][8];
+    private float[] middle = new float[2*8];
     private float[] lp = new float[8];
     private float[] rp = new float[8];
     private static final int MAX_N_CURVES = 11;
@@ -766,55 +766,55 @@
 
     private void somethingTo(final int type) {
         // need these so we can update the state at the end of this method
-        final float xf = middle[0][type-2], yf = middle[0][type-1];
-        float dxs = middle[0][2] - middle[0][0];
-        float dys = middle[0][3] - middle[0][1];
-        float dxf = middle[0][type - 2] - middle[0][type - 4];
-        float dyf = middle[0][type - 1] - middle[0][type - 3];
+        final float xf = middle[type-2], yf = middle[type-1];
+        float dxs = middle[2] - middle[0];
+        float dys = middle[3] - middle[1];
+        float dxf = middle[type - 2] - middle[type - 4];
+        float dyf = middle[type - 1] - middle[type - 3];
         switch(type) {
         case 6:
             if ((dxs == 0f && dys == 0f) ||
                 (dxf == 0f && dyf == 0f)) {
-               dxs = dxf = middle[0][4] - middle[0][0];
-               dys = dyf = middle[0][5] - middle[0][1];
+               dxs = dxf = middle[4] - middle[0];
+               dys = dyf = middle[5] - middle[1];
             }
             break;
         case 8:
             boolean p1eqp2 = (dxs == 0f && dys == 0f);
             boolean p3eqp4 = (dxf == 0f && dyf == 0f);
             if (p1eqp2) {
-                dxs = middle[0][4] - middle[0][0];
-                dys = middle[0][5] - middle[0][1];
+                dxs = middle[4] - middle[0];
+                dys = middle[5] - middle[1];
                 if (dxs == 0f && dys == 0f) {
-                    dxs = middle[0][6] - middle[0][0];
-                    dys = middle[0][7] - middle[0][1];
+                    dxs = middle[6] - middle[0];
+                    dys = middle[7] - middle[1];
                 }
             }
             if (p3eqp4) {
-                dxf = middle[0][6] - middle[0][2];
-                dyf = middle[0][7] - middle[0][3];
+                dxf = middle[6] - middle[2];
+                dyf = middle[7] - middle[3];
                 if (dxf == 0f && dyf == 0f) {
-                    dxf = middle[0][6] - middle[0][0];
-                    dyf = middle[0][7] - middle[0][1];
+                    dxf = middle[6] - middle[0];
+                    dyf = middle[7] - middle[1];
                 }
             }
         }
         if (dxs == 0f && dys == 0f) {
             // this happens iff the "curve" is just a point
-            lineTo(middle[0][0], middle[0][1]);
+            lineTo(middle[0], middle[1]);
             return;
         }
         // if these vectors are too small, normalize them, to avoid future
         // precision problems.
         if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
-            double len = Math.hypot(dxs, dys);
-            dxs = (float)(dxs / len);
-            dys = (float)(dys / len);
+            float len = (float)Math.sqrt(dxs*dxs + dys*dys);
+            dxs /= len;
+            dys /= len;
         }
         if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
-            double len = Math.hypot(dxf, dyf);
-            dxf = (float)(dxf / len);
-            dyf = (float)(dyf / len);
+            float len = (float)Math.sqrt(dxf*dxf + dyf*dyf);
+            dxf /= len;
+            dyf /= len;
         }
 
         computeOffset(dxs, dys, lineWidth2, offset[0]);
@@ -822,20 +822,20 @@
         final float my = offset[0][1];
         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
 
-        int nSplits = findSubdivPoints(middle[0], subdivTs, type,lineWidth2);
+        int nSplits = findSubdivPoints(middle, subdivTs, type, lineWidth2);
 
         int kind = 0;
-        Iterator<float[]> it = Curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
+        Iterator<Integer> it = Curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
         while(it.hasNext()) {
-            float[] curCurve = it.next();
+            int curCurveOff = it.next();
 
             kind = 0;
             switch (type) {
             case 8:
-                kind = computeOffsetCubic(curCurve, 0, lp, rp);
+                kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
                 break;
             case 6:
-                kind = computeOffsetQuad(curCurve, 0, lp, rp);
+                kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
                 break;
             }
             if (kind != 0) {
@@ -871,8 +871,7 @@
     // to get good offset curves a distance of w away from the middle curve.
     // Stores the points in ts, and returns how many of them there were.
     private static Curve c = new Curve();
-    private static int findSubdivPoints(float[] pts, float[] ts,
-                                        final int type, final float w)
+    private static int findSubdivPoints(float[] pts, float[] ts, final int type, final float w)
     {
         final float x12 = pts[2] - pts[0];
         final float y12 = pts[3] - pts[1];
@@ -919,6 +918,7 @@
         // now we must subdivide at points where one of the offset curves will have
         // a cusp. This happens at ts where the radius of curvature is equal to w.
         ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
+
         ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
         Helpers.isort(ts, 0, ret);
         return ret;
@@ -928,10 +928,10 @@
                                   float x2, float y2,
                                   float x3, float y3)
     {
-        middle[0][0] = cx0; middle[0][1] = cy0;
-        middle[0][2] = x1; middle[0][3] = y1;
-        middle[0][4] = x2; middle[0][5] = y2;
-        middle[0][6] = x3; middle[0][7] = y3;
+        middle[0] = cx0; middle[1] = cy0;
+        middle[2] = x1;  middle[3] = y1;
+        middle[4] = x2;  middle[5] = y2;
+        middle[6] = x3;  middle[7] = y3;
         somethingTo(8);
     }
 
@@ -940,9 +940,9 @@
     }
 
     @Override public void quadTo(float x1, float y1, float x2, float y2) {
-        middle[0][0] = cx0; middle[0][1] = cy0;
-        middle[0][2] = x1; middle[0][3] = y1;
-        middle[0][4] = x2; middle[0][5] = y2;
+        middle[0] = cx0; middle[1] = cy0;
+        middle[2] = x1;  middle[3] = y1;
+        middle[4] = x2;  middle[5] = y2;
         somethingTo(6);
     }
 
--- a/src/share/classes/sun/java2d/pisces/TransformingPathConsumer2D.java	Thu Feb 03 19:15:30 2011 -0800
+++ b/src/share/classes/sun/java2d/pisces/TransformingPathConsumer2D.java	Tue Feb 08 09:22:49 2011 -0500
@@ -28,7 +28,7 @@
 import sun.awt.geom.PathConsumer2D;
 import java.awt.geom.AffineTransform;
 
-public class TransformingPathConsumer2D {
+final class TransformingPathConsumer2D {
     public static PathConsumer2D
         transformConsumer(PathConsumer2D out,
                           AffineTransform at)
@@ -50,17 +50,72 @@
                     return new TranslateFilter(out, Mxt, Myt);
                 }
             } else {
-                return new ScaleFilter(out, Mxx, Myy, Mxt, Myt);
+                if (Mxt == 0f && Myt == 0f) {
+                    return new DeltaScaleFilter(out, Mxx, Myy);
+                } else {
+                    return new ScaleFilter(out, Mxx, Myy, Mxt, Myt);
+                }
             }
+        } else if (Mxt == 0f && Myt == 0f) {
+            return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy);
         } else {
             return new TransformFilter(out, Mxx, Mxy, Mxt, Myx, Myy, Myt);
         }
     }
 
-    static class TranslateFilter implements PathConsumer2D {
-        PathConsumer2D out;
-        float tx;
-        float ty;
+    public static PathConsumer2D
+        deltaTransformConsumer(PathConsumer2D out,
+                               AffineTransform at)
+    {
+        if (at == null) {
+            return out;
+        }
+        float Mxx = (float) at.getScaleX();
+        float Mxy = (float) at.getShearX();
+        float Myx = (float) at.getShearY();
+        float Myy = (float) at.getScaleY();
+        if (Mxy == 0f && Myx == 0f) {
+            if (Mxx == 1f && Myy == 1f) {
+                return out;
+            } else {
+                return new DeltaScaleFilter(out, Mxx, Myy);
+            }
+        } else {
+            return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy);
+        }
+    }
+
+    public static PathConsumer2D
+        inverseDeltaTransformConsumer(PathConsumer2D out,
+                                      AffineTransform at)
+    {
+        if (at == null) {
+            return out;
+        }
+        float Mxx = (float) at.getScaleX();
+        float Mxy = (float) at.getShearX();
+        float Myx = (float) at.getShearY();
+        float Myy = (float) at.getScaleY();
+        if (Mxy == 0f && Myx == 0f) {
+            if (Mxx == 1f && Myy == 1f) {
+                return out;
+            } else {
+                return new DeltaScaleFilter(out, 1.0f/Mxx, 1.0f/Myy);
+            }
+        } else {
+            float det = Mxx * Myy - Mxy * Myx;
+            return new DeltaTransformFilter(out,
+                                            Myy / det,
+                                            -Mxy / det,
+                                            -Myx / det,
+                                            Mxx / det);
+        }
+    }
+
+    static final class TranslateFilter implements PathConsumer2D {
+        private final PathConsumer2D out;
+        private final float tx;
+        private final float ty;
 
         TranslateFilter(PathConsumer2D out,
                         float tx, float ty)
@@ -107,12 +162,12 @@
         }
     }
 
-    static class ScaleFilter implements PathConsumer2D {
-        PathConsumer2D out;
-        float sx;
-        float sy;
-        float tx;
-        float ty;
+    static final class ScaleFilter implements PathConsumer2D {
+        private final PathConsumer2D out;
+        private final float sx;
+        private final float sy;
+        private final float tx;
+        private final float ty;
 
         ScaleFilter(PathConsumer2D out,
                     float sx, float sy, float tx, float ty)
@@ -161,14 +216,14 @@
         }
     }
 
-    static class TransformFilter implements PathConsumer2D {
-        PathConsumer2D out;
-        float Mxx;
-        float Mxy;
-        float Mxt;
-        float Myx;
-        float Myy;
-        float Myt;
+    static final class TransformFilter implements PathConsumer2D {
+        private final PathConsumer2D out;
+        private final float Mxx;
+        private final float Mxy;
+        private final float Mxt;
+        private final float Myx;
+        private final float Myy;
+        private final float Myt;
 
         TransformFilter(PathConsumer2D out,
                         float Mxx, float Mxy, float Mxt,
@@ -226,4 +281,113 @@
             return 0;
         }
     }
+
+    static final class DeltaScaleFilter implements PathConsumer2D {
+        private final float sx, sy;
+        private final PathConsumer2D out;
+
+        public DeltaScaleFilter(PathConsumer2D out, float Mxx, float Myy) {
+            sx = Mxx;
+            sy = Myy;
+            this.out = out;
+        }
+
+        public void moveTo(float x0, float y0) {
+            out.moveTo(x0 * sx, y0 * sy);
+        }
+
+        public void lineTo(float x1, float y1) {
+            out.lineTo(x1 * sx, y1 * sy);
+        }
+
+        public void quadTo(float x1, float y1,
+                           float x2, float y2)
+        {
+            out.quadTo(x1 * sx, y1 * sy,
+                       x2 * sx, y2 * sy);
+        }
+
+        public void curveTo(float x1, float y1,
+                            float x2, float y2,
+                            float x3, float y3)
+        {
+            out.curveTo(x1 * sx, y1 * sy,
+                        x2 * sx, y2 * sy,
+                        x3 * sx, y3 * sy);
+        }
+
+        public void closePath() {
+            out.closePath();
+        }
+
+        public void pathDone() {
+            out.pathDone();
+        }
+
+        public long getNativeConsumer() {
+            return 0;
+        }
+    }
+
+    static final class DeltaTransformFilter implements PathConsumer2D {
+        private PathConsumer2D out;
+        private final float Mxx;
+        private final float Mxy;
+        private final float Myx;
+        private final float Myy;
+
+        DeltaTransformFilter(PathConsumer2D out,
+                             float Mxx, float Mxy,
+                             float Myx, float Myy)
+        {
+            this.out = out;
+            this.Mxx = Mxx;
+            this.Mxy = Mxy;
+            this.Myx = Myx;
+            this.Myy = Myy;
+        }
+
+        public void moveTo(float x0, float y0) {
+            out.moveTo(x0 * Mxx + y0 * Mxy,
+                       x0 * Myx + y0 * Myy);
+        }
+
+        public void lineTo(float x1, float y1) {
+            out.lineTo(x1 * Mxx + y1 * Mxy,
+                       x1 * Myx + y1 * Myy);
+        }
+
+        public void quadTo(float x1, float y1,
+                           float x2, float y2)
+        {
+            out.quadTo(x1 * Mxx + y1 * Mxy,
+                       x1 * Myx + y1 * Myy,
+                       x2 * Mxx + y2 * Mxy,
+                       x2 * Myx + y2 * Myy);
+        }
+
+        public void curveTo(float x1, float y1,
+                            float x2, float y2,
+                            float x3, float y3)
+        {
+            out.curveTo(x1 * Mxx + y1 * Mxy,
+                        x1 * Myx + y1 * Myy,
+                        x2 * Mxx + y2 * Mxy,
+                        x2 * Myx + y2 * Myy,
+                        x3 * Mxx + y3 * Mxy,
+                        x3 * Myx + y3 * Myy);
+        }
+
+        public void closePath() {
+            out.closePath();
+        }
+
+        public void pathDone() {
+            out.pathDone();
+        }
+
+        public long getNativeConsumer() {
+            return 0;
+        }
+    }
 }