Hi,

This patch fixes the transformations we use when calculating device-space bounds to support rotations and shears in the transform (before, we had assumed that the shape would only be translated/scaled - which is true for most, but not all, cases).

Included is also a small optimization to updateClip() that tries to avoid converting Rectangles to GeneralPath's when possible.

Hope everyone has a great holiday season!
Francis


2006-12-22  Francis Kung  <[EMAIL PROTECTED]>

        * gnu/java/awt/peer/gtk/BufferedImageGraphics.java
        (locked): Removed field.
        (BufferedImageGraphics): Remove locked flag.
        (drawComposite): Transform bounds properly; set cairo composite 
directly.
        (drawImage): Transform bounds properly.
        (updateBufferedImage): Remove locked flag; transform bounds properly.
        * gnu/java/awt/peer/gtk/CairoGraphics2D.java
        (copyArea): Add comment.
        (drawImage): Transform bounds properly; update clipping region.
        (getClipInDevSpace): Transform bounds properly.
        (getTransformedBounds): New method.
        (setAntialias): Updated javadoc.
        (setCustomPaint): Transform bounds properly.
        (updateClip): Avoid use of GeneralPath when possible.
Index: gnu/java/awt/peer/gtk/BufferedImageGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java,v
retrieving revision 1.22
diff -u -r1.22 BufferedImageGraphics.java
--- gnu/java/awt/peer/gtk/BufferedImageGraphics.java	18 Dec 2006 15:35:04 -0000	1.22
+++ gnu/java/awt/peer/gtk/BufferedImageGraphics.java	22 Dec 2006 19:41:37 -0000
@@ -40,6 +40,7 @@
 
 import java.awt.AlphaComposite;
 import java.awt.Color;
+import java.awt.Composite;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.GraphicsConfiguration;
@@ -49,7 +50,6 @@
 import java.awt.Toolkit;
 import java.awt.font.GlyphVector;
 import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
@@ -75,12 +75,6 @@
   private BufferedImage image, buffer;
   
   /**
-   * Allows us to lock the image from updates (if we want to perform a few
-   * intermediary operations on the cairo surface, then update it all at once)
-   */
-  private boolean locked;
-
-  /**
    * Image size.
    */
   private int imageWidth, imageHeight;
@@ -109,7 +103,6 @@
     this.image = bi;
     imageWidth = bi.getWidth();
     imageHeight = bi.getHeight();
-    locked = false;
     
     if (!(image.getSampleModel() instanceof SinglePixelPackedSampleModel))
       hasFastCM = false;
@@ -189,7 +182,6 @@
     cairo_t = surface.newCairoContext();
     imageWidth = copyFrom.imageWidth;
     imageHeight = copyFrom.imageHeight;
-    locked = false;
     
     hasFastCM = copyFrom.hasFastCM;
     hasAlpha = copyFrom.hasAlpha;
@@ -202,15 +194,12 @@
    */
   private void updateBufferedImage(int x, int y, int width, int height)
   {  
-    if (locked)
-      return;
-    
-    double[] points = new double[]{x, y, width+x, height+y};
-    transform.transform(points, 0, points, 0, 2);
-    x = (int)points[0];
-    y = (int)points[1];
-    width = (int)Math.ceil(points[2] - points[0]);
-    height = (int)Math.ceil(points[3] - points[1]);
+    Rectangle bounds = new Rectangle(x, y, width, height);
+    bounds = getTransformedBounds(bounds, transform).getBounds();
+    x = bounds.x;
+    y = bounds.y;
+    width = bounds.width;
+    height = bounds.height;
 
     int[] pixels = surface.getPixels(imageWidth * imageHeight);
 
@@ -403,14 +392,10 @@
         BufferedImage bImg = (BufferedImage) img;
         
         // Find translated bounds
-        Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY());
-        Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(),
-                                        bImg.getHeight() + bImg.getMinY());
+        Rectangle2D bounds = new Rectangle(bImg.getMinX(), bImg.getMinY(),
+                                         bImg.getWidth(), bImg.getHeight());
         if (xform != null)
-          {
-            origin = xform.transform(origin, origin);
-            pt = xform.transform(pt, pt);
-          }
+          bounds = getTransformedBounds(bounds, xform);
         
         // Create buffer and draw image
         createBuffer();
@@ -420,10 +405,7 @@
         g2d.drawImage(img, xform, obs);
 
         // Perform compositing
-        return drawComposite(new Rectangle2D.Double(origin.getX(),
-                                                    origin.getY(),
-                                                    pt.getX(), pt.getY()),
-                             obs);
+        return drawComposite(bounds, obs);
       }
   }
 
