diff -urN openjdk.orig/jdk/src/share/classes/java/awt/BasicStroke.java openjdk/jdk/src/share/classes/java/awt/BasicStroke.java --- openjdk.orig/jdk/src/share/classes/java/awt/BasicStroke.java 2007-10-12 04:02:07.000000000 -0400 +++ openjdk/jdk/src/share/classes/java/awt/BasicStroke.java 2007-10-16 22:13:51.000000000 -0400 @@ -1,5 +1,7 @@ /* * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Portions Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + * see below for license notice. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +27,17 @@ package java.awt; +import gnu.java.awt.CubicSegment; +import gnu.java.awt.LineSegment; +import gnu.java.awt.QuadSegment; +import gnu.java.awt.Segment; + +import java.awt.geom.FlatteningPathIterator; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.util.Arrays; + /** * The <code>BasicStroke</code> class defines a basic set of rendering * attributes for the outlines of graphics primitives, which are rendered @@ -156,6 +169,8 @@ float dash[]; float dash_phase; + private Segment start, end; + /** * Constructs a new <code>BasicStroke</code> with the specified * attributes. @@ -291,10 +307,12 @@ * @return the <code>Shape</code> of the stroked outline. */ public Shape createStrokedShape(Shape s) { - sun.java2d.pipe.RenderingEngine re = - sun.java2d.pipe.RenderingEngine.getInstance(); - return re.createStrokedShape(s, width, cap, join, miterlimit, - dash, dash_phase); + PathIterator pi = s.getPathIterator(null); + + if( dash == null ) + return solidStroke(pi); + + return dashedStroke(pi); } /** @@ -439,4 +456,511 @@ return true; } + +/* + * This rest of this file is from GNU Classpath. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath 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 for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ + private Shape solidStroke(PathIterator pi) { + double[] coords = new double[6]; + double x, y, x0, y0; + boolean pathOpen = false; + GeneralPath output = new GeneralPath (); + Segment[] p; + x = x0 = y = y0 = 0; + + while (!pi.isDone()) { + switch (pi.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + x0 = x = coords[0]; + y0 = y = coords[1]; + if (pathOpen) { + capEnds(); + convertPath(output, start); + start = end = null; + pathOpen = false; + } + break; + + case PathIterator.SEG_LINETO: + p = (new LineSegment(x, y, coords[0], coords[1])). + getDisplacedSegments(width/2.0); + if (!pathOpen) { + start = p[0]; + end = p[1]; + pathOpen = true; + } else { + addSegments(p); + } + + x = coords[0]; + y = coords[1]; + break; + + case PathIterator.SEG_QUADTO: + p = (new QuadSegment(x, y, coords[0], coords[1], coords[2], + coords[3])).getDisplacedSegments(width/2.0); + if (!pathOpen) { + start = p[0]; + end = p[1]; + pathOpen = true; + } else { + addSegments(p); + } + + x = coords[2]; + y = coords[3]; + break; + + case PathIterator.SEG_CUBICTO: + p = new CubicSegment(x, y, coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]).getDisplacedSegments(width/2.0); + if (!pathOpen) { + start = p[0]; + end = p[1]; + pathOpen = true; + } else { + addSegments(p); + } + + x = coords[4]; + y = coords[5]; + break; + + case PathIterator.SEG_CLOSE: + if (x == x0 && y == y0) { + joinSegments(new Segment[] { start.first, end.first }); + } else { + p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width / 2.0); + addSegments(p); + } + convertPath(output, start); + convertPath(output, end); + start = end = null; + pathOpen = false; + output.setWindingRule(GeneralPath.WIND_EVEN_ODD); + break; + } + pi.next(); + } + + if (pathOpen) { + capEnds(); + convertPath(output, start); + } + return output; + } + + private Shape dashedStroke(PathIterator pi) + { + // The choice of (flatnessSq == width / 3) is made to be consistent with + // the flattening in CubicSegment.getDisplacedSegments + FlatteningPathIterator flat = new FlatteningPathIterator(pi, + Math.sqrt(width / 3)); + + // Holds the endpoint of the current segment (or piece of a segment) + double[] coords = new double[2]; + + // Holds end of the last segment + double x, y, x0, y0; + x = x0 = y = y0 = 0; + + // Various useful flags + boolean pathOpen = false; + boolean dashOn = true; + boolean offsetting = (dash_phase != 0); + + // How far we are into the current dash + double distance = 0; + int dashIndex = 0; + + // And variables to hold the final output + GeneralPath output = new GeneralPath(); + Segment[] p; + + // Iterate over the FlatteningPathIterator + while (! flat.isDone()) { + switch (flat.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + x0 = x = coords[0]; + y0 = y = coords[1]; + + if (pathOpen) { + capEnds(); + convertPath(output, start); + start = end = null; + pathOpen = false; + } + + break; + + case PathIterator.SEG_LINETO: + boolean segmentConsumed = false; + + while (! segmentConsumed) { + // Find the total remaining length of this segment + double segLength = Math.sqrt((x - coords[0]) * (x - coords[0]) + + (y - coords[1]) + * (y - coords[1])); + boolean spanBoundary = true; + double[] segmentEnd = null; + + // The current segment fits entirely inside the current dash + if ((offsetting && distance + segLength <= dash_phase) + || distance + segLength <= dash[dashIndex]) { + spanBoundary = false; + + } else { + // Otherwise, we need to split the segment in two, as this + // segment spans a dash boundry + segmentEnd = (double[]) coords.clone(); + + // Calculate the remaining distance in this dash, + // and coordinates of the dash boundary + double reqLength; + if (offsetting) + reqLength = dash_phase - distance; + else + reqLength = dash[dashIndex] - distance; + + coords[0] = x + ((coords[0] - x) * reqLength / segLength); + coords[1] = y + ((coords[1] - y) * reqLength / segLength); + } + + if (offsetting || ! dashOn) { + // Dash is off, or we are in offset - treat this as a + // moveTo + x0 = x = coords[0]; + y0 = y = coords[1]; + + if (pathOpen) { + capEnds(); + convertPath(output, start); + start = end = null; + pathOpen = false; + } + } else { + // Dash is on - treat this as a lineTo + p = (new LineSegment(x, y, coords[0], coords[1])).getDisplacedSegments(width / 2.0); + + if (! pathOpen) { + start = p[0]; + end = p[1]; + pathOpen = true; + } else { + addSegments(p); + } + + x = coords[0]; + y = coords[1]; + } + + // Update variables depending on whether we spanned a + // dash boundary or not + if (! spanBoundary) { + distance += segLength; + segmentConsumed = true; + } else { + if (offsetting) + offsetting = false; + dashOn = ! dashOn; + distance = 0; + coords = segmentEnd; + + if (dashIndex + 1 == dash.length) + dashIndex = 0; + else + dashIndex++; + + // Since the value of segmentConsumed is still false, + // the next run of the while loop will complete the segment + } + } + break; + + // This is a flattened path, so we don't need to deal with curves + } + flat.next(); + } + + if (pathOpen) { + capEnds(); + convertPath(output, start); + } + return output; + } + + /** + * Cap the ends of the path (joining the start and end list of segments) + */ + private void capEnds() { + Segment returnPath = end.last; + + end.reverseAll(); // reverse the path. + end = null; + capEnd(start, returnPath); + start.last = returnPath.last; + end = null; + + capEnd(start, start); + } + + /** + * Append the Segments in s to the GeneralPath p + */ + private void convertPath(GeneralPath p, Segment s) { + Segment v = s; + p.moveTo((float)s.P1.getX(), (float)s.P1.getY()); + + do { + if(v instanceof LineSegment) + p.lineTo((float)v.P2.getX(), (float)v.P2.getY()); + else if(v instanceof QuadSegment) + p.quadTo((float)((QuadSegment)v).cp.getX(), + (float)((QuadSegment)v).cp.getY(), + (float)v.P2.getX(), + (float)v.P2.getY()); + else if(v instanceof CubicSegment) + p.curveTo((float)((CubicSegment)v).cp1.getX(), + (float)((CubicSegment)v).cp1.getY(), + (float)((CubicSegment)v).cp2.getX(), + (float)((CubicSegment)v).cp2.getY(), + (float)v.P2.getX(), + (float)v.P2.getY()); + v = v.next; + } while(v != s && v != null); + + p.closePath(); + } + + /** + * Add the segments to start and end (the inner and outer edges of the stroke) + */ + private void addSegments(Segment[] segments) { + joinSegments(segments); + start.add(segments[0]); + end.add(segments[1]); + } + + private void joinSegments(Segment[] segments) { + double[] p0 = start.last.cp2(); + double[] p1 = new double[]{start.last.P2.getX(), start.last.P2.getY()}; + double[] p2 = new double[]{segments[0].first.P1.getX(), segments[0].first.P1.getY()}; + double[] p3 = segments[0].cp1(); + Point2D p; + + p = lineIntersection(p0[0],p0[1],p1[0],p1[1], + p2[0],p2[1],p3[0],p3[1], false); + + double det = (p1[0] - p0[0])*(p3[1] - p2[1]) - + (p3[0] - p2[0])*(p1[1] - p0[1]); + + if (det > 0) { + // start and segment[0] form the 'inner' part of a join, + // connect the overlapping segments + joinInnerSegments(start, segments[0], p); + joinOuterSegments(end, segments[1], p); + } else { + // end and segment[1] form the 'inner' part + joinInnerSegments(end, segments[1], p); + joinOuterSegments(start, segments[0], p); + } + } + + /** + * Make a cap between a and b segments, + * where a-->b is the direction of iteration. + */ + private void capEnd(Segment a, Segment b) { + double[] p0, p1; + double dx, dy, l; + Point2D c1,c2; + + switch (cap) { + case CAP_BUTT: + a.add(new LineSegment(a.last.P2, b.P1)); + break; + + case CAP_SQUARE: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + dx = p1[0] - p0[0]; + dy = p1[1] - p0[1]; + l = Math.sqrt(dx * dx + dy * dy); + dx = 0.5*width*dx/l; + dy = 0.5*width*dy/l; + c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); + c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy); + a.add(new LineSegment(a.last.P2, c1)); + a.add(new LineSegment(c1, c2)); + a.add(new LineSegment(c2, b.P1)); + break; + + case CAP_ROUND: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + dx = p1[0] - p0[0]; + dy = p1[1] - p0[1]; + if (dx != 0 && dy != 0) { + l = Math.sqrt(dx * dx + dy * dy); + dx = (2.0/3.0)*width*dx/l; + dy = (2.0/3.0)*width*dy/l; + } + + c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); + c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy); + a.add(new CubicSegment(a.last.P2, c1, c2, b.P1)); + break; + } + a.add(b); + } + + /** + * Returns the intersection of two lines, or null if there isn't one. + * @param infinite - true if the lines should be regarded as infinite, false + * if the intersection must be within the given segments. + * @return a Point2D or null. + */ + private Point2D lineIntersection(double X1, double Y1, + double X2, double Y2, + double X3, double Y3, + double X4, double Y4, + boolean infinite) { + double x1 = X1; + double y1 = Y1; + double rx = X2 - x1; + double ry = Y2 - y1; + + double x2 = X3; + double y2 = Y3; + double sx = X4 - x2; + double sy = Y4 - y2; + + double determinant = sx * ry - sy * rx; + double nom = (sx * (y2 - y1) + sy * (x1 - x2)); + + // lines can be considered parallel. + if (Math.abs(determinant) < 1E-6) + return null; + + nom = nom / determinant; + + // check if lines are within the bounds + if(!infinite && (nom > 1.0 || nom < 0.0)) + return null; + + return new Point2D.Double(x1 + nom * rx, y1 + nom * ry); + } + + /** + * Join a and b segments, where a-->b is the direction of iteration. + * + * insideP is the inside intersection point of the join, needed for + * calculating miter lengths. + */ + private void joinOuterSegments(Segment a, Segment b, Point2D insideP) { + double[] p0, p1; + double dx, dy, l; + Point2D c1,c2; + + switch (join) { + case JOIN_MITER: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + double[] p2 = new double[]{b.P1.getX(), b.P1.getY()}; + double[] p3 = b.cp1(); + Point2D p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], true); + if( p == null || insideP == null ) + a.add(new LineSegment(a.last.P2, b.P1)); + else if((p.distance(insideP)/width) < miterlimit) { + a.add(new LineSegment(a.last.P2, p)); + a.add(new LineSegment(p, b.P1)); + } else { + // outside miter limit, do a bevel join. + a.add(new LineSegment(a.last.P2, b.P1)); + } + break; + + case JOIN_ROUND: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + dx = p1[0] - p0[0]; + dy = p1[1] - p0[1]; + l = Math.sqrt(dx * dx + dy * dy); + dx = 0.5*width*dx/l; + dy = 0.5*width*dy/l; + c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); + + p0 = new double[]{b.P1.getX(), b.P1.getY()}; + p1 = b.cp1(); + + dx = p0[0] - p1[0]; // backwards direction. + dy = p0[1] - p1[1]; + l = Math.sqrt(dx * dx + dy * dy); + dx = 0.5*width*dx/l; + dy = 0.5*width*dy/l; + c2 = new Point2D.Double(p0[0] + dx, p0[1] + dy); + a.add(new CubicSegment(a.last.P2, c1, c2, b.P1)); + break; + + case JOIN_BEVEL: + a.add(new LineSegment(a.last.P2, b.P1)); + break; + } + } + + /** + * Join a and b segments, removing any overlap + */ + private void joinInnerSegments(Segment a, Segment b, Point2D p) { + double[] p0 = a.last.cp2(); + double[] p1 = new double[] { a.last.P2.getX(), a.last.P2.getY() }; + double[] p2 = new double[] { b.P1.getX(), b.P1.getY() }; + double[] p3 = b.cp1(); + + if (p == null) { + // Dodgy. + a.add(new LineSegment(a.last.P2, b.P1)); + p = new Point2D.Double((b.P1.getX() + a.last.P2.getX()) / 2.0, + (b.P1.getY() + a.last.P2.getY()) / 2.0); + } else { + // This assumes segments a and b are single segments, which is + // incorrect - if they are a linked list of segments (ie, passed in + // from a flattening operation), this produces strange results!! + a.last.P2 = b.P1 = p; + } + } } diff -urN openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java openjdk/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java 2007-10-12 04:03:11.000000000 -0400 +++ openjdk/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java 2007-10-16 22:07:44.000000000 -0400 @@ -423,6 +423,7 @@ transy = 0; } drawPath(sg2d, p2df, transx, transy); +/* } else if (sg2d.strokeState < sg2d.STROKE_CUSTOM) { ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s); try { @@ -431,6 +432,7 @@ si.dispose(); } } else { +*/ fill(sg2d, sg2d.stroke.createStrokedShape(s)); } } --- ../oipenjdkb23/openjdk/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java 2007-10-30 04:37:34.000000000 -0400 +++ openjdk/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java 2007-11-13 13:01:58.000000000 -0500 @@ -188,7 +188,7 @@ transX, transY, p2df); return; } - +/* if (sg2d.strokeState == sg2d.STROKE_CUSTOM) { fill(sg2d, sg2d.stroke.createStrokedShape(s)); return; @@ -201,6 +201,8 @@ } finally { sr.dispose(); } +*/ + fill(sg2d, sg2d.stroke.createStrokedShape(s)); } /** --- ../opienjdkb23/openjdk/jdk/src/share/classes/sun/java2d/pipe/SpanShapeRenderer.java 2007-10-30 04:37:35.000000000 -0400 +++ openjdk/jdk/src/share/classes/sun/java2d/pipe/SpanShapeRenderer.java 2007-11-13 13:02:26.000000000 -0500 @@ -82,6 +82,7 @@ } public void draw(SunGraphics2D sg, Shape s) { +/* if (sg.stroke instanceof BasicStroke) { ShapeSpanIterator sr = LoopPipe.getStrokeSpans(sg, s); try { @@ -89,9 +90,9 @@ } finally { sr.dispose(); } - } else { + } else {*/ fill(sg, sg.stroke.createStrokedShape(s)); - } +// } } public static final int NON_RECTILINEAR_TRANSFORM_MASK = --- ../opeinjdkb23/openjdk/jdk/src/solaris/classes/sun/java2d/x11/X11Renderer.java 2007-10-30 04:39:14.000000000 -0400 +++ openjdk/jdk/src/solaris/classes/sun/java2d/x11/X11Renderer.java 2007-11-13 13:03:24.000000000 -0500 @@ -336,6 +336,7 @@ // Otherwise we will use drawPath() for // high-quality thin paths. doPath(sg2d, s, false); +/* } else if (sg2d.strokeState < sg2d.STROKE_CUSTOM) { // REMIND: X11 can handle uniform scaled wide lines // and dashed lines itself if we set the appropriate @@ -354,6 +355,7 @@ } finally { si.dispose(); } +*/ } else { fill(sg2d, sg2d.stroke.createStrokedShape(s)); }