--- old/src/share/classes/sun/java2d/pisces/Dasher.java 2010-06-16 16:16:50.584649271 +0100
+++ new/src/share/classes/sun/java2d/pisces/Dasher.java 2010-06-16 16:16:50.419107155 +0100
@@ -181,33 +181,37 @@
}
public void lineTo(int x1, int y1) {
+ long MAX = java.lang.Integer.MAX_VALUE;
+ long MIN = java.lang.Integer.MIN_VALUE;
while (true) {
+ // We use longs in this method to avoid overflows.
+
int d = dash[idx] - phase;
- int lx = x1 - x0;
- int ly = y1 - y0;
+ long xLen = (long)x1 - x0;
+ long yLen = (long)y1 - y0;
// Compute segment length in the untransformed
// coordinate system
// IMPL NOTE - use fixed point
- int l;
+ long origLen;
if (symmetric) {
- l = (int)((PiscesMath.hypot(lx, ly)*65536L)/ldet);
+ origLen = (int)((PiscesMath.hypot(xLen, yLen)*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);
+ long origXLen = (m11*xLen - m01*yLen)/ldet;
+ long origYLen = (m00*yLen - m10*xLen)/ldet;
+ origLen = PiscesMath.hypot(origXLen, origYLen);
}
- if (l < d) {
+ if (origLen < d) {
goTo(x1, y1);
// Advance phase within current dash segment
- phase += l;
+ phase += origLen;
return;
}
- long t;
- int xsplit, ysplit;
+ long t; // the ratio (olddash/oldlinelength) * 2^16
+ long xsplit, ysplit;
// // For zero length dashses, SE appears to move 1/8 unit
// // in device space
// if (d == 0) {
@@ -220,11 +224,26 @@
// 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);
+ t = ((long)d << 16)/origLen;
+ xsplit = x0 + (t * xLen >> 16);
+ ysplit = y0 + (t * yLen >> 16);
// }
- goTo(xsplit, ysplit);
+
+ // If java math had infinite precision, xsplit and ysplit would
+ // be guaranteed never to go outside of the range [MIN, MAX], but
+ // it isn't, and an overflow here would be hard to diagnose, so
+ // the efficiency trade off is worth it.
+ // Note: I have not proven mathematicaly there there is, indeed,
+ // some input that could would overflow here, so it might be
+ // nice to try to prove that overflow is impossible. If it is,
+ // the next 6 lines can be eliminated.
+ if (xsplit > MAX || xsplit < MIN) {
+ xsplit = (xsplit > MAX) ? MAX : MIN;
+ }
+ if (ysplit > MAX || ysplit < MIN) {
+ ysplit = (ysplit > MAX) ? MAX : MIN;
+ }
+ goTo((int)xsplit, (int)ysplit);
// Advance to next dash segment
idx = (idx + 1) % dash.length;
--- old/src/share/classes/sun/java2d/pisces/Stroker.java 2010-06-16 16:16:51.507550899 +0100
+++ new/src/share/classes/sun/java2d/pisces/Stroker.java 2010-06-16 16:16:51.342489094 +0100
@@ -113,6 +113,10 @@
double m10_2_m11_2;
double m00_m10_m01_m11;
+ // An upper bound on the width the the transformed line and the
+ // untransformed line.
+ int wub;
+
/**
* Empty constructor. setOutput
and
* setParameters
must be called prior to calling any
@@ -227,6 +231,9 @@
pen_dy[i] = (int)(r*(dm10*cos + dm11*sin));
}
+ wub = (int)((lineWidth * PiscesMath.hypot((long)m01+m00, (long)m10+m11)) >> 16);
+ wub = (wub > lineWidth) ? wub : lineWidth;
+
prev = CLOSE;
rindex = 0;
started = false;
@@ -276,11 +283,12 @@
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;
+ // These need to be longs to avoid overflows.
+ long dx0 = (long)x1 - x0;
+ long dy0 = (long)y1 - y0;
+ long dx1 = (long)x2 - x1;
+ long dy1 = (long)y2 - y1;
+ return dx0*dy1 < dy0*dx1;
}
private boolean side(int x, int y, int x0, int y0, int x1, int y1) {
@@ -472,10 +480,35 @@
}
}
+ private static void makeOverflowSafe(int[] coords, int wub) {
+ int x = coords[0];
+ int y = coords[1];
+ if ((long)x + wub != x + wub) {
+ x -= wub;
+ }
+ if ((long)y + wub != y + wub) {
+ y -= wub;
+ }
+ if ((long)x - wub != x - wub) {
+ x += wub;
+ }
+ if ((long)y - wub != y - wub) {
+ y += wub;
+ }
+ coords[0] = x;
+ coords[1] = y;
+ }
public void moveTo(int x0, int y0) {
// System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")");
+ // We can't move too close to the borders set by Integer.MAX_VALUE or
+ // MIN_VALUE, because when we try to draw an endcap, or even just adding
+ // offsets to (x0,y0), overflows might happen.
+ int[] coords = {x0, y0};
+ makeOverflowSafe(coords, wub);
+ x0 = coords[0]; y0 = coords[1];
+
if (lineToOrigin) {
// not closing the path, do the previous lineTo
lineToImpl(sx0, sy0, joinToOrigin);
@@ -531,6 +564,16 @@
int mx = offset[0];
int my = offset[1];
+ // Now we want to make sure that none of the operations involving line
+ // drawing will overflow. These include adding pen points to (x1,y1),
+ // drawing a square end cap, and adding/subtracting mx and my to x1
+ // and y1 (in the calls to emitLineTo). However, it does not protect
+ // against possible overflows when drawing miters.
+ // TODO: protect against possible overflows when drawing miters.
+ int[] coords = {x1, y1};
+ makeOverflowSafe(coords, wub);
+ x1 = coords[0]; y1 = coords[1];
+
if (!started) {
emitMoveTo(x0 + mx, y0 + my);
this.sx1 = x1;