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);

Reply via email to