6967436: lines longer than 2^15 can fill window.
authordlila
Tue, 10 Aug 2010 13:19:44 -0400
changeset 6284 695b3c6241c8
parent 6097 3b77573415af
child 6285 96a57de47def
6967436: lines longer than 2^15 can fill window. 6967433: dashed lines broken when using scaling transforms. Summary: converted pisces to floating point. Also, using better AA algorithm Reviewed-by: flar
jdk/src/share/classes/sun/java2d/pisces/Dasher.java
jdk/src/share/classes/sun/java2d/pisces/LineSink.java
jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java
jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
jdk/src/share/classes/sun/java2d/pisces/Renderer.java
jdk/src/share/classes/sun/java2d/pisces/Stroker.java
jdk/src/share/classes/sun/java2d/pisces/Transform4.java
--- a/jdk/src/share/classes/sun/java2d/pisces/Dasher.java	Thu Jul 29 17:12:27 2010 -0700
+++ b/jdk/src/share/classes/sun/java2d/pisces/Dasher.java	Tue Aug 10 13:19:44 2010 -0400
@@ -36,117 +36,71 @@
  * semantics are unclear.
  *
  */
-public class Dasher extends LineSink {
+public class Dasher implements LineSink {
+    private final LineSink output;
+    private final float[] dash;
+    private final float startPhase;
+    private final boolean startDashOn;
+    private final int startIdx;
 
-    LineSink output;
-    int[] dash;
-    int startPhase;
-    boolean startDashOn;
-    int startIdx;
-
-    int idx;
-    boolean dashOn;
-    int phase;
-
-    int sx, sy;
-    int x0, y0;
+    private final float m00, m10, m01, m11;
+    private final float det;
 
-    int m00, m01;
-    int m10, m11;
-
-    Transform4 transform;
-
-    boolean symmetric;
-    long ldet;
+    private boolean firstDashOn;
+    private boolean starting;
 
-    boolean firstDashOn;
-    boolean starting;
-    int sx1, sy1;
+    private int idx;
+    private boolean dashOn;
+    private float phase;
 
-    /**
-     * Empty constructor.  <code>setOutput</code> and
-     * <code>setParameters</code> must be called prior to calling any
-     * other methods.
-     */
-    public Dasher() {}
+    private float sx, sy;
+    private float x0, y0;
+    private float sx1, sy1;
+
 
     /**
      * Constructs a <code>Dasher</code>.
      *
      * @param output an output <code>LineSink</code>.
-     * @param dash an array of <code>int</code>s containing the dash
-     * pattern in S15.16 format.
-     * @param phase an <code>int</code> containing the dash phase in
-     * S15.16 format.
+     * @param dash an array of <code>int</code>s containing the dash pattern
+     * @param phase an <code>int</code> containing the dash phase
      * @param transform a <code>Transform4</code> object indicating
      * the transform that has been previously applied to all incoming
      * coordinates.  This is required in order to compute dash lengths
      * properly.
      */
     public Dasher(LineSink output,
-                  int[] dash, int phase,
-                  Transform4 transform) {
-        setOutput(output);
-        setParameters(dash, phase, transform);
-    }
-
-    /**
-     * Sets the output <code>LineSink</code> of this
-     * <code>Dasher</code>.
-     *
-     * @param output an output <code>LineSink</code>.
-     */
-    public void setOutput(LineSink output) {
-        this.output = output;
-    }
-
-    /**
-     * Sets the parameters of this <code>Dasher</code>.
-     *
-     * @param dash an array of <code>int</code>s containing the dash
-     * pattern in S15.16 format.
-     * @param phase an <code>int</code> containing the dash phase in
-     * S15.16 format.
-     * @param transform a <code>Transform4</code> object indicating
-     * the transform that has been previously applied to all incoming
-     * coordinates.  This is required in order to compute dash lengths
-     * properly.
-     */
-    public void setParameters(int[] dash, int phase,
-                              Transform4 transform) {
+                  float[] dash, float phase,
+                  float a00, float a01, float a10, float a11) {
         if (phase < 0) {
             throw new IllegalArgumentException("phase < 0 !");
         }
 
+        this.output = output;
+
         // Normalize so 0 <= phase < dash[0]
         int idx = 0;
         dashOn = true;
-        int d;
+        float d;
         while (phase >= (d = dash[idx])) {
             phase -= d;
             idx = (idx + 1) % dash.length;
             dashOn = !dashOn;
         }
 
-        this.dash = new int[dash.length];
-        for (int i = 0; i < dash.length; i++) {
-            this.dash[i] = dash[i];
-        }
+        this.dash = dash;
         this.startPhase = this.phase = phase;
         this.startDashOn = dashOn;
         this.startIdx = idx;
 
-        this.transform = transform;
-
-        this.m00 = transform.m00;
-        this.m01 = transform.m01;
-        this.m10 = transform.m10;
-        this.m11 = transform.m11;
-        this.ldet = ((long)m00*m11 - (long)m01*m10) >> 16;
-        this.symmetric = (m00 == m11 && m10 == -m01);
+        m00 = a00;
+        m01 = a01;
+        m10 = a10;
+        m11 = a11;
+        det = m00 * m11 - m01 * m10;
     }
 
-    public void moveTo(int x0, int y0) {
+    public void moveTo(float x0, float y0) {
         output.moveTo(x0, y0);
         this.idx = startIdx;
         this.dashOn = this.startDashOn;
@@ -160,7 +114,7 @@
         output.lineJoin();
     }
 
-    private void goTo(int x1, int y1) {
+    private void goTo(float x1, float y1) {
         if (dashOn) {
             if (starting) {
                 this.sx1 = x1;
@@ -180,52 +134,64 @@
         this.y0 = y1;
     }
 
-    public void lineTo(int x1, int y1) {
-        while (true) {
-            int d = dash[idx] - phase;
-            int lx = x1 - x0;
-            int ly = y1 - y0;
+    public void lineTo(float x1, float y1) {
+        // The widened line is squished to a 0 width one, so no drawing is done
+        if (det == 0) {
+            goTo(x1, y1);
+            return;
+        }
+        float dx = x1 - x0;
+        float dy = y1 - y0;
 
-            // Compute segment length in the untransformed
-            // coordinate system
-            // IMPL NOTE - use fixed point
+
+        // Compute segment length in the untransformed
+        // coordinate system
 
-            int l;
-            if (symmetric) {
-                l = (int)((PiscesMath.hypot(lx, ly)*65536L)/ldet);
-            } else{
-                long la = ((long)ly*m00 - (long)lx*m10)/ldet;
-                long lb = ((long)ly*m01 - (long)lx*m11)/ldet;
-                l = (int)PiscesMath.hypot(la, lb);
-            }
+        float la = (dy*m00 - dx*m10)/det;
+        float lb = (dy*m01 - dx*m11)/det;
+        float origLen = (float) Math.hypot(la, lb);
 
-            if (l < d) {
+        if (origLen == 0) {
+            // Let the output LineSink deal with cases where dx, dy are 0.
+            goTo(x1, y1);
+            return;
+        }
+
+        // The scaling factors needed to get the dx and dy of the
+        // transformed dash segments.
+        float cx = dx / origLen;
+        float cy = dy / origLen;
+
+        while (true) {
+            float leftInThisDashSegment = dash[idx] - phase;
+            if (origLen < leftInThisDashSegment) {
                 goTo(x1, y1);
                 // Advance phase within current dash segment
-                phase += l;
+                phase += origLen;
+                return;
+            } else if (origLen == leftInThisDashSegment) {
+                goTo(x1, y1);
+                phase = 0f;
+                idx = (idx + 1) % dash.length;
+                dashOn = !dashOn;
                 return;
             }
 
-            long t;
-            int xsplit, ysplit;
-//             // For zero length dashses, SE appears to move 1/8 unit
-//             // in device space
-//             if (d == 0) {
-//                 double dlx = lx/65536.0;
-//                 double dly = ly/65536.0;
-//                 len = PiscesMath.hypot(dlx, dly);
-//                 double dt = 1.0/(8*len);
-//                 double dxsplit = (x0/65536.0) + dt*dlx;
-//                 double dysplit = (y0/65536.0) + dt*dly;
-//                 xsplit = (int)(dxsplit*65536.0);
-//                 ysplit = (int)(dysplit*65536.0);
-//             } else {
-                t = ((long)d << 16)/l;
-                xsplit = x0 + (int)(t*(x1 - x0) >> 16);
-                ysplit = y0 + (int)(t*(y1 - y0) >> 16);
-//             }
-            goTo(xsplit, ysplit);
+            float dashx, dashy;
+            float dashdx = dash[idx] * cx;
+            float dashdy = dash[idx] * cy;
+            if (phase == 0) {
+                dashx = x0 + dashdx;
+                dashy = y0 + dashdy;
+            } else {
+                float p = (leftInThisDashSegment) / dash[idx];
+                dashx = x0 + p * dashdx;
+                dashy = y0 + p * dashdy;
+            }
 
+            goTo(dashx, dashy);
+
+            origLen -= (dash[idx] - phase);
             // Advance to next dash segment
             idx = (idx + 1) % dash.length;
             dashOn = !dashOn;
@@ -233,6 +199,7 @@
         }
     }
 
+
     public void close() {
         lineTo(sx, sy);
         if (firstDashOn) {
--- a/jdk/src/share/classes/sun/java2d/pisces/LineSink.java	Thu Jul 29 17:12:27 2010 -0700
+++ b/jdk/src/share/classes/sun/java2d/pisces/LineSink.java	Tue Aug 10 13:19:44 2010 -0400
@@ -39,16 +39,16 @@
  * <code>LineSink</code> interface.
  *
  */
-public abstract class LineSink {
+public interface LineSink {
 
     /**
      * Moves the current drawing position to the point <code>(x0,
      * y0)</code>.
      *
-     * @param x0 the X coordinate in S15.16 format
-     * @param y0 the Y coordinate in S15.16 format
+     * @param x0 the X coordinate
+     * @param y0 the Y coordinate
      */
-    public abstract void moveTo(int x0, int y0);
+    public void moveTo(float x0, float y0);
 
     /**
      * Provides a hint that the current segment should be joined to
@@ -65,29 +65,29 @@
      * <p> Other <code>LineSink</code> classes should simply pass this
      * hint to their output sink as needed.
      */
-    public abstract void lineJoin();
+    public void lineJoin();
 
     /**
      * Draws a line from the current drawing position to the point
      * <code>(x1, y1)</code> and sets the current drawing position to
      * <code>(x1, y1)</code>.
      *
-     * @param x1 the X coordinate in S15.16 format
-     * @param y1 the Y coordinate in S15.16 format
+     * @param x1 the X coordinate
+     * @param y1 the Y coordinate
      */
-    public abstract void lineTo(int x1, int y1);
+    public void lineTo(float x1, float y1);
 
     /**
      * Closes the current path by drawing a line from the current
      * drawing position to the point specified by the moset recent
      * <code>moveTo</code> command.
      */
-    public abstract void close();
+    public void close();
 
     /**
      * Ends the current path.  It may be necessary to end a path in
      * order to allow end caps to be drawn.
      */
-    public abstract void end();
+    public void end();
 
 }
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java	Thu Jul 29 17:12:27 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.java2d.pisces;
-
-public class PiscesMath {
-
-    private PiscesMath() {}
-
-    private static final int SINTAB_LG_ENTRIES = 10;
-    private static final int SINTAB_ENTRIES = 1 << SINTAB_LG_ENTRIES;
-    private static int[] sintab;
-
-    public static final int PI = (int)(Math.PI*65536.0);
-    public static final int TWO_PI = (int)(2.0*Math.PI*65536.0);
-    public static final int PI_OVER_TWO = (int)((Math.PI/2.0)*65536.0);
-    public static final int SQRT_TWO = (int)(Math.sqrt(2.0)*65536.0);
-
-    static {
-        sintab = new int[SINTAB_ENTRIES + 1];
-        for (int i = 0; i < SINTAB_ENTRIES + 1; i++) {
-            double theta = i*(Math.PI/2.0)/SINTAB_ENTRIES;
-            sintab[i] = (int)(Math.sin(theta)*65536.0);
-        }
-    }
-
-    public static int sin(int theta) {
-        int sign = 1;
-        if (theta < 0) {
-            theta = -theta;
-            sign = -1;
-        }
-        // 0 <= theta
-        while (theta >= TWO_PI) {
-            theta -= TWO_PI;
-        }
-        // 0 <= theta < 2*PI
-        if (theta >= PI) {
-            theta = TWO_PI - theta;
-            sign = -sign;
-        }
-        // 0 <= theta < PI
-        if (theta > PI_OVER_TWO) {
-            theta = PI - theta;
-        }
-        // 0 <= theta <= PI/2
-        int itheta = (int)((long)theta*SINTAB_ENTRIES/(PI_OVER_TWO));
-        return sign*sintab[itheta];
-    }
-
-    public static int cos(int theta) {
-        return sin(PI_OVER_TWO - theta);
-    }
-
-//     public static double sqrt(double x) {
-//         double dsqrt = Math.sqrt(x);
-//         int ix = (int)(x*65536.0);
-//         Int Isqrt = Isqrt(Ix);
-
-//         Long Lx = (Long)(X*65536.0);
-//         Long Lsqrt = Lsqrt(Lx);
-
-//         System.Out.Println();
-//         System.Out.Println("X = " + X);
-//         System.Out.Println("Dsqrt = " + Dsqrt);
-
-//         System.Out.Println("Ix = " + Ix);
-//         System.Out.Println("Isqrt = " + Isqrt/65536.0);
-
-//         System.Out.Println("Lx = " + Lx);
-//         System.Out.Println("Lsqrt = " + Lsqrt/65536.0);
-
-//         Return Dsqrt;
-//     }
-
-    // From Ken Turkowski, _Fixed-Point Square Root_, In Graphics Gems V
-    public static int isqrt(int x) {
-        int fracbits = 16;
-
-        int root = 0;
-        int remHi = 0;
-        int remLo = x;
-        int count = 15 + fracbits/2;
-
-        do {
-            remHi = (remHi << 2) | (remLo >>> 30); // N.B. - unsigned shift R
-            remLo <<= 2;
-            root <<= 1;
-            int testdiv = (root << 1) + 1;
-            if (remHi >= testdiv) {
-                remHi -= testdiv;
-                root++;
-            }
-        } while (count-- != 0);
-
-        return root;
-    }
-
-    public static long lsqrt(long x) {
-        int fracbits = 16;
-
-        long root = 0;
-        long remHi = 0;
-        long remLo = x;
-        int count = 31 + fracbits/2;
-
-        do {
-            remHi = (remHi << 2) | (remLo >>> 62); // N.B. - unsigned shift R
-            remLo <<= 2;
-            root <<= 1;
-            long testDiv = (root << 1) + 1;
-            if (remHi >= testDiv) {
-                remHi -= testDiv;
-                root++;
-            }
-        } while (count-- != 0);
-
-        return root;
-    }
-
-    public static double hypot(double x, double y) {
-        // new RuntimeException().printStackTrace();
-        return Math.sqrt(x*x + y*y);
-    }
-
-    public static int hypot(int x, int y) {
-        return (int)((lsqrt((long)x*x + (long)y*y) + 128) >> 8);
-    }
-
-    public static long hypot(long x, long y) {
-        return (lsqrt(x*x + y*y) + 128) >> 8;
-    }
-}
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Thu Jul 29 17:12:27 2010 -0700
+++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Tue Aug 10 13:19:44 2010 -0400
@@ -37,24 +37,8 @@
 import sun.java2d.pipe.AATileGenerator;
 
 public class PiscesRenderingEngine extends RenderingEngine {
-    public static Transform4 IdentT4 = new Transform4();
     public static double defaultFlat = 0.1;
 
-    static int FloatToS15_16(float flt) {
-        flt = flt * 65536f + 0.5f;
-        if (flt <= -(65536f * 65536f)) {
-            return Integer.MIN_VALUE;
-        } else if (flt >= (65536f * 65536f)) {
-            return Integer.MAX_VALUE;
-        } else {
-            return (int) Math.floor(flt);
-        }
-    }
-
-    static float S15_16ToFloat(int fix) {
-        return (fix / 65536f);
-    }
-
     /**
      * Create a widened path as specified by the parameters.
      * <p>
@@ -85,18 +69,19 @@
         strokeTo(src,
                  null,
                  width,
+                 false,
                  caps,
                  join,
                  miterlimit,
                  dashes,
                  dashphase,
                  new LineSink() {
-                     public void moveTo(int x0, int y0) {
-                         p2d.moveTo(S15_16ToFloat(x0), S15_16ToFloat(y0));
+                     public void moveTo(float x0, float y0) {
+                         p2d.moveTo(x0, y0);
                      }
                      public void lineJoin() {}
-                     public void lineTo(int x1, int y1) {
-                         p2d.lineTo(S15_16ToFloat(x1), S15_16ToFloat(y1));
+                     public void lineTo(float x1, float y1) {
+                         p2d.lineTo(x1, y1);
                      }
                      public void close() {
                          p2d.closePath();
@@ -144,12 +129,12 @@
     {
         strokeTo(src, at, bs, thin, normalize, antialias,
                  new LineSink() {
-                     public void moveTo(int x0, int y0) {
-                         consumer.moveTo(S15_16ToFloat(x0), S15_16ToFloat(y0));
+                     public void moveTo(float x0, float y0) {
+                         consumer.moveTo(x0, y0);
                      }
                      public void lineJoin() {}
-                     public void lineTo(int x1, int y1) {
-                         consumer.lineTo(S15_16ToFloat(x1), S15_16ToFloat(y1));
+                     public void lineTo(float x1, float y1) {
+                         consumer.lineTo(x1, y1);
                      }
                      public void close() {
                          consumer.closePath();
@@ -181,6 +166,7 @@
         strokeTo(src,
                  at,
                  lw,
+                 normalize,
                  bs.getEndCap(),
                  bs.getLineJoin(),
                  bs.getMiterLimit(),
@@ -258,6 +244,7 @@
     void strokeTo(Shape src,
                   AffineTransform at,
                   float width,
+                  boolean normalize,
                   int caps,
                   int join,
                   float miterlimit,
@@ -265,32 +252,16 @@
                   float dashphase,
                   LineSink lsink)
     {
-        Transform4 t4;
-
-        if (at == null || at.isIdentity()) {
-            t4 = IdentT4;
-        } else {
-            t4 = new Transform4(FloatToS15_16((float) at.getScaleX()),
-                                FloatToS15_16((float) at.getShearX()),
-                                FloatToS15_16((float) at.getShearY()),
-                                FloatToS15_16((float) at.getScaleY()));
+        float a00 = 1f, a01 = 0f, a10 = 0f, a11 = 1f;
+        if (at != null && !at.isIdentity()) {
+            a00 = (float)at.getScaleX();
+            a01 = (float)at.getShearX();
+            a10 = (float)at.getShearY();
+            a11 = (float)at.getScaleY();
         }
-
-        lsink = new Stroker(lsink,
-                            FloatToS15_16(width),
-                            caps,
-                            join,
-                            FloatToS15_16(miterlimit),
-                            t4);
+        lsink = new Stroker(lsink, width, caps, join, miterlimit, a00, a01, a10, a11);
         if (dashes != null) {
-            int fdashes[] = new int[dashes.length];
-            for (int i = 0; i < dashes.length; i++) {
-                fdashes[i] = FloatToS15_16(dashes[i]);
-            }
-            lsink = new Dasher(lsink,
-                               fdashes,
-                               FloatToS15_16(dashphase),
-                               t4);
+            lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11);
         }
 
         PathIterator pi = src.getPathIterator(at, defaultFlat);
@@ -302,13 +273,11 @@
         while (!pi.isDone()) {
             switch (pi.currentSegment(coords)) {
             case PathIterator.SEG_MOVETO:
-                lsink.moveTo(FloatToS15_16(coords[0]),
-                             FloatToS15_16(coords[1]));
+                lsink.moveTo(coords[0], coords[1]);
                 break;
             case PathIterator.SEG_LINETO:
                 lsink.lineJoin();
-                lsink.lineTo(FloatToS15_16(coords[0]),
-                             FloatToS15_16(coords[1]));
+                lsink.lineTo(coords[0], coords[1]);
                 break;
             case PathIterator.SEG_CLOSE:
                 lsink.lineJoin();
@@ -378,17 +347,19 @@
                                               int bbox[])
     {
         PiscesCache pc = PiscesCache.createInstance();
-        Renderer r = new Renderer();
-        r.setCache(pc);
-        r.setAntialiasing(3, 3);
-        r.beginRendering(clip.getLoX(), clip.getLoY(),
-                         clip.getWidth(), clip.getHeight());
+        Renderer r;
         if (bs == null) {
             PathIterator pi = s.getPathIterator(at, defaultFlat);
-            r.setWindingRule(pi.getWindingRule());
+            r = new Renderer(3, 3,
+                             clip.getLoX(), clip.getLoY(),
+                             clip.getWidth(), clip.getHeight(),
+                             pi.getWindingRule(), pc);
             pathTo(pi, r);
         } else {
-            r.setWindingRule(PathIterator.WIND_NON_ZERO);
+            r = new Renderer(3, 3,
+                             clip.getLoX(), clip.getLoY(),
+                             clip.getWidth(), clip.getHeight(),
+                             PathIterator.WIND_NON_ZERO, pc);
             strokeTo(s, at, bs, thin, normalize, true, r);
         }
         r.endRendering();
--- a/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Thu Jul 29 17:12:27 2010 -0700
+++ b/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Tue Aug 10 13:19:44 2010 -0400
@@ -25,446 +25,440 @@
 
 package sun.java2d.pisces;
 
-public class Renderer extends LineSink {
+import java.util.Arrays;
+
+public class Renderer implements LineSink {
+
+///////////////////////////////////////////////////////////////////////////////
+// Scan line iterator and edge crossing data.
+//////////////////////////////////////////////////////////////////////////////
+
+    private int[] crossings;
+
+    // This is an array of indices into the edge array. It is initialized to
+    // [i * SIZEOF_STRUCT_EDGE for i in range(0, edgesSize/SIZEOF_STRUCT_EDGE)]
+    // (where range(i, j) is i,i+1,...,j-1 -- just like in python).
+    // The reason for keeping this is because we need the edges array sorted
+    // by y0, but we don't want to move all that data around, so instead we
+    // sort the indices into the edge array, and use edgeIndices to access
+    // the edges array. This is meant to simulate a pointer array (hence the name)
+    private int[] edgePtrs;
+
+    // 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 int minX, maxX;
+    private int nextY;
+
+    // indices into the edge pointer list. They indicate the "active" sublist in
+    // the edge list (the portion of the list that contains all the edges that
+    // cross the next scan line).
+    private int lo, hi;
+
+    private static final int INIT_CROSSINGS_SIZE = 50;
+    private void ScanLineItInitialize() {
+        crossings = new int[INIT_CROSSINGS_SIZE];
+        edgePtrs = new int[edgesSize / SIZEOF_STRUCT_EDGE];
+        for (int i = 0; i < edgePtrs.length; i++) {
+            edgePtrs[i] = i * SIZEOF_STRUCT_EDGE;
+        }
+
+        qsort(0, edgePtrs.length - 1);
+
+        // 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 (lo = 0; lo < edgePtrs.length && edges[edgePtrs[lo]+Y1] <= nextY; lo++)
+            ;
+        for (hi = lo; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++)
+            ; // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive)
+        for (int i = lo; i < hi; i++) {
+            setCurY(edgePtrs[i], nextY);
+        }
+
+        // We accumulate X in the iterator because accumulating it in addEdge
+        // like we do with Y does not do much good: if there's an edge
+        // (0,0)->(1000,10000), and if y gets clipped to 1000, then the x
+        // bound should be 100, but the accumulator from addEdge would say 1000,
+        // so we'd still have to accumulate the X bounds as we add crossings.
+        minX = boundsMinX;
+        maxX = boundsMaxX;
+    }
+
+    private int ScanLineItCurrentY() {
+        return nextY - 1;
+    }
+
+    private int ScanLineItGoToNextYAndComputeCrossings() {
+        // we go through the active list and remove the ones that don't cross
+        // the nextY scanline.
+        int crossingIdx = 0;
+        for (int i = lo; i < hi; i++) {
+            if (edges[edgePtrs[i]+Y1] <= nextY) {
+                edgePtrs[i] = edgePtrs[lo++];
+            }
+        }
+        if (hi - lo > crossings.length) {
+            int newSize = Math.max(hi - lo, crossings.length * 2);
+            crossings = Arrays.copyOf(crossings, newSize);
+        }
+        // Now every edge between lo and hi crosses nextY. Compute it's
+        // crossing and put it in the crossings array.
+        for (int i = lo; i < hi; i++) {
+            addCrossing(nextY, getCurCrossing(edgePtrs[i]), (int)edges[edgePtrs[i]+OR], crossingIdx);
+            gotoNextY(edgePtrs[i]);
+            crossingIdx++;
+        }
+
+        nextY++;
+        // Expand active list to include new edges.
+        for (; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) {
+            setCurY(edgePtrs[hi], nextY);
+        }
+
+        Arrays.sort(crossings, 0, crossingIdx);
+        return crossingIdx;
+    }
+
+    private boolean ScanLineItHasNext() {
+        return nextY < maxY;
+    }
+
+    private void addCrossing(int y, int x, int or, int idx) {
+        if (x < minX) {
+            minX = x;
+        }
+        if (x > maxX) {
+            maxX = x;
+        }
+        x <<= 1;
+        crossings[idx] = ((or == 1) ? (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 void qsort(int first, int last) {
+        if (last > first) {
+            int p = partition(first, last);
+            if (first < p - 1) {
+                qsort(first, p - 1);
+            }
+            if (p < last) {
+                qsort(p, last);
+            }
+        }
+    }
+
+    // i, j are indices into edgePtrs.
+    private int partition(int i, int j) {
+        int pivotVal = edgePtrs[i];
+        while (i <= j) {
+            // edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C
+            while (edges[edgePtrs[i]+CURY] < edges[pivotVal+CURY]) { i++; }
+            while (edges[edgePtrs[j]+CURY] > edges[pivotVal+CURY]) { j--; }
+            if (i <= j) {
+                int tmp = edgePtrs[i];
+                edgePtrs[i] = edgePtrs[j];
+                edgePtrs[j] = tmp;
+                i++;
+                j--;
+            }
+        }
+        return i;
+    }
+
+//============================================================================
+
+
+//////////////////////////////////////////////////////////////////////////////
+//  EDGE LIST
+//////////////////////////////////////////////////////////////////////////////
+
+    private static final int INIT_NUM_EDGES = 1000;
+    private static final int SIZEOF_STRUCT_EDGE = 5;
+
+    // The following array is a poor man's struct array:
+    // it simulates a struct array by having
+    // edges[SIZEOF_STRUCT_EDGE * i + j] be the jth field in the ith element
+    // of an array of edge structs.
+    private float[] edges;
+    private int edgesSize; // size of the edge list.
+    private static final int Y1    = 0;
+    private static final int SLOPE = 1;
+    private static final int OR    = 2; // the orientation. This can be -1 or 1.
+                                     // -1 means up, 1 means down.
+    private static final int CURY  = 3; // j = 5 corresponds to the "current Y".
+                             // Each edge keeps track of the last scanline
+                             // crossing it computed, and this is the y coord of
+                             // that scanline.
+    private static final int CURX = 4; //the x coord of the current crossing.
+
+    // Note that while the array is declared as a float[] not all of it's
+    // elements should be floats. currentY and Orientation should be ints (or int and
+    // byte respectively), but they all need to be the same type. This isn't
+    // really a problem because floats can represent exactly all 23 bit integers,
+    // which should be more than enough.
+    // Note, also, that we only need x1 for slope computation, so we don't need
+    // to store it. x0, y0 don't need to be stored either. They can be put into
+    // curx, cury, and it's ok if they're lost when curx and cury are changed.
+    // We take this undeniably ugly and error prone approach (instead of simply
+    // making an Edge class) for performance reasons. Also, it would probably be nicer
+    // to have one array for each field, but that would defeat the purpose because
+    // it would make poor use of the processor cache, since we tend to access
+    // all the fields for one edge at a time.
+
+    private float edgeMinY;
+    private float edgeMaxY;
+
+
+    private void addEdge(float x0, float y0, float x1, float y1) {
+        float or = (y0 < y1) ? 1f : -1f; // orientation: 1 = UP; -1 = DOWN
+        if (or == -1) {
+            float tmp = y0;
+            y0 = y1;
+            y1 = tmp;
+            tmp = x0;
+            x0 = x1;
+            x1 = tmp;
+        }
+        // skip edges that don't cross a scanline
+        if (Math.ceil(y0) >= Math.ceil(y1)) {
+            return;
+        }
+
+        int newSize = edgesSize + SIZEOF_STRUCT_EDGE;
+        if (edges.length < newSize) {
+            edges = Arrays.copyOf(edges, newSize * 2);
+        }
+        edges[edgesSize+CURX] = x0;
+        edges[edgesSize+CURY] = y0;
+        edges[edgesSize+Y1] = y1;
+        edges[edgesSize+SLOPE] = (x1 - x0) / (y1 - y0);
+        edges[edgesSize+OR] = or;
+        // the crossing values can't be initialized meaningfully yet. This
+        // will have to wait until setCurY is called
+        edgesSize += SIZEOF_STRUCT_EDGE;
+
+        // Accumulate edgeMinY and edgeMaxY
+        if (y0 < edgeMinY) { edgeMinY = y0; }
+        if (y1 > edgeMaxY) { edgeMaxY = y1; }
+    }
+
+    // As far as the following methods care, this edges extends to infinity.
+    // They can compute the x intersect of any horizontal line.
+    // precondition: idx is the index to the start of the desired edge.
+    // So, if the ith edge is wanted, idx should be SIZEOF_STRUCT_EDGE * i
+    private void setCurY(int idx, int y) {
+        // compute the x crossing of edge at idx and horizontal line y
+        // currentXCrossing = (y - y0)*slope + x0
+        edges[idx + CURX] = (y - edges[idx + CURY]) * edges[idx + SLOPE] + edges[idx+CURX];
+        edges[idx + CURY] = (float)y;
+    }
+
+    private void gotoNextY(int idx) {
+        edges[idx + CURY] += 1f; // i.e. curY += 1
+        edges[idx + CURX] += edges[idx + SLOPE]; // i.e. curXCrossing += slope
+    }
+
+    private int getCurCrossing(int idx) {
+        return (int)edges[idx + CURX];
+    }
+//====================================================================================
+
     public static final int WIND_EVEN_ODD = 0;
     public static final int WIND_NON_ZERO = 1;
 
-    // Initial edge list size
-    // IMPL_NOTE - restore size after growth
-    public static final int INITIAL_EDGES = 1000;
-
-    // Recommended maximum scratchpad sizes.  The arrays will grow
-    // larger if needed, but when finished() is called they will be released
-    // if they have grown larger than these sizes.
-    public static final int DEFAULT_INDICES_SIZE = 8192;
-    public static final int DEFAULT_CROSSINGS_SIZE = 32*1024;
-
     // Antialiasing
-    private int SUBPIXEL_LG_POSITIONS_X;
-    private int SUBPIXEL_LG_POSITIONS_Y;
-    private int SUBPIXEL_MASK_X;
-    private int SUBPIXEL_MASK_Y;
-    private int SUBPIXEL_POSITIONS_X;
-    private int SUBPIXEL_POSITIONS_Y;
-    int MAX_AA_ALPHA;
-    private int MAX_AA_ALPHA_DENOM;
-    private int HALF_MAX_AA_ALPHA_DENOM;
-    private int XSHIFT;
-    private int YSHIFT;
-    private int YSTEP;
-    private int HYSTEP;
-    private int YMASK;
-
-    private static final int MIN_QUAD_OPT_WIDTH = 100 << 16;
+    final private int SUBPIXEL_LG_POSITIONS_X;
+    final private int SUBPIXEL_LG_POSITIONS_Y;
+    final private int SUBPIXEL_POSITIONS_X;
+    final private int SUBPIXEL_POSITIONS_Y;
+    final private int SUBPIXEL_MASK_X;
+    final private int SUBPIXEL_MASK_Y;
+    final int MAX_AA_ALPHA;
 
     // Cache to store RLE-encoded coverage mask of the current primitive
-    PiscesCache cache;
+    final PiscesCache cache;
 
-    // Bounds of the drawing region, at S15.16 precsion
-    private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
-
-    // Bounds of the current primitive, at subsample precision
-    private int rasterMinX, rasterMaxX, rasterMinY, rasterMaxY;
+    // Bounds of the drawing region, at subpixel precision.
+    final private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
 
     // Pixel bounding box for current primitive
-    private int bboxX0, bboxY0, bboxX1, bboxY1;
+    private int pix_bboxX0, pix_bboxY0, pix_bboxX1, pix_bboxY1;
 
     // Current winding rule
-    private int windingRule;
+    final private int windingRule;
 
     // Current drawing position, i.e., final point of last segment
-    private int x0, y0;
+    private float x0, y0;
 
     // Position of most recent 'moveTo' command
-    private int sx0, sy0;
-
-    // Buffer to be filled with one row's worth of alpha values
-    private byte[] rowAA; // needs to be short if 16x16 subsampling
+    private float pix_sx0, pix_sy0;
 
-    // Track the number of vertical extrema of the incoming edge list
-    // in order to determine the maximum number of crossings of a
-    // scanline
-    private int firstOrientation;
-    private int lastOrientation;
-    private int flips;
-
-    // Parameters for emitRow
-    private int alphaWidth;
-
-    public Renderer() {
-    }
-
-    public void setAntialiasing(int subpixelLgPositionsX,
-                                int subpixelLgPositionsY) {
+    public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY,
+                    int pix_boundsX, int pix_boundsY,
+                    int pix_boundsWidth, int pix_boundsHeight,
+                    int windingRule,
+                    PiscesCache cache) {
         this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX;
         this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY;
+        this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;
+        this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;
+        this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
+        this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
+        this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);
 
-        this.SUBPIXEL_MASK_X =
-            (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;
-        this.SUBPIXEL_MASK_Y =
-            (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;
-        this.SUBPIXEL_POSITIONS_X =
-            1 << (SUBPIXEL_LG_POSITIONS_X);
-        this.SUBPIXEL_POSITIONS_Y =
-            1 << (SUBPIXEL_LG_POSITIONS_Y);
-        this.MAX_AA_ALPHA =
-            (SUBPIXEL_POSITIONS_X*SUBPIXEL_POSITIONS_Y);
-        this.MAX_AA_ALPHA_DENOM = 255*MAX_AA_ALPHA;
-        this.HALF_MAX_AA_ALPHA_DENOM = MAX_AA_ALPHA_DENOM/2;
-        this.XSHIFT = 16 - SUBPIXEL_LG_POSITIONS_X;
-        this.YSHIFT = 16 - SUBPIXEL_LG_POSITIONS_Y;
-        this.YSTEP = 1 << YSHIFT;
-        this.HYSTEP = 1 << (YSHIFT - 1);
-        this.YMASK = ~(YSTEP - 1);
-    }
+        this.edges = new float[SIZEOF_STRUCT_EDGE * INIT_NUM_EDGES];
+        edgeMinY = Float.POSITIVE_INFINITY;
+        edgeMaxY = Float.NEGATIVE_INFINITY;
+        edgesSize = 0;
+
+        this.windingRule = windingRule;
+        this.cache = cache;
 
-    public int getSubpixelLgPositionsX() {
-        return SUBPIXEL_LG_POSITIONS_X;
-    }
+        this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X;
+        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;
 
-    public int getSubpixelLgPositionsY() {
-        return SUBPIXEL_LG_POSITIONS_Y;
-    }
-
-    public void setWindingRule(int windingRule) {
-        this.windingRule = windingRule;
-    }
-
-    public int getWindingRule() {
-        return windingRule;
+        this.pix_bboxX0 = pix_boundsX;
+        this.pix_bboxY0 = pix_boundsY;
+        this.pix_bboxX1 = pix_boundsX + pix_boundsWidth;
+        this.pix_bboxY1 = pix_boundsY + pix_boundsHeight;
     }
 
-    public void beginRendering(int boundsX, int boundsY,
-                               int boundsWidth, int boundsHeight) {
-        lastOrientation = 0;
-        flips = 0;
-
-        resetEdges();
-
-        this.boundsMinX = boundsX << 16;
-        this.boundsMinY = boundsY << 16;
-        this.boundsMaxX = (boundsX + boundsWidth) << 16;
-        this.boundsMaxY = (boundsY + boundsHeight) << 16;
-
-        this.bboxX0 = boundsX;
-        this.bboxY0 = boundsY;
-        this.bboxX1 = boundsX + boundsWidth;
-        this.bboxY1 = boundsY + boundsHeight;
+    private float tosubpixx(float pix_x) {
+        return pix_x * SUBPIXEL_POSITIONS_X;
+    }
+    private float tosubpixy(float pix_y) {
+        return pix_y * SUBPIXEL_POSITIONS_Y;
     }
 
-    public void moveTo(int x0, int y0) {
-        // System.out.println("Renderer: moveTo " + x0/65536.0 + " " + y0/65536.0);
+    public void moveTo(float pix_x0, float pix_y0) {
         close();
-        this.sx0 = this.x0 = x0;
-        this.sy0 = this.y0 = y0;
-        this.lastOrientation = 0;
+        this.pix_sx0 = pix_x0;
+        this.pix_sy0 = pix_y0;
+        this.y0 = tosubpixy(pix_y0);
+        this.x0 = tosubpixx(pix_x0);
     }
 
-    public void lineJoin() {
-        // System.out.println("Renderer: lineJoin");
-        // do nothing
-    }
+    public void lineJoin() { /* do nothing */ }
 
-    public void lineTo(int x1, int y1) {
-        // System.out.println("Renderer: lineTo " + x1/65536.0 + " " + y1/65536.0);
+    public void lineTo(float pix_x1, float pix_y1) {
+        float x1 = tosubpixx(pix_x1);
+        float y1 = tosubpixy(pix_y1);
 
         // Ignore horizontal lines
-        // Next line will count flip
         if (y0 == y1) {
             this.x0 = x1;
             return;
         }
 
-        int orientation = (y0 < y1) ? 1 : -1;
-        if (lastOrientation == 0) {
-            firstOrientation = orientation;
-        } else if (orientation != lastOrientation) {
-            ++flips;
-        }
-        lastOrientation = orientation;
-
-        // Bias Y by 1 ULP so endpoints never lie on a scanline
-        addEdge(x0, y0 | 0x1, x1, y1 | 0x1);
+        addEdge(x0, y0, x1, y1);
 
         this.x0 = x1;
         this.y0 = y1;
     }
 
     public void close() {
-        // System.out.println("Renderer: close");
-
-        int orientation = lastOrientation;
-        if (y0 != sy0) {
-            orientation = (y0 < sy0) ? 1 : -1;
-        }
-        if (orientation != firstOrientation) {
-            ++flips;
-        }
-        lineTo(sx0, sy0);
+        // lineTo expects its input in pixel coordinates.
+        lineTo(pix_sx0, pix_sy0);
     }
 
     public void end() {
         close();
-        // System.out.println("Renderer: end");
-        // do nothing
-    }
-
-    // Scan convert a single edge
-    private void computeCrossingsForEdge(int index,
-                                         int boundsMinY, int boundsMaxY) {
-        int iy0 = edges[index + 1];
-        int iy1 = edges[index + 3];
-
-        // Clip to valid Y range
-        int clipy0 = (iy0 > boundsMinY) ? iy0 : boundsMinY;
-        int clipy1 = (iy1 < boundsMaxY) ? iy1 : boundsMaxY;
-
-        int minY = ((clipy0 + HYSTEP) & YMASK) + HYSTEP;
-        int maxY = ((clipy1 - HYSTEP) & YMASK) + HYSTEP;
-
-        // IMPL_NOTE - If line falls outside the valid X range, could
-        // draw a vertical line instead
-
-        // Exit if no scanlines are crossed
-        if (minY > maxY) {
-            return;
-        }
-
-        // Scan convert line using a DDA approach
-
-        int ix0 = edges[index];
-        int ix1 = edges[index + 2];
-        long dx = ((long) ix1) - ix0;
-        long dy = ((long) iy1) - iy0;
-
-        // Compute first crossing point at y = minY
-        int orientation = edges[index + 4];
-        int y = minY;
-        long lx = (((long) y) - iy0)*dx/dy + ix0;
-        addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation);
-
-        // Advance y to next scanline, exit if past endpoint
-        y += YSTEP;
-        if (y > maxY) {
-            return;
-        }
-
-        // Compute xstep only if additional scanlines are crossed
-        // For each scanline, add xstep to lx and YSTEP to y and
-        // emit the new crossing
-        long xstep = ((long)YSTEP*dx)/dy;
-        for (; y <= maxY; y += YSTEP) {
-            lx += xstep;
-            addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation);
-        }
-    }
-
-    private void computeBounds() {
-        rasterMinX = crossingMinX & ~SUBPIXEL_MASK_X;
-        rasterMaxX = crossingMaxX | SUBPIXEL_MASK_X;
-        rasterMinY = crossingMinY & ~SUBPIXEL_MASK_Y;
-        rasterMaxY = crossingMaxY | SUBPIXEL_MASK_Y;
-
-        // If nothing was drawn, we have:
-        // minX = Integer.MAX_VALUE and maxX = Integer.MIN_VALUE
-        // so nothing to render
-        if (rasterMinX > rasterMaxX || rasterMinY > rasterMaxY) {
-            rasterMinX = 0;
-            rasterMaxX = -1;
-            rasterMinY = 0;
-            rasterMaxY = -1;
-            return;
-        }
-
-        if (rasterMinX < boundsMinX >> XSHIFT) {
-            rasterMinX = boundsMinX >> XSHIFT;
-        }
-        if (rasterMinY < boundsMinY >> YSHIFT) {
-            rasterMinY = boundsMinY >> YSHIFT;
-        }
-        if (rasterMaxX > boundsMaxX >> XSHIFT) {
-            rasterMaxX = boundsMaxX >> XSHIFT;
-        }
-        if (rasterMaxY > boundsMaxY >> YSHIFT) {
-            rasterMaxY = boundsMaxY >> YSHIFT;
-        }
-    }
-
-    private int clamp(int x, int min, int max) {
-        if (x < min) {
-            return min;
-        } else if (x > max) {
-            return max;
-        }
-        return x;
     }
 
     private void _endRendering() {
-        if (flips == 0) {
-            bboxX0 = bboxY0 = 0;
-            bboxX1 = bboxY1 = -1;
-            return;
-        }
+        // Mask to determine the relevant bit of the crossing sum
+        // 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 = ((boundsMaxX - boundsMinX) >> SUBPIXEL_LG_POSITIONS_X) + 1;
+        byte[] alpha = new byte[width+1];
 
-        // Special case for filling a single rect with a flat, opaque color
-        // REMIND: This special case was not originally written to fill a
-        // cache object and called directly to a Blit - it needs some code
-        // to fill the cache instead to be useful for this usage...
-        if (false /* Does not work with cache (yet?) */ &&
-            edgeIdx == 10 &&
-            edges[0] == edges[2] &&
-            edges[1] == edges[6] &&
-            edges[3] == edges[8] &&
-            edges[5] == edges[7] &&
-            Math.abs(edges[0] - edges[5]) > MIN_QUAD_OPT_WIDTH)
-        {
+        // Now we iterate through the scanlines. We must tell emitRow the coord
+        // of the first non-transparent pixel, so we must keep accumulators for
+        // the first and last pixels of the section of the current pixel row
+        // that we will emit.
+        // We also need to accumulate pix_bbox*, but the iterator does it
+        // for us. We will just get the values from it once this loop is done
+        int pix_maxX = Integer.MIN_VALUE;
+        int pix_minX = Integer.MAX_VALUE;
 
-            int x0 = edges[0] >> XSHIFT;
-            int y0 = edges[1] >> YSHIFT;
-            int x1 = edges[5] >> XSHIFT;
-            int y1 = edges[3] >> YSHIFT;
+        int y = boundsMinY; // needs to be declared here so we emit the last row properly.
+        ScanLineItInitialize();
+        for ( ; ScanLineItHasNext(); ) {
+            int numCrossings = ScanLineItGoToNextYAndComputeCrossings();
+            y = ScanLineItCurrentY();
 
-            if (x0 > x1) {
-                int tmp = x0;
-                x0 = x1;
-                x1 = tmp;
-            }
-            if (y0 > y1) {
-                int tmp = y0;
-                y0 = y1;
-                y1 = tmp;
+            if (numCrossings > 0) {
+                int lowx = crossings[0] >> 1;
+                int highx = crossings[numCrossings - 1] >> 1;
+                int x0 = Math.max(lowx, boundsMinX);
+                int x1 = Math.min(highx, boundsMaxX);
+
+                pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
+                pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
             }
 
-            int bMinX = this.boundsMinX >> XSHIFT;
-            int bMinY = this.boundsMinY >> YSHIFT;
-            int bMaxX = this.boundsMaxX >> XSHIFT;
-            int bMaxY = this.boundsMaxY >> YSHIFT;
+            int sum = 0;
+            int prev = boundsMinX;
+            for (int i = 0; i < numCrossings; i++) {
+                int curxo = crossings[i];
+                int curx = curxo >> 1;
+                int crorientation = ((curxo & 0x1) == 0x1) ? 1 : -1;
+                if ((sum & mask) != 0) {
+                    int x0 = Math.max(prev, boundsMinX);
+                    int x1 = Math.min(curx, boundsMaxX);
+                    if (x0 < x1) {
+                        x0 -= boundsMinX; // turn x0, x1 from coords to indeces
+                        x1 -= boundsMinX; // in the alpha array.
 
-            // Clip to image bounds in supersampled coordinates
-            x0 = clamp(x0, bMinX, bMaxX);
-            x1 = clamp(x1, bMinX, bMaxX);
-            y0 = clamp(y0, bMinY, bMaxY);
-            y1 = clamp(y1, bMinY, bMaxY);
+                        int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X;
+                        int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
 
-            /*
-             * REMIND: Need to fill the cache here instead...
-            Blit.fillRectSrcOver(this,
-                                 imageData, imageType,
-                                 imageOffset,
-                                 imageScanlineStride, imagePixelStride,
-                                 width, height,
-                                 x0, y0, x1, y1,
-                                 cred, cgreen, cblue);
-            */
+                        if (pix_x == pix_xmaxm1) {
+                            // Start and end in same pixel
+                            alpha[pix_x] += (x1 - x0);
+                            alpha[pix_x+1] -= (x1 - x0);
+                        } else {
+                            int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
+                            alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X);
+                            alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X);
+                            alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X);
+                            alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X);
+                        }
+                    }
+                }
+                sum += crorientation;
+                prev = curx;
+            }
 
-            bboxX0 = x0 >> SUBPIXEL_LG_POSITIONS_X;
-            bboxY0 = y0 >> SUBPIXEL_LG_POSITIONS_Y;
-            bboxX1 = (x1 + SUBPIXEL_POSITIONS_X - 1)
-                >> SUBPIXEL_LG_POSITIONS_X;
-            bboxY1 = (y1 + SUBPIXEL_POSITIONS_Y - 1)
-                >> SUBPIXEL_LG_POSITIONS_Y;
-
-            return;
-        }
-
-        int minY = (edgeMinY > boundsMinY) ? edgeMinY : boundsMinY;
-        int maxY = (edgeMaxY < boundsMaxY) ? edgeMaxY : boundsMaxY;
-
-        // Check for empty intersection of primitive with the drawing area
-        if (minY > maxY) {
-            bboxX0 = bboxY0 = 0;
-            bboxX1 = bboxY1 = -1;
-            return;
+            if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) {
+                emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
+                pix_minX = Integer.MAX_VALUE;
+                pix_maxX = Integer.MIN_VALUE;
+            }
         }
 
-        // Compute Y extent in subpixel coordinates
-        int iminY = (minY >> YSHIFT) & ~SUBPIXEL_MASK_Y;
-        int imaxY = (maxY >> YSHIFT) | SUBPIXEL_MASK_Y;
-        int yextent = (imaxY - iminY) + 1;
-
-        // Maximum number of crossings
-        int size = flips*yextent;
-
-        int bmax = (boundsMaxY >> YSHIFT) - 1;
-        if (imaxY > bmax) {
-            imaxY = bmax;
+        // Emit final row
+        if (pix_maxX >= pix_minX) {
+            emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
         }
-
-        // Initialize X bounds, will be refined for each strip
-        bboxX0 = Integer.MAX_VALUE;
-        bboxX1 = Integer.MIN_VALUE;
-
-        // Set Y bounds
-        bboxY0 = iminY >> SUBPIXEL_LG_POSITIONS_Y;
-        bboxY1 = (imaxY + SUBPIXEL_POSITIONS_Y - 1) >> SUBPIXEL_LG_POSITIONS_Y;
-
-        // Compute number of rows that can be processing using
-        // a crossings table no larger than DEFAULT_CROSSINGS_SIZE.
-        // However, we must process at least one row, so we grow the table
-        // temporarily if needed.  This would require an object with a
-        // huge number of flips.
-        int rows = DEFAULT_CROSSINGS_SIZE/(flips*SUBPIXEL_POSITIONS_Y);
-        rows = Math.min(rows, yextent);
-        rows = Math.max(rows, 1);
-        for (int i = iminY; i <= imaxY; i += rows*SUBPIXEL_POSITIONS_Y) {
-            // Compute index of last scanline to be processed in this pass
-            int last = Math.min(i + rows*SUBPIXEL_POSITIONS_Y - 1, imaxY);
-            setCrossingsExtents(i, last, flips);
-
-            int bminY = i << YSHIFT;
-            int bmaxY = (last << YSHIFT) | ~YMASK;
+        pix_bboxX0 = minX >> SUBPIXEL_LG_POSITIONS_X;
+        pix_bboxX1 = maxX >> SUBPIXEL_LG_POSITIONS_X;
+        pix_bboxY0 = minY >> SUBPIXEL_LG_POSITIONS_Y;
+        pix_bboxY1 = maxY >> SUBPIXEL_LG_POSITIONS_Y;
+    }
 
-            // Process edges from the edge list
-            int maxIdx = edgeIdx;
-            for (int index = 0; index < maxIdx; index += 5) {
-                // Test y1 < min:
-                //
-                // If edge lies entirely above current strip,
-                // discard it
-                if (edges[index + 3] < bminY) {
-                    // Overwrite the edge with the last edge
-                    edgeIdx -= 5;
-                    int fidx = edgeIdx;
-                    int tidx = index;
-                    edges[tidx++] = edges[fidx++];
-                    edges[tidx++] = edges[fidx++];
-                    edges[tidx++] = edges[fidx++];
-                    edges[tidx++] = edges[fidx++];
-                    edges[tidx  ] = edges[fidx  ];
-
-                    maxIdx -= 5;
-                    index -= 5;
-                    continue;
-                }
-
-                // Test y0 > max:
-                //
-                // If edge lies entirely below current strip,
-                // skip it for now
-                if (edges[index + 1] > bmaxY) {
-                    continue;
-                }
-
-                computeCrossingsForEdge(index, bminY, bmaxY);
-            }
-
-            computeBounds();
-            if (rasterMaxX < rasterMinX) {
-                continue;
-            }
-
-            bboxX0 = Math.min(bboxX0,
-                              rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
-            bboxX1 = Math.max(bboxX1,
-                              (rasterMaxX + SUBPIXEL_POSITIONS_X - 1)
-                              >> SUBPIXEL_LG_POSITIONS_X);
-            renderStrip();
-        }
-
-        // Free up any unusually large scratchpad memory used by the
-        // preceding primitive
-        crossingListFinished();
-    }
 
     public void endRendering() {
         // Set up the cache to accumulate the bounding box
@@ -478,176 +472,31 @@
         _endRendering();
     }
 
-    public void getBoundingBox(int[] bbox) {
-        bbox[0] = bboxX0;
-        bbox[1] = bboxY0;
-        bbox[2] = bboxX1 - bboxX0;
-        bbox[3] = bboxY1 - bboxY0;
+    public void getBoundingBox(int[] pix_bbox) {
+        pix_bbox[0] = pix_bboxX0;
+        pix_bbox[1] = pix_bboxY0;
+        pix_bbox[2] = pix_bboxX1 - pix_bboxX0;
+        pix_bbox[3] = pix_bboxY1 - pix_bboxY0;
     }
 
-    private void renderStrip() {
-        // Grow rowAA according to the raster width
-        int width = (rasterMaxX - rasterMinX + 1) >> SUBPIXEL_LG_POSITIONS_X;
-        alphaWidth = width;
-
-        // Allocate one extra entry in rowAA to avoid a conditional in
-        // the rendering loop
-        int bufLen = width + 1;
-        if (this.rowAA == null || this.rowAA.length < bufLen) {
-            this.rowAA = new byte[bufLen];
-        }
-
-        // Mask to determine the relevant bit of the crossing sum
-        // 0x1 if EVEN_ODD, all bits if NON_ZERO
-        int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
-
-        int y = 0;
-        int prevY = rasterMinY - 1;
-
-        int minX = Integer.MAX_VALUE;
-        int maxX = Integer.MIN_VALUE;
-
-        iterateCrossings();
-        while (hasMoreCrossingRows()) {
-            y = crossingY;
-
-            // Emit any skipped rows
-            for (int j = prevY + 1; j < y; j++) {
-                if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) ||
-                    (j == rasterMaxY)) {
-                    emitRow(j >> SUBPIXEL_LG_POSITIONS_Y, 0, -1);
-                }
-            }
-            prevY = y;
-
-            if (crossingRowIndex < crossingRowCount) {
-                int lx = crossings[crossingRowOffset + crossingRowIndex];
-                lx >>= 1;
-                int hx = crossings[crossingRowOffset + crossingRowCount - 1];
-                hx >>= 1;
-                int x0 = lx > rasterMinX ? lx : rasterMinX;
-                int x1 = hx < rasterMaxX ? hx : rasterMaxX;
-                x0 -= rasterMinX;
-                x1 -= rasterMinX;
-
-                minX = Math.min(minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
-                maxX = Math.max(maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
-            }
-
-            int sum = 0;
-            int prev = rasterMinX;
-            while (crossingRowIndex < crossingRowCount) {
-                int crxo = crossings[crossingRowOffset + crossingRowIndex];
-                crossingRowIndex++;
-
-                int crx = crxo >> 1;
-                int crorientation = ((crxo & 0x1) == 0x1) ? 1 : -1;
-
-                if ((sum & mask) != 0) {
-                    // Clip to active X range, if x1 < x0 loop will
-                    // have no effect
-                    int x0 = prev > rasterMinX ? prev : rasterMinX;
-                    int x1 =  crx < rasterMaxX ?  crx : rasterMaxX;
-
-                    // Empty spans
-                    if (x1 > x0) {
-                        x0 -= rasterMinX;
-                        x1 -= rasterMinX;
-
-                        // Accumulate alpha, equivalent to:
-                        //   for (int x = x0; x < x1; x++) {
-                        //       ++rowAA[x >> SUBPIXEL_LG_POSITIONS_X];
-                        //   }
-                        //
-                        // In the middle of the span, we can update a full
-                        // pixel at a time (i.e., SUBPIXEL_POSITIONS_X
-                        // subpixels)
-
-                        int x = x0 >> SUBPIXEL_LG_POSITIONS_X;
-                        int xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
-                        if (x == xmaxm1) {
-                            // Start and end in same pixel
-                            rowAA[x] += x1 - x0;
-                        } else {
-                            // Start and end in different pixels
-                            rowAA[x++] += SUBPIXEL_POSITIONS_X -
-                                (x0 & SUBPIXEL_MASK_X);
-                            int xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
-                            while (x < xmax) {
-                                rowAA[x++] += SUBPIXEL_POSITIONS_X;
-                            }
-                            // Note - at this point it is possible that
-                            // x == width, which implies that
-                            // x1 & SUBPIXEL_MASK_X == 0.  We allocate
-                            // one extra entry in rowAA so this
-                            // assignment will be harmless.  The alternative
-                            // is an extra conditional here, or some other
-                            // scheme to deal with the last pixel better.
-                            rowAA[x] += x1 & SUBPIXEL_MASK_X;
-                        }
-                    }
-                }
-                sum += crorientation;
-                prev = crx;
-            }
-
-            // Every SUBPIXEL_POSITIONS rows, output an antialiased row
-            if (((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) ||
-                (y == rasterMaxY)) {
-                emitRow(y >> SUBPIXEL_LG_POSITIONS_Y, minX, maxX);
-                minX = Integer.MAX_VALUE;
-                maxX = Integer.MIN_VALUE;
-            }
-        }
-
-        // Emit final row
-        for (int j = prevY + 1; j <= rasterMaxY; j++) {
-            if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) ||
-                (j == rasterMaxY)) {
-                emitRow(j >> SUBPIXEL_LG_POSITIONS_Y, minX, maxX);
-                minX = Integer.MAX_VALUE;
-                maxX = Integer.MIN_VALUE;
-            }
-        }
-    }
-
-    private void clearAlpha(byte[] alpha,
-                            int width,
-                            int minX, int maxX) {
-        if (maxX >= minX) {
-            int w = maxX - minX + 1;
-            if (w + minX > width) {
-                w = width - minX;
-            }
-
-            int aidx = minX;
-            for (int i = 0; i < w; i++, aidx++) {
-                alpha[aidx] = (byte)0;
-            }
-        }
-    }
-
-    private void emitRow(int y, int minX, int maxX) {
+    private void emitRow(byte[] alphaRow, int pix_y, int pix_from, int pix_to) {
         // Copy rowAA data into the cache if one is present
         if (cache != null) {
-            if (maxX >= minX) {
-                int x0 = minX + (rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
-                int x1 = maxX + (rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
+            if (pix_to >= pix_from) {
+                cache.startRow(pix_y, pix_from, pix_to);
 
-                cache.startRow(y, x0, x1);
-                int srcIdx = minX;
+                // Perform run-length encoding and store results in the cache
+                int from = pix_from - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X);
+                int to = pix_to - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X);
 
-                // Perform run-length encoding
-                // and store results in the cache
-                byte startVal = rowAA[srcIdx++];
                 int runLen = 1;
-                while (srcIdx <= maxX) {
-                    byte nextVal = rowAA[srcIdx++];
+                byte startVal = alphaRow[from];
+                for (int i = from + 1; i <= to; i++) {
+                    byte nextVal = (byte)(startVal + alphaRow[i]);
                     if (nextVal == startVal && runLen < 255) {
-                        ++runLen;
+                        runLen++;
                     } else {
                         cache.addRLERun(startVal, runLen);
-
                         runLen = 1;
                         startVal = nextVal;
                     }
@@ -656,190 +505,6 @@
                 cache.addRLERun((byte)0, 0);
             }
         }
-
-        clearAlpha(rowAA,
-                   alphaWidth,
-                   minX, maxX);
-    }
-
-    public void setCache(PiscesCache cache) {
-        this.cache = cache;
-    }
-
-    // Edge list data
-
-    private int[] edges = new int[5*INITIAL_EDGES];
-    private int edgeIdx = 0;
-    private int edgeMinY = Integer.MAX_VALUE;
-    private int edgeMaxY = Integer.MIN_VALUE;
-
-    private void addEdge(int x0, int y0, int x1, int y1) {
-        int newLen = edgeIdx + 5;
-        if (edges.length < newLen) {
-            int[] tmp = new int[Math.max(11*edges.length/10, newLen)];
-            System.arraycopy(edges, 0, tmp, 0, edgeIdx);
-            this.edges = tmp;
-        }
-
-        int orientation = 1;
-        if (y0 > y1) {
-            int tmp = y0;
-            y0 = y1;
-            y1 = tmp;
-
-            orientation = -1;
-        }
-
-        // Skip edges that don't cross a subsampled scanline
-        int eminY = ((y0 + HYSTEP) & YMASK);
-        int emaxY = ((y1 - HYSTEP) & YMASK);
-        if (eminY > emaxY) {
-            return;
-        }
-
-        if (orientation == -1) {
-            int tmp = x0;
-            x0 = x1;
-            x1 = tmp;
-        }
-
-        edges[edgeIdx++] = x0;
-        edges[edgeIdx++] = y0;
-        edges[edgeIdx++] = x1;
-        edges[edgeIdx++] = y1;
-        edges[edgeIdx++] = orientation;
-
-        // Update Y bounds of primitive
-        if (y0 < edgeMinY) {
-            edgeMinY = y0;
-        }
-        if (y1 > edgeMaxY) {
-            edgeMaxY = y1;
-        }
-    }
-
-    private void resetEdges() {
-        this.edgeIdx = 0;
-        this.edgeMinY = Integer.MAX_VALUE;
-        this.edgeMaxY = Integer.MIN_VALUE;
-    }
-
-    // Crossing list data
-
-    private int[] crossingIndices;
-    private int[] crossings;
-    private int crossingMinY;
-    private int crossingMaxY;
-    private int crossingMinX = Integer.MAX_VALUE;
-    private int crossingMaxX = Integer.MIN_VALUE;
-    private int crossingMaxXEntries;
-    private int numCrossings = 0;
-    private boolean crossingsSorted = false;
-
-    private int crossingY;
-    private int crossingRowCount;
-    private int crossingRowOffset;
-    private int crossingRowIndex;
-
-    private void setCrossingsExtents(int minY, int maxY, int maxXEntries) {
-        int yextent = maxY - minY + 1;
-
-        // Grow indices array as needed
-        if (crossingIndices == null || crossingIndices.length < yextent) {
-            this.crossingIndices =
-                new int[Math.max(yextent, DEFAULT_INDICES_SIZE)];
-        }
-        // Grow crossings array as needed
-        if (crossings == null || crossings.length < yextent*maxXEntries) {
-            this.crossings = new int[Math.max(yextent*maxXEntries,
-                                              DEFAULT_CROSSINGS_SIZE)];
-        }
-        this.crossingMinY = minY;
-        this.crossingMaxY = maxY;
-        this.crossingMaxXEntries = maxXEntries;
-        resetCrossings();
-    }
-
-    private void resetCrossings() {
-        int yextent = crossingMaxY - crossingMinY + 1;
-        int start = 0;
-        for (int i = 0; i < yextent; i++) {
-            crossingIndices[i] = start;
-            start += crossingMaxXEntries;
-        }
-        crossingMinX = Integer.MAX_VALUE;
-        crossingMaxX = Integer.MIN_VALUE;
-        numCrossings = 0;
-        crossingsSorted = false;
-    }
-
-    // Free sorting arrays if larger than maximum size
-    private void crossingListFinished() {
-        if (crossings != null && crossings.length > DEFAULT_CROSSINGS_SIZE) {
-            crossings = new int[DEFAULT_CROSSINGS_SIZE];
-        }
-        if (crossingIndices != null &&
-            crossingIndices.length > DEFAULT_INDICES_SIZE)
-        {
-            crossingIndices = new int[DEFAULT_INDICES_SIZE];
-        }
-    }
-
-    private void sortCrossings(int[] x, int off, int len) {
-        for (int i = off + 1; i < off + len; i++) {
-            int j = i;
-            int xj = x[j];
-            int xjm1;
-
-            while (j > off && (xjm1 = x[j - 1]) > xj) {
-                x[j] = xjm1;
-                x[j - 1] = xj;
-                j--;
-            }
-        }
-    }
-
-    private void sortCrossings() {
-        int start = 0;
-        for (int i = 0; i <= crossingMaxY - crossingMinY; i++) {
-            sortCrossings(crossings, start, crossingIndices[i] - start);
-            start += crossingMaxXEntries;
-        }
-    }
-
-    private void addCrossing(int y, int x, int orientation) {
-        if (x < crossingMinX) {
-            crossingMinX = x;
-        }
-        if (x > crossingMaxX) {
-            crossingMaxX = x;
-        }
-
-        int index = crossingIndices[y - crossingMinY]++;
-        x <<= 1;
-        crossings[index] = (orientation == 1) ? (x | 0x1) : x;
-
-        ++numCrossings;
-    }
-
-    private void iterateCrossings() {
-        if (!crossingsSorted) {
-            sortCrossings();
-            crossingsSorted = true;
-        }
-        crossingY = crossingMinY - 1;
-        crossingRowOffset = -crossingMaxXEntries;
-    }
-
-    private boolean hasMoreCrossingRows() {
-        if (++crossingY <= crossingMaxY) {
-            crossingRowOffset += crossingMaxXEntries;
-            int y = crossingY - crossingMinY;
-            crossingRowCount = crossingIndices[y] - y*crossingMaxXEntries;
-            crossingRowIndex = 0;
-            return true;
-        } else {
-            return false;
-        }
+        java.util.Arrays.fill(alphaRow, (byte)0);
     }
 }
--- a/jdk/src/share/classes/sun/java2d/pisces/Stroker.java	Thu Jul 29 17:12:27 2010 -0700
+++ b/jdk/src/share/classes/sun/java2d/pisces/Stroker.java	Tue Aug 10 13:19:44 2010 -0400
@@ -25,7 +25,7 @@
 
 package sun.java2d.pisces;
 
-public class Stroker extends LineSink {
+public class Stroker implements LineSink {
 
     private static final int MOVE_TO = 0;
     private static final int LINE_TO = 1;
@@ -61,19 +61,15 @@
      */
     public static final int CAP_SQUARE = 2;
 
-    LineSink output;
+    private final LineSink output;
 
-    int lineWidth;
-    int capStyle;
-    int joinStyle;
-    int miterLimit;
+    private final int capStyle;
+    private final int joinStyle;
 
-    Transform4 transform;
-    int m00, m01;
-    int m10, m11;
+    private final float m00, m01, m10, m11, det;
 
-    int lineWidth2;
-    long scaledLineWidth2;
+    private final float lineWidth2;
+    private final float scaledLineWidth2;
 
     // For any pen offset (pen_dx, pen_dy) that does not depend on
     // the line orientation, the pen should be transformed so that:
@@ -88,143 +84,86 @@
     //
     // pen_dx'(r, theta) = r*(m00*cos(theta) + m01*sin(theta))
     // pen_dy'(r, theta) = r*(m10*cos(theta) + m11*sin(theta))
-    int numPenSegments;
-    int[] pen_dx;
-    int[] pen_dy;
-    boolean[] penIncluded;
-    int[] join;
+    private int numPenSegments;
+    private final float[] pen_dx;
+    private final float[] pen_dy;
+    private boolean[] penIncluded;
+    private final float[] join;
 
-    int[] offset = new int[2];
-    int[] reverse = new int[100];
-    int[] miter = new int[2];
-    long miterLimitSq;
+    private final float[] offset = new float[2];
+    private float[] reverse = new float[100];
+    private final float[] miter = new float[2];
+    private final float miterLimitSq;
 
-    int prev;
-    int rindex;
-    boolean started;
-    boolean lineToOrigin;
-    boolean joinToOrigin;
-
-    int sx0, sy0, sx1, sy1, x0, y0, x1, y1;
-    int mx0, my0, mx1, my1, omx, omy;
-    int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0;
+    private int prev;
+    private int rindex;
+    private boolean started;
+    private boolean lineToOrigin;
+    private boolean joinToOrigin;
 
-    double m00_2_m01_2;
-    double m10_2_m11_2;
-    double m00_m10_m01_m11;
+    private float sx0, sy0, sx1, sy1, x0, y0, px0, py0;
+    private float mx0, my0, omx, omy;
 
-    /**
-     * Empty constructor.  <code>setOutput</code> and
-     * <code>setParameters</code> must be called prior to calling any
-     * other methods.
-     */
-    public Stroker() {}
+    private float m00_2_m01_2;
+    private float m10_2_m11_2;
+    private float m00_m10_m01_m11;
 
     /**
      * Constructs a <code>Stroker</code>.
      *
      * @param output an output <code>LineSink</code>.
-     * @param lineWidth the desired line width in pixels, in S15.16
-     * format.
+     * @param lineWidth the desired line width in pixels
      * @param capStyle the desired end cap style, one of
      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
      * <code>CAP_SQUARE</code>.
      * @param joinStyle the desired line join style, one of
      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
      * <code>JOIN_BEVEL</code>.
-     * @param miterLimit the desired miter limit, in S15.16 format.
+     * @param miterLimit the desired miter limit
      * @param transform a <code>Transform4</code> object indicating
      * the transform that has been previously applied to all incoming
      * coordinates.  This is required in order to produce consistently
      * shaped end caps and joins.
      */
     public Stroker(LineSink output,
-                   int lineWidth,
+                   float lineWidth,
                    int capStyle,
                    int joinStyle,
-                   int miterLimit,
-                   Transform4 transform) {
-        setOutput(output);
-        setParameters(lineWidth, capStyle, joinStyle, miterLimit, transform);
-    }
-
-    /**
-     * Sets the output <code>LineSink</code> of this
-     * <code>Stroker</code>.
-     *
-     * @param output an output <code>LineSink</code>.
-     */
-    public void setOutput(LineSink output) {
+                   float miterLimit,
+                   float m00, float m01, float m10, float m11) {
         this.output = output;
-    }
 
-    /**
-     * Sets the parameters of this <code>Stroker</code>.
-     * @param lineWidth the desired line width in pixels, in S15.16
-     * format.
-     * @param capStyle the desired end cap style, one of
-     * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
-     * <code>CAP_SQUARE</code>.
-     * @param joinStyle the desired line join style, one of
-     * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
-     * <code>JOIN_BEVEL</code>.
-     * @param miterLimit the desired miter limit, in S15.16 format.
-     * @param transform a <code>Transform4</code> object indicating
-     * the transform that has been previously applied to all incoming
-     * coordinates.  This is required in order to produce consistently
-     * shaped end caps and joins.
-     */
-    public void setParameters(int lineWidth,
-                              int capStyle,
-                              int joinStyle,
-                              int miterLimit,
-                              Transform4 transform) {
-        this.lineWidth = lineWidth;
-        this.lineWidth2 = lineWidth >> 1;
-        this.scaledLineWidth2 = ((long)transform.m00*lineWidth2) >> 16;
+        this.lineWidth2 = lineWidth / 2;
+        this.scaledLineWidth2 = m00 * lineWidth2;
         this.capStyle = capStyle;
         this.joinStyle = joinStyle;
-        this.miterLimit = miterLimit;
 
-        this.transform = transform;
-        this.m00 = transform.m00;
-        this.m01 = transform.m01;
-        this.m10 = transform.m10;
-        this.m11 = transform.m11;
-
-        this.m00_2_m01_2 = (double)m00*m00 + (double)m01*m01;
-        this.m10_2_m11_2 = (double)m10*m10 + (double)m11*m11;
-        this.m00_m10_m01_m11 = (double)m00*m10 + (double)m01*m11;
+        m00_2_m01_2 = m00*m00 + m01*m01;
+        m10_2_m11_2 = m10*m10 + m11*m11;
+        m00_m10_m01_m11 = m00*m10 + m01*m11;
 
-        double dm00 = m00/65536.0;
-        double dm01 = m01/65536.0;
-        double dm10 = m10/65536.0;
-        double dm11 = m11/65536.0;
-        double determinant = dm00*dm11 - dm01*dm10;
+        this.m00 = m00;
+        this.m01 = m01;
+        this.m10 = m10;
+        this.m11 = m11;
+        det = m00*m11 - m01*m10;
 
-        if (joinStyle == JOIN_MITER) {
-            double limit =
-                (miterLimit/65536.0)*(lineWidth2/65536.0)*determinant;
-            double limitSq = limit*limit;
-            this.miterLimitSq = (long)(limitSq*65536.0*65536.0);
-        }
+        float limit = miterLimit * lineWidth2 * det;
+        this.miterLimitSq = limit*limit;
 
-        this.numPenSegments = (int)(3.14159f*lineWidth/65536.0f);
-        if (pen_dx == null || pen_dx.length < numPenSegments) {
-            this.pen_dx = new int[numPenSegments];
-            this.pen_dy = new int[numPenSegments];
-            this.penIncluded = new boolean[numPenSegments];
-            this.join = new int[2*numPenSegments];
-        }
+        this.numPenSegments = (int)(3.14159f * lineWidth);
+        this.pen_dx = new float[numPenSegments];
+        this.pen_dy = new float[numPenSegments];
+        this.penIncluded = new boolean[numPenSegments];
+        this.join = new float[2*numPenSegments];
 
         for (int i = 0; i < numPenSegments; i++) {
-            double r = lineWidth/2.0;
-            double theta = (double)i*2.0*Math.PI/numPenSegments;
+            double theta = (i * 2.0 * Math.PI)/numPenSegments;
 
             double cos = Math.cos(theta);
             double sin = Math.sin(theta);
-            pen_dx[i] = (int)(r*(dm00*cos + dm01*sin));
-            pen_dy[i] = (int)(r*(dm10*cos + dm11*sin));
+            pen_dx[i] = (float)(lineWidth2 * (m00*cos + m01*sin));
+            pen_dy[i] = (float)(lineWidth2 * (m10*cos + m11*sin));
         }
 
         prev = CLOSE;
@@ -233,32 +172,31 @@
         lineToOrigin = false;
     }
 
-    private void computeOffset(int x0, int y0, int x1, int y1, int[] m) {
-        long lx = (long)x1 - (long)x0;
-        long ly = (long)y1 - (long)y0;
+    private void computeOffset(float x0, float y0,
+                               float x1, float y1, float[] m) {
+        float lx = x1 - x0;
+        float ly = y1 - y0;
 
-        int dx, dy;
+        float dx, dy;
         if (m00 > 0 && m00 == m11 && m01 == 0 & m10 == 0) {
-            long ilen = PiscesMath.hypot(lx, ly);
+            float ilen = (float)Math.hypot(lx, ly);
             if (ilen == 0) {
                 dx = dy = 0;
             } else {
-                dx = (int)( (ly*scaledLineWidth2)/ilen);
-                dy = (int)(-(lx*scaledLineWidth2)/ilen);
+                dx = (ly * scaledLineWidth2)/ilen;
+                dy = -(lx * scaledLineWidth2)/ilen;
             }
         } else {
-            double dlx = x1 - x0;
-            double dly = y1 - y0;
-            double det = (double)m00*m11 - (double)m01*m10;
             int sdet = (det > 0) ? 1 : -1;
-            double a = dly*m00 - dlx*m10;
-            double b = dly*m01 - dlx*m11;
-            double dh = PiscesMath.hypot(a, b);
-            double div = sdet*lineWidth2/(65536.0*dh);
-            double ddx = dly*m00_2_m01_2 - dlx*m00_m10_m01_m11;
-            double ddy = dly*m00_m10_m01_m11 - dlx*m10_2_m11_2;
-            dx = (int)(ddx*div);
-            dy = (int)(ddy*div);
+            float a = ly * m00 - lx * m10;
+            float b = ly * m01 - lx * m11;
+            float dh = (float)Math.hypot(a, b);
+            float div = sdet * lineWidth2/dh;
+
+            float ddx = ly * m00_2_m01_2 - lx * m00_m10_m01_m11;
+            float ddy = ly * m00_m10_m01_m11 - lx * m10_2_m11_2;
+            dx = ddx*div;
+            dy = ddy*div;
         }
 
         m[0] = dx;
@@ -267,58 +205,43 @@
 
     private void ensureCapacity(int newrindex) {
         if (reverse.length < newrindex) {
-            int[] tmp = new int[Math.max(newrindex, 6*reverse.length/5)];
-            System.arraycopy(reverse, 0, tmp, 0, rindex);
-            this.reverse = tmp;
+            reverse = java.util.Arrays.copyOf(reverse, 6*reverse.length/5);
         }
     }
 
-    private boolean isCCW(int x0, int y0,
-                          int x1, int y1,
-                          int x2, int y2) {
-        int dx0 = x1 - x0;
-        int dy0 = y1 - y0;
-        int dx1 = x2 - x1;
-        int dy1 = y2 - y1;
-        return (long)dx0*dy1 < (long)dy0*dx1;
+    private boolean isCCW(float x0, float y0,
+                          float x1, float y1,
+                          float x2, float y2) {
+        return (x1 - x0) * (y2 - y1) < (y1 - y0) * (x2 - x1);
     }
 
-    private boolean side(int x, int y, int x0, int y0, int x1, int y1) {
-        long lx = x;
-        long ly = y;
-        long lx0 = x0;
-        long ly0 = y0;
-        long lx1 = x1;
-        long ly1 = y1;
-
-        return (ly0 - ly1)*lx + (lx1 - lx0)*ly + (lx0*ly1 - lx1*ly0) > 0;
+    private boolean side(float x,  float y,
+                         float x0, float y0,
+                         float x1, float y1) {
+        return (y0 - y1)*x + (x1 - x0)*y + (x0*y1 - x1*y0) > 0;
     }
 
-    private int computeRoundJoin(int cx, int cy,
-                                 int xa, int ya,
-                                 int xb, int yb,
+    private int computeRoundJoin(float cx, float cy,
+                                 float xa, float ya,
+                                 float xb, float yb,
                                  int side,
                                  boolean flip,
-                                 int[] join) {
-        int px, py;
+                                 float[] join) {
+        float px, py;
         int ncoords = 0;
 
         boolean centerSide;
         if (side == 0) {
             centerSide = side(cx, cy, xa, ya, xb, yb);
         } else {
-            centerSide = (side == 1) ? true : false;
+            centerSide = (side == 1);
         }
         for (int i = 0; i < numPenSegments; i++) {
             px = cx + pen_dx[i];
             py = cy + pen_dy[i];
 
             boolean penSide = side(px, py, xa, ya, xb, yb);
-            if (penSide != centerSide) {
-                penIncluded[i] = true;
-            } else {
-                penIncluded[i] = false;
-            }
+            penIncluded[i] = (penSide != centerSide);
         }
 
         int start = -1, end = -1;
@@ -338,10 +261,10 @@
         }
 
         if (start != -1 && end != -1) {
-            long dxa = cx + pen_dx[start] - xa;
-            long dya = cy + pen_dy[start] - ya;
-            long dxb = cx + pen_dx[start] - xb;
-            long dyb = cy + pen_dy[start] - yb;
+            float dxa = cx + pen_dx[start] - xa;
+            float dya = cy + pen_dy[start] - ya;
+            float dxb = cx + pen_dx[start] - xb;
+            float dyb = cy + pen_dy[start] - yb;
 
             boolean rev = (dxa*dxa + dya*dya > dxb*dxb + dyb*dyb);
             int i = rev ? end : start;
@@ -362,22 +285,25 @@
         return ncoords/2;
     }
 
-    private static final long ROUND_JOIN_THRESHOLD = 1000L;
-    private static final long ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000L;
+    // pisces used to use fixed point arithmetic with 16 decimal digits. I
+    // didn't want to change the values of the constants below when I converted
+    // it to floating point, so that's why the divisions by 2^16 are there.
+    private static final float ROUND_JOIN_THRESHOLD = 1000/65536f;
+    private static final float ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000/65536f;
 
-    private void drawRoundJoin(int x, int y,
-                               int omx, int omy, int mx, int my,
+    private void drawRoundJoin(float x, float y,
+                               float omx, float omy, float mx, float my,
                                int side,
                                boolean flip,
                                boolean rev,
-                               long threshold) {
+                               float threshold) {
         if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) {
             return;
         }
 
-        long domx = (long)omx - mx;
-        long domy = (long)omy - my;
-        long len = domx*domx + domy*domy;
+        float domx = omx - mx;
+        float domy = omy - my;
+        float len = domx*domx + domy*domy;
         if (len < threshold) {
             return;
         }
@@ -389,10 +315,10 @@
             my = -my;
         }
 
-        int bx0 = x + omx;
-        int by0 = y + omy;
-        int bx1 = x + mx;
-        int by1 = y + my;
+        float bx0 = x + omx;
+        float by0 = y + omy;
+        float bx1 = x + mx;
+        float by1 = y + my;
 
         int npoints = computeRoundJoin(x, y,
                                        bx0, by0, bx1, by1, side, flip,
@@ -404,40 +330,30 @@
 
     // Return the intersection point of the lines (ix0, iy0) -> (ix1, iy1)
     // and (ix0p, iy0p) -> (ix1p, iy1p) in m[0] and m[1]
-    private void computeMiter(int ix0, int iy0, int ix1, int iy1,
-                              int ix0p, int iy0p, int ix1p, int iy1p,
-                              int[] m) {
-        long x0 = ix0;
-        long y0 = iy0;
-        long x1 = ix1;
-        long y1 = iy1;
+    private void computeMiter(float x0, float y0, float x1, float y1,
+                              float x0p, float y0p, float x1p, float y1p,
+                              float[] m) {
+        float x10 = x1 - x0;
+        float y10 = y1 - y0;
+        float x10p = x1p - x0p;
+        float y10p = y1p - y0p;
 
-        long x0p = ix0p;
-        long y0p = iy0p;
-        long x1p = ix1p;
-        long y1p = iy1p;
-
-        long x10 = x1 - x0;
-        long y10 = y1 - y0;
-        long x10p = x1p - x0p;
-        long y10p = y1p - y0p;
-
-        long den = (x10*y10p - x10p*y10) >> 16;
+        float den = x10*y10p - x10p*y10;
         if (den == 0) {
-            m[0] = ix0;
-            m[1] = iy0;
+            m[0] = x0;
+            m[1] = y0;
             return;
         }
 
-        long t = (x1p*(y0 - y0p) - x0*y10p + x0p*(y1p - y0)) >> 16;
-        m[0] = (int)(x0 + (t*x10)/den);
-        m[1] = (int)(y0 + (t*y10)/den);
+        float t = x1p*(y0 - y0p) - x0*y10p + x0p*(y1p - y0);
+        m[0] = x0 + (t*x10)/den;
+        m[1] = y0 + (t*y10)/den;
     }
 
-    private void drawMiter(int px0, int py0,
-                           int x0, int y0,
-                           int x1, int y1,
-                           int omx, int omy, int mx, int my,
+    private void drawMiter(float px0, float py0,
+                           float x0, float y0,
+                           float x1, float y1,
+                           float omx, float omy, float mx, float my,
                            boolean rev) {
         if (mx == omx && my == omy) {
             return;
@@ -461,11 +377,11 @@
                      miter);
 
         // Compute miter length in untransformed coordinates
-        long dx = (long)miter[0] - x0;
-        long dy = (long)miter[1] - y0;
-        long a = (dy*m00 - dx*m10) >> 16;
-        long b = (dy*m01 - dx*m11) >> 16;
-        long lenSq = a*a + b*b;
+        float dx = miter[0] - x0;
+        float dy = miter[1] - y0;
+        float a = dy*m00 - dx*m10;
+        float b = dy*m01 - dx*m11;
+        float lenSq = a*a + b*b;
 
         if (lenSq < miterLimitSq) {
             emitLineTo(miter[0], miter[1], rev);
@@ -473,7 +389,7 @@
     }
 
 
-    public void moveTo(int x0, int y0) {
+    public void moveTo(float x0, float y0) {
         // System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
 
         if (lineToOrigin) {
@@ -501,7 +417,7 @@
         this.joinSegment = true;
     }
 
-    public void lineTo(int x1, int y1) {
+    public void lineTo(float x1, float y1) {
         // System.out.println("Stroker.lineTo(" + x1/65536.0 + ", " + y1/65536.0 + ")");
 
         if (lineToOrigin) {
@@ -526,10 +442,10 @@
         joinSegment = false;
     }
 
-    private void lineToImpl(int x1, int y1, boolean joinSegment) {
+    private void lineToImpl(float x1, float y1, boolean joinSegment) {
         computeOffset(x0, y0, x1, y1, offset);
-        int mx = offset[0];
-        int my = offset[1];
+        float mx = offset[0];
+        float my = offset[1];
 
         if (!started) {
             emitMoveTo(x0 + mx, y0 + my);
@@ -567,10 +483,6 @@
         emitLineTo(x0 - mx, y0 - my, true);
         emitLineTo(x1 - mx, y1 - my, true);
 
-        lx0 = x1 + mx; ly0 = y1 + my;
-        lx0p = x1 - mx; ly0p = y1 - my;
-        lx1 = x1; ly1 = y1;
-
         this.omx = mx;
         this.omy = my;
         this.px0 = x0;
@@ -594,8 +506,8 @@
         }
 
         computeOffset(x0, y0, sx0, sy0, offset);
-        int mx = offset[0];
-        int my = offset[1];
+        float mx = offset[0];
+        float my = offset[1];
 
         // Draw penultimate join
         boolean ccw = isCCW(px0, py0, x0, y0, sx0, sy0);
@@ -678,12 +590,10 @@
         this.prev = MOVE_TO;
     }
 
-    long lineLength(long ldx, long ldy) {
-        long ldet = ((long)m00*m11 - (long)m01*m10) >> 16;
-        long la = ((long)ldy*m00 - (long)ldx*m10)/ldet;
-        long lb = ((long)ldy*m01 - (long)ldx*m11)/ldet;
-        long llen = (int)PiscesMath.hypot(la, lb);
-        return llen;
+    double userSpaceLineLength(double dx, double dy) {
+        double a = (dy*m00 - dx*m10)/det;
+        double b = (dy*m01 - dx*m11)/det;
+        return Math.hypot(a, b);
     }
 
     private void finish() {
@@ -692,13 +602,13 @@
                           omx, omy, -omx, -omy, 1, false, false,
                           ROUND_JOIN_THRESHOLD);
         } else if (capStyle == CAP_SQUARE) {
-            long ldx = (long)(px0 - x0);
-            long ldy = (long)(py0 - y0);
-            long llen = lineLength(ldx, ldy);
-            long s = (long)lineWidth2*65536/llen;
+            float dx = px0 - x0;
+            float dy = py0 - y0;
+            float len = (float)userSpaceLineLength(dx, dy);
+            float s = lineWidth2/len;
 
-            int capx = x0 - (int)(ldx*s >> 16);
-            int capy = y0 - (int)(ldy*s >> 16);
+            float capx = x0 - dx*s;
+            float capy = y0 - dy*s;
 
             emitLineTo(capx + omx, capy + omy);
             emitLineTo(capx - omx, capy - omy);
@@ -714,13 +624,13 @@
                           -mx0, -my0, mx0, my0, 1, false, false,
                           ROUND_JOIN_THRESHOLD);
         } else if (capStyle == CAP_SQUARE) {
-            long ldx = (long)(sx1 - sx0);
-            long ldy = (long)(sy1 - sy0);
-            long llen = lineLength(ldx, ldy);
-            long s = (long)lineWidth2*65536/llen;
+            float dx = sx1 - sx0;
+            float dy = sy1 - sy0;
+            float len = (float)userSpaceLineLength(dx, dy);
+            float s = lineWidth2/len;
 
-            int capx = sx0 - (int)(ldx*s >> 16);
-            int capy = sy0 - (int)(ldy*s >> 16);
+            float capx = sx0 - dx*s;
+            float capy = sy0 - dy*s;
 
             emitLineTo(capx - mx0, capy - my0);
             emitLineTo(capx + mx0, capy + my0);
@@ -730,17 +640,17 @@
         this.joinSegment = false;
     }
 
-    private void emitMoveTo(int x0, int y0) {
+    private void emitMoveTo(float x0, float y0) {
         // System.out.println("Stroker.emitMoveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
         output.moveTo(x0, y0);
     }
 
-    private void emitLineTo(int x1, int y1) {
+    private void emitLineTo(float x1, float y1) {
         // System.out.println("Stroker.emitLineTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
         output.lineTo(x1, y1);
     }
 
-    private void emitLineTo(int x1, int y1, boolean rev) {
+    private void emitLineTo(float x1, float y1, boolean rev) {
         if (rev) {
             ensureCapacity(rindex + 2);
             reverse[rindex++] = x1;
@@ -755,3 +665,4 @@
         output.close();
     }
 }
+
--- a/jdk/src/share/classes/sun/java2d/pisces/Transform4.java	Thu Jul 29 17:12:27 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.java2d.pisces;
-
-public class Transform4 {
-
-    public int m00, m01, m10, m11;
-//     double det; // det*65536
-
-    public Transform4() {
-        this(1 << 16, 0, 0, 1 << 16);
-    }
-
-    public Transform4(int m00, int m01,
-                      int m10, int m11) {
-        this.m00 = m00;
-        this.m01 = m01;
-        this.m10 = m10;
-        this.m11 = m11;
-
-//         this.det = (double)m00*m11 - (double)m01*m10;
-    }
-
-//     public Transform4 createInverse() {
-//         double dm00 = m00/65536.0;
-//         double dm01 = m01/65536.0;
-//         double dm10 = m10/65536.0;
-//         double dm11 = m11/65536.0;
-
-//         double invdet = 65536.0/(dm00*dm11 - dm01*dm10);
-
-//         int im00 = (int)( dm11*invdet);
-//         int im01 = (int)(-dm01*invdet);
-//         int im10 = (int)(-dm10*invdet);
-//         int im11 = (int)( dm00*invdet);
-
-//         return new Transform4(im00, im01, im10, im11);
-//     }
-
-//     public void transform(int[] point) {
-//     }
-
-//     /**
-//      * Returns the length of the line segment obtained by inverse
-//      * transforming the points <code>(x0, y0)</code> and <code>(x1,
-//      * y1)</code>.
-//      */
-//     public int getTransformedLength(int x0, int x1, int y0, int y1) {
-//         int lx = x1 - x0;
-//         int ly = y1 - y0;
-
-//         double a = (double)m00*ly - (double)m10*lx;
-//         double b = (double)m01*ly - (double)m11*lx;
-//         double len = PiscesMath.sqrt((a*a + b*b)/(det*det));
-//         return (int)(len*65536.0);
-//     }
-
-//     public int getType() {
-//     }
-
-}