@@ -473,12 +455,7 @@
   private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
   {
     // Find bounds in device space
-    double[] points = new double[] {bounds.getX(), bounds.getY(),
-                                    bounds.getMaxX(), bounds.getMaxY()};
-    transform.transform(points, 0, points, 0, 2);
-    bounds = new Rectangle2D.Double(points[0], points[1],
-                                    (points[2] - points[0]),
-                                    (points[3] - points[1]));
+    bounds = getTransformedBounds(bounds, transform);
 
     // Clip bounds by the stored clip, and by the internal buffer
     Rectangle2D devClip = this.getClipInDevSpace();
@@ -487,17 +464,15 @@
                             buffer.getWidth(), buffer.getHeight());
     Rectangle2D.intersect(bounds, devClip, bounds);
     
-    // Round bounds as needed, but be conservative in our rounding
+    // Round bounds as needed, but be careful in our rounding
     // (otherwise it may leave unpainted stripes)
     double x = bounds.getX();
     double y = bounds.getY();
-    double w = bounds.getWidth();
-    double h = bounds.getHeight();
-    if (Math.floor(x) != x)
-      w--;
-    if (Math.floor(y) != y)
-      h--;
-    bounds.setRect(Math.ceil(x), Math.ceil(y), Math.floor(w), Math.floor(h));
+    double maxX = x + bounds.getWidth();
+    double maxY = y + bounds.getHeight();
+    x = Math.round(x);
+    y = Math.round(y);
+    bounds.setRect(x, y, Math.round(maxX - x), Math.round(maxY - y));
     
     // Find subimage of internal buffer for updating
     BufferedImage buffer2 = buffer;
@@ -516,9 +491,10 @@
     compCtx.compose(buffer2.getRaster(), current.getRaster(),
                     current.getRaster());
     
-    // Prevent the clearRect in CairoGraphics2D.drawImage from clearing
-    // our composited image
-    locked = true;
+    // Set cairo's composite to direct SRC, since we've already done our own
+    // compositing   
+    Composite oldcomp = comp;
+    setComposite(AlphaComposite.Src);
     
     // This MUST call directly into the "action" method in CairoGraphics2D,
     // not one of the wrappers, to ensure that the composite isn't processed
@@ -526,8 +502,9 @@
     boolean rv = super.drawImage(current,
                                  AffineTransform.getTranslateInstance(bounds.getX(),
                                                                       bounds.getY()),
-                                 new Color(0,0,0,0), null);
-    locked = false;
+                                 null, null);
+    setComposite(oldcomp);
+    updateColor();
     return rv;
   }
   
Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v
retrieving revision 1.59
diff -u -r1.59 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java	15 Dec 2006 21:48:39 -0000	1.59
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java	22 Dec 2006 19:41:37 -0000
@@ -770,15 +770,11 @@
     int userHeight = bounds.height;
     
     // Find bounds in device space
-    Point2D origin = transform.transform(new Point2D.Double(userX, userY),
-                                         null);
-    Point2D extreme = transform.transform(new Point2D.Double(userWidth + userX,
-                                                             userHeight + userY),
-                                          null);
-    int deviceX = (int)origin.getX();
-    int deviceY = (int)origin.getY();
-    int deviceWidth = (int)Math.ceil(extreme.getX() - origin.getX());
-    int deviceHeight = (int)Math.ceil(extreme.getY() - origin.getY());
+    Rectangle2D bounds2D = getTransformedBounds(bounds, transform);
+    int deviceX = (int)bounds2D.getX();
+    int deviceY = (int)bounds2D.getY();
+    int deviceWidth = (int)Math.ceil(bounds2D.getWidth());
+    int deviceHeight = (int)Math.ceil(bounds2D.getHeight());
 
     // Get raster of the paint background
     PaintContext pc = paint.createContext(CairoSurface.cairoColorModel,
@@ -983,18 +979,7 @@
     if (transform == null)
       return uclip;
     else
-      {
-	Point2D pos = transform.transform(new Point2D.Double(uclip.getX(),
-	                                                     uclip.getY()),
-	                                  (Point2D) null);
-	Point2D extent = transform.deltaTransform(new Point2D.Double(uclip
-	                                                             .getWidth(),
-	                                                             uclip
-	                                                             .getHeight()),
-	                                          (Point2D) null);
-	return new Rectangle2D.Double(pos.getX(), pos.getY(), extent.getX(),
-	                              extent.getY());
-      }
+      return getTransformedBounds(clip.getBounds2D(), transform);
   }
 
   public void setClip(int x, int y, int width, int height)
