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
--- 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() {
-// }
-
-}