Request for review: cleanup in preparation of the final java2d backport.

Dr Andrew John Hughes ahughes at redhat.com
Thu Feb 24 16:40:37 PST 2011


On 16:42 Thu 24 Feb     , Denis Lila wrote:
> This is the final java2d backport I wanted to do, and
> it is the reason for the 3 previous patches
> 
> I'm testing it right now, and 30 minutes into the build
> everything is fine (with the previous 3 patches applied
> (in the order they were posted) to revision 6e7f2c1f25df
> of head). I'll have to do a merge :-(.
> 

Looks ok.

> Regards,
> Denis.
> 
> ----- Original Message -----
> > And the attached file is the final part of the cleanup.
> > It just removes 3 patches that were obsoleted by the
> > previous changeset (if the previous changest is approved
> > for pushing, that is).
> > 
> > Ok to push?
> > 
> > Thank you,
> > Denis.
> > 
> > ----- Original Message -----
> > > This:
> > > http://icedtea.classpath.org/~dlila/cleanupPart2.patch
> > > is the second part of the clean up. It removes
> > > patches/openjdk/6967436-6976265-6967434-pisces.patch
> > > (which we stopped applying in the previous cleanupPart1.patch),
> > > it adds the 3 replacement patches for it, it adds
> > > piscesMakefile.patch to make the build work, and it reapplies
> > > patches/openjdk/6766342-AA-simple-shape-performance.patch.
> > >
> > > It also stops applying 3 patches made obsolete by the 3 new
> > > patches (but doesn't remove them (should it?). I've left this
> > > for the next changeset).
> > >
> > > Ok to push (contingent on cleanupPart1.patch being ok)?
> > >
> > > Thank you,
> > > Denis.
> > >
> > > ----- Original Message -----
> > > > > The IcedTea version:
> > > > >
> > > > > 2008-10-27 Mark Wielaard <mark at klomp.org>
> > > > >
> > > > > * patches/icedtea-renderer-crossing.patch: New patch.
> > > > >
> > > > > predates the OpenJDK one:
> > > > >
> > > > > changeset: 1878:ccc36189f2a7
> > > > > user: rkennke
> > > > > date: Mon Oct 05 23:12:22 2009 +0200
> > > > >
> > > > > by just under a year. Roman may even have been using the IcedTea
> > > > > patch
> > > > > and not bothered
> > > > > to report to us.
> > > > >
> > > > > Anyway, good to be replaced in a separate changeset.
> > > >
> > > > The attached patch does the replacement of
> > > > patches/renderer-crossings.patch. I do this first because it came
> > > > before any of the other things, and the other patches won't apply
> > > > without it.
> > > >
> > > > Unfortunately, it also has to stop applying
> > > > patches/openjdk/6967436-6976265-6967434-pisces.patch
> > > > patches/openjdk/6766342-AA-simple-shape-performance.patch
> > > > If we don't stop applying the first of those, the patching will
> > > > fail. It's not a good idea to apply the second because it changes
> > > > a file that is also changed by
> > > > 6967436-6976265-6967434-pisces.patch.
> > > > So until the replacement for 6967436-6976265-6967434-pisces.patch
> > > > is
> > > > in, we shouldn't apply it.
> > > >
> > > > Ok to push?
> > > >
> > > > The next changeset after this will introduce the three
> > > > replacements
> > > > of
> > > > patches/openjdk/6967436-6976265-6967434-pisces.patch.
> > > >
> > > > Thank you.
> > > >
> > > > ----- Original Message -----
> > > > > On 12:45 Thu 24 Feb , Denis Lila wrote:
> > > > > > Hi.
> > > > > >
> > > > > > I want to push this patch:
> > > > > > http://icedtea.classpath.org/~dlila/hgCleanup.diff. I tried
> > > > > > attaching it,
> > > > > > but it was too big and I cancelled the message.
> > > > > >
> > > > >
> > > > > Ok let's split this up and do them in individual changesets with
> > > > > individual
> > > > > ChangeLogs. It's much easier to track things that way if we're
> > > > > not
> > > > > mixing
> > > > > up orthogonal things.
> > > > >
> > > > > > The changes are:
> > > > > > patches/openjdk/6967436-6976265-6967434-pisces.patch was
> > > > > > replaced
> > > > > > by
> > > > > > 6967436-6967433-floating-pt-conversion.patch,
> > > > > > 6967434-bad-round-joins.patch,
> > > > > > and 6976265-stroke-control.patch. Each of the separate patches
> > > > > > is
> > > > > > its own
> > > > > > changeset in openjdk7, so it was my mistake to begin with to
> > > > > > lump
> > > > > > them
> > > > > > all into one backport. Splitting it up into the 3 patches
> > > > > > fixes
> > > > > > it.
> > > > > > Also
> > > > > > each of the three patches is an unmodified "hg export" of its
> > > > > > openjdk7
> > > > > > revision. What we had in 6967436-6976265-6967434-pisces.patch
> > > > > > wasn't,
> > > > > > because I had to remove some of the @Override annotations so
> > > > > > that
> > > > > > the
> > > > > > build wouldn't fail. The proper way to get around this is by
> > > > > > changing
> > > > > > the source and target options to ecj, which is what
> > > > > > patches/piscesMakefile.patch does.
> > > > > >
> > > > >
> > > > > Ok so this is one patch: the three patch split + the
> > > > > piscesMakefile
> > > > > to
> > > > > make
> > > > > it work.
> > > > >
> > > > > > I replaced patches/renderer-crossing.patch with
> > > > > > patches/openjdk/6887494-NPE-in-pisces.patch. The former adds
> > > > > >     if (crossingIndices != null && crossingIndices.length >
> > > > > >     DEFAULT_INDICES_SIZE) {
> > > > > > while the latter adds
> > > > > >     if (crossingIndices != null &&
> > > > > >         crossingIndices.length > DEFAULT_INDICES_SIZE)
> > > > > >     {
> > > > > > so they do exactly the same thing, but the layout is
> > > > > > different.
> > > > > > What
> > > > > > I think
> > > > > > happened is that patches/renderer-crossing.patch was added to
> > > > > > icedtea before
> > > > > > openjdk7, and when it went into openjdk7 the newlines were
> > > > > > changed.
> > > > > > So now
> > > > > > I'm replacing the patch with a proper backport.
> > > > > >
> > > > >
> > > >
> > > > >
> > > > > > The other changes are just removing obsolete patches, and
> > > > > > they're
> > > > > > described
> > > > > > in the ChangeLog.
> > > > > >
> > > > >
> > > > > Again, separate changeset.
> > > > >
> > > > > > Thank you,
> > > > > > Denis.
> > > > >
> > > > > --
> > > > > Andrew :)
> > > > >
> > > > > Free Java Software Engineer
> > > > > Red Hat, Inc. (http://www.redhat.com)
> > > > >
> > > > > Support Free Java!
> > > > > Contribute to GNU Classpath and IcedTea
> > > > > http://www.gnu.org/software/classpath
> > > > > http://icedtea.classpath.org
> > > > > PGP Key: F5862A37 (https://keys.indymedia.org/)
> > > > > Fingerprint = EA30 D855 D50F 90CD F54D 0698 0713 C3ED F586 2A37

> diff -r 3179d5bf9efe ChangeLog
> --- a/ChangeLog	Thu Feb 24 16:15:58 2011 -0500
> +++ b/ChangeLog	Thu Feb 24 16:33:33 2011 -0500
> @@ -1,3 +1,12 @@
> +2011-02-24  Denis Lila  <dlila at redhat.com>
> +
> +	* Makefile.am:
> +	Added patch.
> +	* NEWS:
> +	Added backport entry.
> +	* patches/openjdk/7016856-pisces-performance.patch:
> +	Backport.
> +
>  2011-02-24  Denis Lila  <dlila at redhat.com>
>  
>  	* patches/java2d-stroker-internal-close-joint.patch:
> diff -r 3179d5bf9efe Makefile.am
> --- a/Makefile.am	Thu Feb 24 16:15:58 2011 -0500
> +++ b/Makefile.am	Thu Feb 24 16:33:33 2011 -0500
> @@ -325,7 +325,8 @@
>  	patches/openjdk/6967436-6967433-floating-pt-conversion.patch \
>  	patches/openjdk/6976265-stroke-control.patch \
>  	patches/openjdk/6967434-bad-round-joins.patch \
> -	patches/openjdk/6766342-AA-simple-shape-performance.patch
> +	patches/openjdk/6766342-AA-simple-shape-performance.patch \
> +	patches/openjdk/7016856-pisces-performance.patch
>  
>  if !WITH_ALT_HSBUILD
>  ICEDTEA_PATCHES += \
> diff -r 3179d5bf9efe NEWS
> --- a/NEWS	Thu Feb 24 16:15:58 2011 -0500
> +++ b/NEWS	Thu Feb 24 16:33:33 2011 -0500
> @@ -433,6 +433,7 @@
>    - S6444769: java/awt/Insets/WindowWithWarningTest/WindowWithWarningTest.html fails
>    - S6775317: Improve performance of non-AA transformed rectangles and single wide lines in software pipelines
>    - S6766342: Improve performance of Ductus rasterizer
> +  - S7016856: fix dashing performance regression. Improve other rendering performance.
>  * Bug fixes
>    - RH661505: JPEGs with sRGB IEC61966-2.1 color profiles have wrong colors
>    - PR600: HS19 upgrade broke CACAO build on ARM
> diff -r 3179d5bf9efe patches/openjdk/7016856-pisces-performance.patch
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/patches/openjdk/7016856-pisces-performance.patch	Thu Feb 24 16:33:33 2011 -0500
> @@ -0,0 +1,2116 @@
> +# HG changeset patch
> +# User dlila
> +# Date 1297174969 18000
> +# Node ID 5e624003e6225ec1ee92536bc356cba90043826c
> +# Parent  21621a756b32028fde37935cd91330a8e194f96b
> +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
> +
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/Curve.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Curve.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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);
> +-        }
> +-    }
> + }
> + 
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/Dasher.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Dasher.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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;
> +         }
> +     }
> + 
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/Helpers.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Helpers.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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,
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/PiscesCache.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesCache.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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;
> + 
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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));
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/Renderer.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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() {
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/Stroker.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Stroker.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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);
> +     }
> + 
> +diff -r 21621a756b32 -r 5e624003e622 src/share/classes/sun/java2d/pisces/TransformingPathConsumer2D.java
> +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/TransformingPathConsumer2D.java	Thu Feb 03 19:15:30 2011 -0800
> ++++ openjdk/jdk/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;
> ++        }
> ++    }
> + }


-- 
Andrew :)

Free Java Software Engineer
Red Hat, Inc. (http://www.redhat.com)

Support Free Java!
Contribute to GNU Classpath and IcedTea
http://www.gnu.org/software/classpath
http://icedtea.classpath.org
PGP Key: F5862A37 (https://keys.indymedia.org/)
Fingerprint = EA30 D855 D50F 90CD F54D  0698 0713 C3ED F586 2A37



More information about the distro-pkg-dev mailing list