@@ -1334,6 +1319,8 @@
   public void copyArea(int ox, int oy, int owidth, int oheight, 
 		       int odx, int ody)
   {
+    // FIXME: does this handle a rotation transform properly?
+    // (the width/height might not be correct)
     Point2D pos = transform.transform(new Point2D.Double(ox, oy),
 				      (Point2D) null);
     Point2D dim = transform.transform(new Point2D.Double(ox + owidth, 
@@ -1449,7 +1436,7 @@
    * Set antialias if needed.  If the ignoreAA flag is set, this method will
    * return without doing anything.
    * 
-   * @param value RenderingHints.VALUE_ANTIALIAS_ON or RenderingHints.VALUE_ANTIALIAS_OFF
+   * @param needAA RenderingHints.VALUE_ANTIALIAS_ON or RenderingHints.VALUE_ANTIALIAS_OFF
    */
   private void setAntialias(boolean needAA)
   {
@@ -1539,24 +1526,31 @@
         Color oldColor = bg;
         setBackground(bgcolor);
         
-        double[] origin = new double[] {0,0};
-        double[] dimensions = new double[] {width, height};
-        xform.transform(origin, 0, origin, 0, 1);
-        xform.deltaTransform(dimensions, 0, dimensions, 0, 1);
-        clearRect((int)origin[0], (int)origin[1],
-                  (int)dimensions[0], (int)dimensions[1]);
+        Rectangle2D bounds = new Rectangle2D.Double(0, 0, width, height);
+        bounds = getTransformedBounds(bounds, xform);
+        
+        clearRect((int)bounds.getX(), (int)bounds.getY(),
+                  (int)bounds.getWidth(), (int)bounds.getHeight());
         
         setBackground(oldColor);
       }
 
     int[] pixels = b.getRGB(0, 0, width, height, null, 0, width);
-    
     // FIXME: The above method returns data in the standard ARGB colorspace,
     // meaning data should NOT be alpha pre-multiplied; however Cairo expects
     // data to be premultiplied.
+    
+    cairoSave(nativePointer);
+    cairoResetClip(nativePointer);
+    Rectangle2D bounds = new Rectangle2D.Double(0, 0, width, height);
+    bounds = getTransformedBounds(bounds, xform);
+    cairoRectangle(nativePointer, bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
+    cairoClip(nativePointer);
 
     drawPixels(nativePointer, pixels, width, height, width, i2u, alpha,
                getInterpolation());
+    
+    cairoRestore(nativePointer);
 
     // Cairo seems to lose the current color which must be restored.
     updateColor();
@@ -2076,11 +2070,27 @@
     if (clip == null)
       return;
 
-    if (! (clip instanceof GeneralPath))
-      clip = new GeneralPath(clip);
-
-    GeneralPath p = (GeneralPath) clip;
-    p.transform(t);
+    // If the clip is a rectangle, and the transformation preserves the shape
+    // (translate/stretch only), then keep the clip as a rectangle
+    double[] matrix = new double[4];
+    t.getMatrix(matrix);
+    if (clip instanceof Rectangle2D && matrix[1] == 0 && matrix[2] == 0)
+      {
+        Rectangle2D rect = (Rectangle2D)clip;
+        double[] origin = new double[] {rect.getX(), rect.getY()};
+        double[] dimensions = new double[] {rect.getWidth(), rect.getHeight()};
+        t.transform(origin, 0, origin, 0, 1);
+        t.deltaTransform(dimensions, 0, dimensions, 0, 1);
+        rect.setRect(origin[0], origin[1], dimensions[0], dimensions[1]);
+      }
+    else
+      {
+        if (! (clip instanceof GeneralPath))
+          clip = new GeneralPath(clip);
+    
+        GeneralPath p = (GeneralPath) clip;
+        p.transform(t);
+      }
   }
 
   private static Rectangle computeIntersection(int x, int y, int w, int h,
@@ -2103,4 +2113,39 @@
 
     return rect;
   }
+  
+  static Rectangle2D getTransformedBounds(Rectangle2D bounds, AffineTransform tx)
+  {
+    double x1 = bounds.getX();
+    double x2 = bounds.getX() + bounds.getWidth();
+    double x3 = x1;
+    double x4 = x2;
+    double y1 = bounds.getY();
+    double y2 = y1;
+    double y3 = bounds.getY() + bounds.getHeight();
+    double y4 = y3;
+    
+    double[] points = new double[] {x1, y1, x2, y2, x3, y3, x4, y4};
+    tx.transform(points, 0, points, 0, 4);
+    
+    double minX = points[0];
+    double maxX = minX;
+    double minY = points[1];
+    double maxY = minY;
+    for (int i = 0; i < 8; i++)
+      {
+        if (points[i] < minX)
+          minX = points[i];
+        if (points[i] > maxX)
+          maxX = points[i];
+        i++;
+        
+        if (points[i] < minY)
+          minY = points[i];
+        if (points[i] > maxY)
+          maxY = points[i];
+      }
+    
+    return new Rectangle2D.Double(minX, minY, (maxX - minX), (maxY - minY));
+  }
 }
\ No newline at end of file

Reply via email to