diff options
-rw-r--r-- | ChangeLog | 38 | ||||
-rw-r--r-- | gnu/java/awt/java2d/CubicSegment.java | 78 | ||||
-rw-r--r-- | gnu/java/awt/java2d/LineSegment.java | 4 | ||||
-rw-r--r-- | gnu/java/awt/java2d/QuadSegment.java | 4 | ||||
-rw-r--r-- | gnu/java/awt/java2d/Segment.java | 37 | ||||
-rw-r--r-- | gnu/java/awt/peer/gtk/CairoGraphics2D.java | 19 | ||||
-rw-r--r-- | java/awt/BasicStroke.java | 120 |
7 files changed, 205 insertions, 95 deletions
@@ -1,3 +1,41 @@ +2006-07-27 Francis Kung <fkung@redhat.com> + + * gnu/java/awt/java2d/CubicSegment.java: Added import. + (cp1): Renamed from first(). + (c2): Renamed from last(). + (first): Renamed to cp1(). + (getDisplacedSegments): Implemented. + (last): Renamed to cp2(). + * gnu/java/awt/java2d/LineSegment.java + (cp1): Renamed from first(). + (c2): Renamed from last(). + (first): Renamed to cp1(). + (last): Renamed to cp2(). + * gnu/java/awt/java2d/QuadSegment.java + (cp1): Renamed from first(). + (c2): Renamed from last(). + (first): Renamed to cp1(). + (last): Renamed to cp2(). + * gnu/java/awt/java2d/Segment.java: Added comments. + (first): New field. + (Segment): Keep track of first element in list. + (add): Update first & last element variables. + (cp1): Renamed from first(). + (c2): Renamed from last(). + (first()): Renamed to cp1() to reduce ambiguity. + (last()): Renamed to cp2() to reduce ambiguity. + (reverseAll): Update first element variable.. + * gnu/java/awt/peer/gtk/CairoGraphics2D.java + (draw): Remove flattening path iterator. + * java/awt/BasicStroke.java: Clarified comments. + (addSegments): Refactored some code into joinSegments and + joinInnerSegments. + (capEnd): Rename of Segment.first() and Segment.end(). + (joinInnerSegments): New method. + (joinOuterSegments): New method. + (joinSegments): Refactored some code into joinOuterSegments. + (solidStroke): Connect segments together properly. + 2006-07-27 Tom Tromey <tromey@redhat.com> PR classpath/28486: diff --git a/gnu/java/awt/java2d/CubicSegment.java b/gnu/java/awt/java2d/CubicSegment.java index 7d169b78e..bf66be870 100644 --- a/gnu/java/awt/java2d/CubicSegment.java +++ b/gnu/java/awt/java2d/CubicSegment.java @@ -39,6 +39,7 @@ exception statement from your version. */ package gnu.java.awt.java2d; +import java.awt.geom.CubicCurve2D; import java.awt.geom.Point2D; /** @@ -100,28 +101,67 @@ public class CubicSegment extends Segment } /** - * Get the "top" and "bottom" segments of this segment. - * First array element is p0 + normal, second is p0 - normal. + * Get the "top" and "bottom" segments of this segment. First array element is + * p0 + normal, second is p0 - normal. */ public Segment[] getDisplacedSegments(double radius) { + // It is, apparently, impossible to derive a curve parallel to a bezier + // curve (unless it's a straight line), so we have no choice but to + // approximate the displaced segments. Similar to FlattenPathIterator. + + Segment segmentTop = null; + Segment segmentBottom = null; this.radius = radius; - double x0 = P1.getX(); - double y0 = P1.getY(); - double x1 = cp1.getX(); - double y1 = cp1.getY(); - double x2 = cp2.getX(); - double y2 = cp2.getY(); - double x3 = P2.getX(); - double y3 = P2.getY(); - double[] p1 = normal(x0, y0, x1, y1); - double[] p2 = normal(x2, y2, x3, y3); - - // FIXME: Doesn't compile. - // return new Segment[]{s1, s2}; - return new Segment[0]; - } + CubicCurve2D[] curves = new CubicCurve2D[10]; + curves[0] = new CubicCurve2D.Double(P1.getX(), P1.getY(), cp1.getX(), + cp1.getY(), cp2.getX(), cp2.getY(), + P2.getX(), P2.getY()); + int numCurves = 1; + + // Hard-coded a recursion limit of 10 and flatness of 1... should we make + // this an option somewhere? + while (numCurves > 0) + { + // The curve is flat enough, or we've reached our recursion limit, + // so take the current start/end points and add it as a line segment + // to our final approximated curves + if (curves[numCurves - 1].getFlatnessSq() <= (radius / 3) || numCurves == 10) + { + Segment[] displaced = new LineSegment( + curves[numCurves - 1].getP1(), + curves[numCurves - 1].getP2()).getDisplacedSegments(radius); + if (segmentTop == null) + { + segmentTop = displaced[0]; + segmentBottom = displaced[1]; + } + else + { + segmentTop.add(displaced[0]); + segmentBottom.add(displaced[1]); + } + numCurves--; + } + + // Otherwise, subdivide again and continue + else + { + CubicCurve2D left = new CubicCurve2D.Double(); + CubicCurve2D right = new CubicCurve2D.Double(); + curves[numCurves - 1].subdivide(left, right); + curves[numCurves - 1] = right; + curves[numCurves] = left; + curves[numCurves - 1] = right; + curves[numCurves] = left; + numCurves++; + } + } + + return new Segment[] { segmentTop, segmentBottom }; + } + public void reverse() { Point2D temp = P1; @@ -132,12 +172,12 @@ public class CubicSegment extends Segment cp2 = temp; } - public double[] first() + public double[] cp1() { return new double[]{cp1.getX(), cp1.getY()}; } - public double[] last() + public double[] cp2() { return new double[]{cp2.getX(), cp2.getY()}; } diff --git a/gnu/java/awt/java2d/LineSegment.java b/gnu/java/awt/java2d/LineSegment.java index 4a34aa563..0395fd0af 100644 --- a/gnu/java/awt/java2d/LineSegment.java +++ b/gnu/java/awt/java2d/LineSegment.java @@ -106,12 +106,12 @@ public class LineSegment extends Segment P2 = p; } - public double[] first() + public double[] cp1() { return new double[]{P2.getX(), P2.getY()}; } - public double[] last() + public double[] cp2() { return new double[]{P1.getX(), P1.getY()}; } diff --git a/gnu/java/awt/java2d/QuadSegment.java b/gnu/java/awt/java2d/QuadSegment.java index c76804840..5e15fe881 100644 --- a/gnu/java/awt/java2d/QuadSegment.java +++ b/gnu/java/awt/java2d/QuadSegment.java @@ -217,12 +217,12 @@ public class QuadSegment extends Segment P2 = p; } - public double[] first() + public double[] cp1() { return new double[]{cp.getX(), cp.getY()}; } - public double[] last() + public double[] cp2() { return new double[]{cp.getX(), cp.getY()}; } diff --git a/gnu/java/awt/java2d/Segment.java b/gnu/java/awt/java2d/Segment.java index 9a985f696..df1f67605 100644 --- a/gnu/java/awt/java2d/Segment.java +++ b/gnu/java/awt/java2d/Segment.java @@ -42,24 +42,38 @@ import java.awt.geom.Point2D; public abstract class Segment implements Cloneable { - // segment type, PathIterator segment types are used. + // Start and end points of THIS segment public Point2D P1; public Point2D P2; + + // Segments can be linked together internally as a linked list + public Segment first; public Segment next; public Segment last; + + // Half the stroke width protected double radius; + /** + * Create a new, empty segment + */ public Segment() { P1 = P2 = null; + first = this; next = null; last = this; } + /** + * Add a segment to the polygon + * @param newsegment segment to add + */ public void add(Segment newsegment) { + newsegment.first = first; last.next = newsegment; - last = last.next; + last = last.next.last; } /** @@ -68,6 +82,7 @@ public abstract class Segment implements Cloneable public void reverseAll() { reverse(); + first = last; Segment v = next; Segment former = this; next = null; @@ -91,7 +106,7 @@ public abstract class Segment implements Cloneable /** * Get the normal vector to the slope of the line. - * Returns: 0.5*width*(norm of derivative of the (x0,y0)-(x1,y1) vector) + * @return vector of length radius, normal to the (x0,y0)-(x1,y1) vector) */ protected double[] normal(double x0, double y0, double x1, double y1) { @@ -117,6 +132,9 @@ public abstract class Segment implements Cloneable return new double[]{ dx, dy }; } + /** + * Reverse the current segment + */ public abstract void reverse(); /** @@ -125,7 +143,16 @@ public abstract class Segment implements Cloneable */ public abstract Segment[] getDisplacedSegments(double radius); - public abstract double[] first(); - public abstract double[] last(); + /** + * Returns the coordinates of the first control point, or the start point + * for a line segment. + */ + public abstract double[] cp1(); + + /** + * Returns the coordinates of the second control point, or the end point + * for a line segment. + */ + public abstract double[] cp2(); } diff --git a/gnu/java/awt/peer/gtk/CairoGraphics2D.java b/gnu/java/awt/peer/gtk/CairoGraphics2D.java index 158965c76..e264300c5 100644 --- a/gnu/java/awt/peer/gtk/CairoGraphics2D.java +++ b/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -921,21 +921,12 @@ public abstract class CairoGraphics2D extends Graphics2D public void draw(Shape s) { if ((stroke != null && ! (stroke instanceof BasicStroke)) - || (comp instanceof AlphaComposite - && ((AlphaComposite) comp).getAlpha() != 1.0)) + || (comp instanceof AlphaComposite && ((AlphaComposite) comp).getAlpha() != 1.0)) { - // FIXME: This is a hack to work around BasicStrokes's current - // limitations wrt cubic curves. - // See CubicSegment.getDisplacedSegments(). - if (stroke instanceof BasicStroke) - { - PathIterator flatten = s.getPathIterator(null, 1.0); - GeneralPath p = new GeneralPath(); - p.append(flatten, false); - s = p; - } - fill(stroke.createStrokedShape(s)); - return; + // Cairo doesn't support stroking with alpha, so we create the stroked + // shape and fill with alpha instead + fill(stroke.createStrokedShape(s)); + return; } createPath(s); diff --git a/java/awt/BasicStroke.java b/java/awt/BasicStroke.java index a7e958890..160a3eb0f 100644 --- a/java/awt/BasicStroke.java +++ b/java/awt/BasicStroke.java @@ -117,6 +117,7 @@ public class BasicStroke implements Stroke /** The dash phase. */ private final float phase; + // The inner and outer paths of the stroke private Segment start, end; /** @@ -434,8 +435,8 @@ public class BasicStroke implements Stroke else addSegments(p); - x = coords[0]; - y = coords[1]; + x = coords[2]; + y = coords[3]; break; case PathIterator.SEG_CUBICTO: @@ -451,17 +452,25 @@ public class BasicStroke implements Stroke else addSegments(p); - x = coords[0]; - y = coords[1]; + x = coords[4]; + y = coords[5]; break; case PathIterator.SEG_CLOSE: - p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width/2.0); - addSegments(p); + 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(); @@ -498,7 +507,7 @@ public class BasicStroke implements Stroke } /** - * Convert and add the linked list of Segments in s to a GeneralPath p. + * Append the Segments in s to the GeneralPath p */ private void convertPath(GeneralPath p, Segment s) { @@ -526,18 +535,28 @@ public class BasicStroke implements Stroke p.closePath(); } - + /** - * Add to segments to start and end, joining the outer pair and + * Add the segments to start and end (the inner and outer edges of the stroke) */ private void addSegments(Segment[] segments) { - double[] p0 = start.last.last(); + 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].P1.getX(), segments[0].P1.getY()}; - double[] p3 = segments[0].first(); + 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]); @@ -545,42 +564,14 @@ public class BasicStroke implements Stroke { // start and segment[0] form the 'inner' part of a join, // connect the overlapping segments - p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], false); - if( p == null ) - { - // Dodgy. - start.add(new LineSegment(start.last.P2, segments[0].P1)); - p = new Point2D.Double((segments[0].P1.getX()+ start.last.P2.getX())/2.0, - (segments[0].P1.getY()+ start.last.P2.getY())/2.0); - } - else - segments[0].P1 = start.last.P2 = p; - - start.add( segments[0] ); - joinSegments(end, segments[1], p); + joinInnerSegments(start, segments[0], p); + joinOuterSegments(end, segments[1], p); } else { // end and segment[1] form the 'inner' part - p0 = end.last.last(); - p1 = new double[]{end.last.P2.getX(), end.last.P2.getY()}; - p2 = new double[]{segments[1].P1.getX(), segments[1].P1.getY()}; - p3 = segments[1].first(); - - p = lineIntersection(p0[0],p0[1],p1[0],p1[1], - p2[0],p2[1],p3[0],p3[1], false); - if( p == null ) - { - // Dodgy. - end.add(new LineSegment(end.last.P2, segments[1].P1)); - p = new Point2D.Double((segments[1].P1.getX()+ end.last.P2.getX())/2.0, - (segments[1].P1.getY()+ end.last.P2.getY())/2.0); - } - else - segments[1].P1 = end.last.P2 = p; - - end.add( segments[1] ); - joinSegments(start, segments[0], p); + joinInnerSegments(end, segments[1], p); + joinOuterSegments(start, segments[0], p); } } @@ -601,7 +592,7 @@ public class BasicStroke implements Stroke break; case CAP_SQUARE: - p0 = a.last.last(); + 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]; @@ -616,7 +607,7 @@ public class BasicStroke implements Stroke break; case CAP_ROUND: - p0 = a.last.last(); + 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]; @@ -675,7 +666,7 @@ public class BasicStroke implements Stroke * insideP is the inside intersection point of the join, needed for * calculating miter lengths. */ - private void joinSegments(Segment a, Segment b, Point2D insideP) + private void joinOuterSegments(Segment a, Segment b, Point2D insideP) { double[] p0, p1; double dx, dy, l; @@ -684,10 +675,10 @@ public class BasicStroke implements Stroke switch( join ) { case JOIN_MITER: - p0 = a.last.last(); + 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.first(); + 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)); @@ -704,7 +695,7 @@ public class BasicStroke implements Stroke break; case JOIN_ROUND: - p0 = a.last.last(); + 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]; @@ -714,7 +705,7 @@ public class BasicStroke implements Stroke c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); p0 = new double[]{b.P1.getX(), b.P1.getY()}; - p1 = b.first(); + p1 = b.cp1(); dx = p0[0] - p1[0]; // backwards direction. dy = p0[1] - p1[1]; @@ -729,6 +720,29 @@ public class BasicStroke implements Stroke a.add(new LineSegment(a.last.P2, b.P1)); break; } - a.add(b); } -} + + /** + * 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; + } +} |