Hi,
This patch fixes the 0.5 pixel shifting we do in draw operations by
applying a device space -> user space transform first, so that a scaling
factor in the transform will not distort the shape. This necessitates
separate shifting methods for X and Y coordinates.
Also included is a fix for rectangle drawing (if the rectangle's
coordinates are shifted, the width/height must be adjusted to
compensate), and an optimization for buffered image glyph drawing.
Cheers,
Francis
2006-11-22 Francis Kung <[EMAIL PROTECTED]>
* gnu/java/awt/peer/gtk/BufferedImageGraphics.java
(drawGlyphVector): Clip updated area to glyph bounds.
* gnu/java/awt/peer/gtk/CairoGraphics2D.java
(createPath): Eliminate distortion when pixel-shifting rectangles;
separate
x-coordinate and y-coordinate pixel shifting.
(shifted): Removed method.
(shiftX): New method, recognising scaling transforms.
(shiftY): New method, recognising scaling transforms.
(walkPath): Separate x-coordinate and y-coordinate pixel shifting.
Index: gnu/java/awt/peer/gtk/BufferedImageGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java,v
retrieving revision 1.17
diff -u -r1.17 BufferedImageGraphics.java
--- gnu/java/awt/peer/gtk/BufferedImageGraphics.java 21 Nov 2006 21:21:36 -0000 1.17
+++ gnu/java/awt/peer/gtk/BufferedImageGraphics.java 22 Nov 2006 16:40:14 -0000
@@ -379,10 +379,17 @@
public void drawGlyphVector(GlyphVector gv, float x, float y)
{
+ // Find absolute bounds, in user-space, of this glyph vector
+ Rectangle2D bounds = gv.getLogicalBounds();
+ bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(),
+ bounds.getWidth(), bounds.getHeight());
+
+ // Perform draw operation
if (comp == null || comp instanceof AlphaComposite)
{
super.drawGlyphVector(gv, x, y);
- updateBufferedImage(0, 0, imageWidth, imageHeight);
+ updateBufferedImage((int)bounds.getX(), (int)bounds.getY(),
+ (int)bounds.getWidth(), (int)bounds.getHeight());
}
else
{
@@ -393,9 +400,6 @@
g2d.setStroke(this.getStroke());
g2d.drawGlyphVector(gv, x, y);
- Rectangle2D bounds = gv.getLogicalBounds();
- bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(),
- bounds.getWidth(), bounds.getHeight());
drawComposite(bounds, null);
}
}
Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v
retrieving revision 1.52
diff -u -r1.52 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java 21 Nov 2006 21:21:36 -0000 1.52
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java 22 Nov 2006 16:40:15 -0000
@@ -1147,19 +1147,25 @@
if (s instanceof Rectangle2D)
{
Rectangle2D r = (Rectangle2D) s;
- cairoRectangle(nativePointer, shifted(r.getX(),shiftDrawCalls && isDraw),
- shifted(r.getY(), shiftDrawCalls && isDraw), r.getWidth(),
- r.getHeight());
+
+ // Pixels need to be shifted in draw operations to ensure that they
+ // light up entire pixels, but we also need to make sure the rectangle
+ // does not get distorted by this shifting operation
+ double x = shiftX(r.getX(),shiftDrawCalls && isDraw);
+ double y = shiftY(r.getY(), shiftDrawCalls && isDraw);
+ double w = shiftX(r.getWidth() + r.getX(), shiftDrawCalls && isDraw) - x;
+ double h = shiftY(r.getHeight() + r.getY(), shiftDrawCalls && isDraw) - y;
+ cairoRectangle(nativePointer, x, y, w, h);
}
// Lines are easy too
else if (s instanceof Line2D)
{
Line2D l = (Line2D) s;
- cairoMoveTo(nativePointer, shifted(l.getX1(), shiftDrawCalls && isDraw),
- shifted(l.getY1(), shiftDrawCalls && isDraw));
- cairoLineTo(nativePointer, shifted(l.getX2(), shiftDrawCalls && isDraw),
- shifted(l.getY2(), shiftDrawCalls && isDraw));
+ cairoMoveTo(nativePointer, shiftX(l.getX1(), shiftDrawCalls && isDraw),
+ shiftY(l.getY1(), shiftDrawCalls && isDraw));
+ cairoLineTo(nativePointer, shiftX(l.getX2(), shiftDrawCalls && isDraw),
+ shiftY(l.getY2(), shiftDrawCalls && isDraw));
}
// We can optimize ellipses too; however we don't bother optimizing arcs:
@@ -1188,8 +1194,8 @@
}
cairoArc(nativePointer,
- shifted(e.getCenterX() / xscale, shiftDrawCalls && isDraw),
- shifted(e.getCenterY() / yscale, shiftDrawCalls && isDraw),
+ shiftX(e.getCenterX() / xscale, shiftDrawCalls && isDraw),
+ shiftY(e.getCenterY() / yscale, shiftDrawCalls && isDraw),
radius, 0, Math.PI * 2);
if (xscale != 1 || yscale != 1)
@@ -1847,12 +1853,33 @@
}
/**
- * Shifts coordinates by 0.5.
+ * Shifts an x-coordinate by 0.5 in device space.
*/
- private double shifted(double coord, boolean doShift)
+ private double shiftX(double coord, boolean doShift)
{
if (doShift)
- return Math.floor(coord) + 0.5;
+ {
+ double shift = 0.5;
+ if (!transform.isIdentity())
+ shift /= transform.getScaleX();
+ return Math.round(coord) + shift;
+ }
+ else
+ return coord;
+ }
+
+ /**
+ * Shifts a y-coordinate by 0.5 in device space.
+ */
+ private double shiftY(double coord, boolean doShift)
+ {
+ if (doShift)
+ {
+ double shift = 0.5;
+ if (!transform.isIdentity())
+ shift /= transform.getScaleY();
+ return Math.round(coord) + shift;
+ }
else
return coord;
}
@@ -1873,35 +1900,35 @@
switch (seg)
{
case PathIterator.SEG_MOVETO:
- x = shifted(coords[0], doShift);
- y = shifted(coords[1], doShift);
+ x = shiftX(coords[0], doShift);
+ y = shiftY(coords[1], doShift);
cairoMoveTo(nativePointer, x, y);
break;
case PathIterator.SEG_LINETO:
- x = shifted(coords[0], doShift);
- y = shifted(coords[1], doShift);
+ x = shiftX(coords[0], doShift);
+ y = shiftY(coords[1], doShift);
cairoLineTo(nativePointer, x, y);
break;
case PathIterator.SEG_QUADTO:
// splitting a quadratic bezier into a cubic:
// see: http://pfaedit.sourceforge.net/bezier.html
- double x1 = x + (2.0 / 3.0) * (shifted(coords[0], doShift) - x);
- double y1 = y + (2.0 / 3.0) * (shifted(coords[1], doShift) - y);
+ double x1 = x + (2.0 / 3.0) * (shiftX(coords[0], doShift) - x);
+ double y1 = y + (2.0 / 3.0) * (shiftY(coords[1], doShift) - y);
- double x2 = x1 + (1.0 / 3.0) * (shifted(coords[2], doShift) - x);
- double y2 = y1 + (1.0 / 3.0) * (shifted(coords[3], doShift) - y);
+ double x2 = x1 + (1.0 / 3.0) * (shiftX(coords[2], doShift) - x);
+ double y2 = y1 + (1.0 / 3.0) * (shiftY(coords[3], doShift) - y);
- x = shifted(coords[2], doShift);
- y = shifted(coords[3], doShift);
+ x = shiftX(coords[2], doShift);
+ y = shiftY(coords[3], doShift);
cairoCurveTo(nativePointer, x1, y1, x2, y2, x, y);
break;
case PathIterator.SEG_CUBICTO:
- x = shifted(coords[4], doShift);
- y = shifted(coords[5], doShift);
- cairoCurveTo(nativePointer, shifted(coords[0], doShift),
- shifted(coords[1], doShift),
- shifted(coords[2], doShift),
- shifted(coords[3], doShift), x, y);
+ x = shiftX(coords[4], doShift);
+ y = shiftY(coords[5], doShift);
+ cairoCurveTo(nativePointer, shiftX(coords[0], doShift),
+ shiftY(coords[1], doShift),
+ shiftX(coords[2], doShift),
+ shiftY(coords[3], doShift), x, y);
break;
case PathIterator.SEG_CLOSE:
cairoClosePath(nativePointer);