Wow, this looks interesting. I will test it tommorow with piccolo[0]. cya Robert
[0] - http://www.cs.umd.edu/hcil/jazz/ Francis Kung wrote: > Hello, > > Attached is a patch to implement, fix, and clean up aspects of > BasicStroke.createStrokedShape() > > I would appreciate any comments anyone might have. > > Thanks, > Francis > > > 2006-07-25 Francis Kung <[EMAIL PROTECTED]> > > * 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. > > > > ------------------------------------------------------------------------ > > Index: gnu/java/awt/java2d/CubicSegment.java > =================================================================== > RCS file: /cvsroot/classpath/classpath/gnu/java/awt/java2d/CubicSegment.java,v > retrieving revision 1.2 > diff -u -r1.2 CubicSegment.java > --- gnu/java/awt/java2d/CubicSegment.java 10 Jul 2006 18:43:38 -0000 > 1.2 > +++ gnu/java/awt/java2d/CubicSegment.java 25 Jul 2006 21:38:29 -0000 > @@ -39,6 +39,7 @@ > package gnu.java.awt.java2d; > > > +import java.awt.geom.CubicCurve2D; > import java.awt.geom.Point2D; > > /** > @@ -100,28 +101,67 @@ > } > > /** > - * 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].getFlatness() <= 1 || 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 @@ > 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()}; > } > Index: gnu/java/awt/java2d/LineSegment.java > =================================================================== > RCS file: /cvsroot/classpath/classpath/gnu/java/awt/java2d/LineSegment.java,v > retrieving revision 1.2 > diff -u -r1.2 LineSegment.java > --- gnu/java/awt/java2d/LineSegment.java 10 Jul 2006 18:43:38 -0000 > 1.2 > +++ gnu/java/awt/java2d/LineSegment.java 25 Jul 2006 21:38:29 -0000 > @@ -106,12 +106,12 @@ > 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()}; > } > Index: gnu/java/awt/java2d/QuadSegment.java > =================================================================== > RCS file: /cvsroot/classpath/classpath/gnu/java/awt/java2d/QuadSegment.java,v > retrieving revision 1.2 > diff -u -r1.2 QuadSegment.java > --- gnu/java/awt/java2d/QuadSegment.java 10 Jul 2006 18:43:38 -0000 > 1.2 > +++ gnu/java/awt/java2d/QuadSegment.java 25 Jul 2006 21:38:29 -0000 > @@ -217,12 +217,12 @@ > 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()}; > } > Index: gnu/java/awt/java2d/Segment.java > =================================================================== > RCS file: /cvsroot/classpath/classpath/gnu/java/awt/java2d/Segment.java,v > retrieving revision 1.1 > diff -u -r1.1 Segment.java > --- gnu/java/awt/java2d/Segment.java 24 Apr 2006 14:37:23 -0000 1.1 > +++ gnu/java/awt/java2d/Segment.java 25 Jul 2006 21:38:29 -0000 > @@ -42,24 +42,38 @@ > > 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 void reverseAll() > { > reverse(); > + first = last; > Segment v = next; > Segment former = this; > next = null; > @@ -91,7 +106,7 @@ > > /** > * 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 @@ > return new double[]{ dx, dy }; > } > > + /** > + * Reverse the current segment > + */ > public abstract void reverse(); > > /** > @@ -125,7 +143,16 @@ > */ > 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(); > > } > Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java > =================================================================== > RCS file: > /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v > retrieving revision 1.33 > diff -u -r1.33 CairoGraphics2D.java > --- gnu/java/awt/peer/gtk/CairoGraphics2D.java 25 Jul 2006 20:58:19 > -0000 1.33 > +++ gnu/java/awt/peer/gtk/CairoGraphics2D.java 25 Jul 2006 21:38:29 > -0000 > @@ -921,21 +921,12 @@ > 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); > Index: java/awt/BasicStroke.java > =================================================================== > RCS file: /cvsroot/classpath/classpath/java/awt/BasicStroke.java,v > retrieving revision 1.14 > diff -u -r1.14 BasicStroke.java > --- java/awt/BasicStroke.java 10 Jul 2006 18:43:38 -0000 1.14 > +++ java/awt/BasicStroke.java 25 Jul 2006 21:38:30 -0000 > @@ -117,6 +117,7 @@ > /** The dash phase. */ > private final float phase; > > + // The inner and outer paths of the stroke > private Segment start, end; > > /** > @@ -434,8 +435,8 @@ > else > addSegments(p); > > - x = coords[0]; > - y = coords[1]; > + x = coords[2]; > + y = coords[3]; > break; > > case PathIterator.SEG_CUBICTO: > @@ -451,17 +452,25 @@ > 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 @@ > } > > /** > - * 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 @@ > > 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 @@ > { > // 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 @@ > 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 @@ > 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 @@ > * 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 @@ > 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 @@ > 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 @@ > 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 @@ > 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; > + } > +}
signature.asc
Description: OpenPGP digital signature
