Hi,

This patch implements custom composites for the BufferedImageGraphics
peer, and also cleans up some of the ambiguity regarding cairo vs gdk
native formats.

Cheers,
Francis


2006-10-11  Francis Kung  <[EMAIL PROTECTED]>

        * gnu/java/awt/peer/gtk/BufferedImageGraphics.java
        (buffer, locked): New fields.
        (constructors): Initialize new variables.
        (createBuffer): New method.
        (draw): Implement custom composites.
        (drawComposite): New method.
        (drawGlyphVector): Implement custom composites.
        (drawImage): Implement custom composites.
        (drawRenderedImage): Implement custom composites.
        (fill): Implement custom composites.
        (getBufferCM): New method.
        (getNativeCM): New method.
        (updateBufferedImage): Fix premultiplication.
        * gnu/java/awt/peer/gtk/CairoGraphics2D.java
        (copy): Copy composite.
        (drawImage): Set background properly.
        (getBufferCM): New method.
        (setComposite): Reset alpha composite when using custom composite.
        * gnu/java/awt/peer/gtk/CairoSurface.java
        (cairoColorModel): New field.
        (nativeColorModel): Renamed.
        (constructor): Use renamed createCairoSampleModel method.
        (createCairoSampleModel): New method.
        (createNativeSampleModel): Renamed.
        (getBufferedImage): Use renamed cairoColorModel field.
        * gnu/java/awt/peer/gtk/GtkVolatileImage.java
        (gdkColorModel): New field.
        (createGdkSampleModel): New method.
        (getPixels): Added comments.
        (getSnapshot): Use GDK colour and sample models.
        * gnu/java/awt/peer/gtk/VolatileImageGraphics.java
        (createBuffer): Use GDK colour and sample models.
        (getNativeCM): Added comments.
        * java/awt/image/BufferedImage.java
        (constructor): Set premultiplied flag properly.

Index: gnu/java/awt/peer/gtk/BufferedImageGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java,v
retrieving revision 1.11
diff -u -r1.11 BufferedImageGraphics.java
--- gnu/java/awt/peer/gtk/BufferedImageGraphics.java	2 Oct 2006 21:36:21 -0000	1.11
+++ gnu/java/awt/peer/gtk/BufferedImageGraphics.java	11 Oct 2006 19:56:44 -0000
@@ -38,23 +38,27 @@
 
 package gnu.java.awt.peer.gtk;
 
+import java.awt.AlphaComposite;
 import java.awt.Color;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.GraphicsConfiguration;
 import java.awt.Image;
 import java.awt.Rectangle;
 import java.awt.Shape;
+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.Raster;
-import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferInt;
 import java.awt.image.ColorModel;
+import java.awt.image.DataBufferInt;
 import java.awt.image.DirectColorModel;
-import java.awt.image.RenderedImage;
 import java.awt.image.ImageObserver;
+import java.awt.image.ImageProducer;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
 import java.util.WeakHashMap;
 
 /**
@@ -68,7 +72,13 @@
   /**
    * the buffered Image.
    */
-  private BufferedImage image;
+  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.
@@ -105,6 +115,8 @@
     this.image = bi;
     imageWidth = bi.getWidth();
     imageHeight = bi.getHeight();
+    locked = false;
+    
     if(bi.getColorModel().equals(rgb32))
       {
 	hasFastCM = true;
@@ -113,7 +125,7 @@
     else if(bi.getColorModel().equals(argb32))
       {
 	hasFastCM = true;
-	hasAlpha = false;
+	hasAlpha = true;
       }
     else
       hasFastCM = false;
@@ -163,6 +175,7 @@
     cairo_t = surface.newCairoContext();
     imageWidth = copyFrom.imageWidth;
     imageHeight = copyFrom.imageHeight;
+    locked = false;
     copy( copyFrom, cairo_t );
     setClip(0, 0, surface.width, surface.height);
   }
@@ -172,6 +185,9 @@
    */
   private void updateBufferedImage(int x, int y, int width, int height)
   {  
+    if (locked)
+      return;
+    
     int[] pixels = surface.getPixels(imageWidth * imageHeight);
 
     if( x > imageWidth || y > imageHeight )
@@ -184,18 +200,18 @@
     if( y + height > imageHeight ) 
       height = imageHeight - y;
     
-    boolean wasPremultiplied = image.isAlphaPremultiplied();
-    image.coerceData(true);
-
-    if( !hasFastCM )
+    // The setRGB method assumes (or should assume) that pixels are NOT
+    // alpha-premultiplied, but Cairo stores data with premultiplication
+    // (thus the pixels returned in getPixels are premultiplied).
+    // This is ignored for consistency, however, since in
+    // CairoGrahpics2D.drawImage we also use non-premultiplied data
+    if(!hasFastCM)
       image.setRGB(x, y, width, height, pixels, 
 		   x + y * imageWidth, imageWidth);
     else
       System.arraycopy(pixels, y * imageWidth, 
 		       ((DataBufferInt)image.getRaster().getDataBuffer()).
 		       getData(), y * imageWidth, height * imageWidth);
-    
-    image.coerceData(wasPremultiplied);
   }
 
   /**
@@ -228,36 +244,207 @@
    */
   public void draw(Shape s)
   {
-    super.draw(s);
-    Rectangle r = s.getBounds();
-    updateBufferedImage(r.x, r.y, r.width, r.height);
+    if (comp == null || comp instanceof AlphaComposite)
+      {
+        super.draw(s);
+        Rectangle r = s.getBounds();
+        updateBufferedImage(r.x, r.y, r.width, r.height);
+      }
+    else
+      {
+        createBuffer();
+        
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setStroke(this.getStroke());
+        g2d.setColor(this.getColor());
+        g2d.draw(s);
+        
+        drawComposite(s.getBounds2D(), null);
+      }
   }
 
   public void fill(Shape s)
   {
-    super.fill(s);
-    Rectangle r = s.getBounds();
-    updateBufferedImage(r.x, r.y, r.width, r.height);
+    if (comp == null || comp instanceof AlphaComposite)
+      {
+        super.fill(s);
+        Rectangle r = s.getBounds();
+        updateBufferedImage(r.x, r.y, r.width, r.height);
+      }
+    else
+      {
+        createBuffer();
+        
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setPaint(this.getPaint());
+        g2d.setColor(this.getColor());
+        g2d.fill(s);
+        
+        drawComposite(s.getBounds2D(), null);
+      }
   }
 
   public void drawRenderedImage(RenderedImage image, AffineTransform xform)
   {
-    super.drawRenderedImage(image, xform);
-    updateBufferedImage(0, 0, imageWidth, imageHeight);
+    if (comp == null || comp instanceof AlphaComposite)
+      {
+        super.drawRenderedImage(image, xform);
+        updateBufferedImage(0, 0, imageWidth, imageHeight);
+      }
+    else
+      {
+        createBuffer();
+
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setRenderingHints(this.getRenderingHints());
+        g2d.drawRenderedImage(image, xform);
+        
+        drawComposite(buffer.getRaster().getBounds(), null);
+      }
+
   }
 
   protected boolean drawImage(Image img, AffineTransform xform,
 			      Color bgcolor, ImageObserver obs)
   {
-    boolean rv = super.drawImage(img, xform, bgcolor, obs);
-    updateBufferedImage(0, 0, imageWidth, imageHeight);
-    return rv;
+    if (comp == null || comp instanceof AlphaComposite)
+      {
+        boolean rv = super.drawImage(img, xform, bgcolor, obs);
+        updateBufferedImage(0, 0, imageWidth, imageHeight);
+        return rv;
+      }
+    else
+      {
+        // Get buffered image of source
+        if( !(img instanceof BufferedImage) )
+          {
+            ImageProducer source = img.getSource();
+            if (source == null)
+              return false;
+            img = Toolkit.getDefaultToolkit().createImage(source);
+          }
+        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());
+        if (xform != null)
+          {
+            origin = xform.transform(origin, origin);
+            pt = xform.transform(pt, pt);
+          }
+        
+        // Create buffer and draw image
+        createBuffer();
+        
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setRenderingHints(this.getRenderingHints());
+        g2d.drawImage(img, xform, obs);
+
+        // Perform compositing
+        return drawComposite(new Rectangle2D.Double(origin.getX(),
+                                                    origin.getY(),
+                                                    pt.getX(), pt.getY()),
+                             obs);
+      }
   }
 
   public void drawGlyphVector(GlyphVector gv, float x, float y)
   {
-    super.drawGlyphVector(gv, x, y);
-    updateBufferedImage(0, 0, imageWidth, imageHeight);
+    if (comp == null || comp instanceof AlphaComposite)
+      {
+        super.drawGlyphVector(gv, x, y);
+        updateBufferedImage(0, 0, imageWidth, imageHeight);
+      }
+    else
+      {
+        createBuffer();
+
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setPaint(this.getPaint());
+        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);
+      }
+  }
+  
+  private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
+  {
+    // Clip source to visible areas that need updating
+    Rectangle2D clip = this.getClipBounds();
+    Rectangle2D.intersect(bounds, clip, bounds);
+    
+    BufferedImage buffer2 = buffer;
+    if (!bounds.equals(buffer2.getRaster().getBounds()))
+      buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(),
+                                    (int)bounds.getWidth(),
+                                    (int)bounds.getHeight());
+    
+    // Get destination clip to bounds
+    double[] points = new double[] {bounds.getX(), bounds.getY(),
+                                    bounds.getMaxX(), bounds.getMaxY()};
+    transform.transform(points, 0, points, 0, 2);
+    
+    Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1],
+                                                       points[2] - points[0],
+                                                       points[3] - points[1]);
+    
+    Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds);
+    
+    BufferedImage current = image;
+    current = current.getSubimage((int)deviceBounds.getX(),
+                                  (int)deviceBounds.getY(),
+                                  (int)deviceBounds.getWidth(),
+                                  (int)deviceBounds.getHeight());
+
+    // Perform actual composite operation
+    compCtx.compose(buffer2.getRaster(), current.getRaster(),
+                    current.getRaster());
+    
+    // Prevent the clearRect in CairoGraphics2D.drawImage from clearing
+    // our composited image
+    locked = true;
+    
+    // This MUST call directly into the "action" method in CairoGraphics2D,
+    // not one of the wrappers, to ensure that the composite isn't processed
+    // more than once!
+    boolean rv = super.drawImage(current,
+                                 AffineTransform.getTranslateInstance(bounds.getX(),
+                                                                      bounds.getY()),
+                                 new Color(0,0,0,0), null);
+    locked = false;
+    return rv;
+  }
+  
+  private void createBuffer()
+  {
+    if (buffer == null)
+      {
+        buffer = new BufferedImage(image.getWidth(), image.getHeight(),
+                                   BufferedImage.TYPE_INT_ARGB);
+      }
+    else
+      {
+        Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
+        
+        g2d.setBackground(new Color(0,0,0,0));
+        g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
+      }
+  }
+  
+  protected ColorModel getNativeCM()
+  {
+    return image.getColorModel();
+  }
+  
+  protected ColorModel getBufferCM()
+  {
+    return ColorModel.getRGBdefault();
   }
 }
 
Index: gnu/java/awt/peer/gtk/CairoSurface.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoSurface.java,v
retrieving revision 1.19
diff -u -r1.19 CairoSurface.java
--- gnu/java/awt/peer/gtk/CairoSurface.java	3 Oct 2006 19:47:58 -0000	1.19
+++ gnu/java/awt/peer/gtk/CairoSurface.java	11 Oct 2006 19:56:44 -0000
@@ -72,15 +72,13 @@
    */
   long bufferPointer;
 
-  // nativeGetPixels will return [0]=red, [1]=green, [2]=blue, [3]=alpha
-  static ColorModel nativeColorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
-                                                       32,
-                                                       0x000000FF,
-                                                       0x0000FF00,
-                                                       0x00FF0000,
-                                                       0xFF000000,
-                                                       true,
-                                                       Buffers.smallestAppropriateTransferType(32));
+  static ColorModel cairoColorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+                                                           32, 0x00FF0000,
+                                                           0x0000FF00,
+                                                           0x000000FF,
+                                                           0xFF000000,
+                                                           true,
+                                                           Buffers.smallestAppropriateTransferType(32));
 
   /**
    * Allocates and clears the buffer and creates the cairo surface.
@@ -147,7 +145,7 @@
    */
   public CairoSurface(int width, int height)
   {
-    super(createNativeSampleModel(width, height),
+    super(createCairoSampleModel(width, height),
 	      null, new Point(0, 0));
 
     if(width <= 0 || height <= 0)
@@ -260,7 +258,9 @@
    */    
   public static BufferedImage getBufferedImage(CairoSurface surface)
   {
-    return new BufferedImage(nativeColorModel, surface, true, new Hashtable());
+    return new BufferedImage(cairoColorModel, surface,
+                             cairoColorModel.isAlphaPremultiplied(),
+                             new Hashtable());
   }
 
   private class CairoDataBuffer extends DataBuffer
@@ -326,10 +326,10 @@
   /**
    * Creates a SampleModel that matches Cairo's native format
    */
-  protected static SampleModel createNativeSampleModel(int w, int h)
+  protected static SampleModel createCairoSampleModel(int w, int h)
   {
     return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h,
-                                            new int[]{0x000000FF, 0x0000FF00,
-                                                      0x00FF0000, 0xFF000000});    
+                                            new int[]{0x00FF0000, 0x0000FF00,
+                                                      0x000000FF, 0xFF000000});    
   }
 }
Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v
retrieving revision 1.43
diff -u -r1.43 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java	10 Oct 2006 20:24:15 -0000	1.43
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java	11 Oct 2006 19:56:44 -0000
@@ -236,7 +236,6 @@
     nativePointer = init(cairo_t_pointer);
     paint = g.paint;
     stroke = g.stroke;
-    comp = g.comp;
     setRenderingHints(g.hints);
     
     Color foreground;
@@ -965,27 +964,30 @@
       compCtx.dispose();
     compCtx = null;
 
-    if (comp == null)
-      comp = AlphaComposite.SrcOver;
-    
     if (comp instanceof AlphaComposite)
       {
 	AlphaComposite a = (AlphaComposite) comp;
-	cairoSetOperator(nativePointer, a.getRule());
+        cairoSetOperator(nativePointer, a.getRule());
       }
       
     else
       {
-        // FIXME: this check is only required "if this Graphics2D
-        // context is drawing to a Component on the display screen".
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null)
-          sm.checkPermission(new AWTPermission("readDisplayPixels"));
-
-        // FIXME: implement general Composite support
-        //throw new java.lang.UnsupportedOperationException();
-        // this is in progress!  yay!
-        compCtx = comp.createContext(getNativeCM(), getNativeCM(), hints);
+        cairoSetOperator(nativePointer, AlphaComposite.SRC_OVER);
+        
+        if (comp != null)
+          {
+            // FIXME: this check is only required "if this Graphics2D
+            // context is drawing to a Component on the display screen".
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null)
+              sm.checkPermission(new AWTPermission("readDisplayPixels"));
+    
+            // FIXME: implement general Composite support
+            //throw new java.lang.UnsupportedOperationException();
+            // this is in progress!  yay!
+            //compCtx = comp.createContext(getNativeCM(), getNativeCM(), hints);
+            compCtx = comp.createContext(getBufferCM(), getNativeCM(), hints);
+          }
       }
   }
   
@@ -1003,6 +1005,12 @@
     // for now, so that the build doesn't break.
     return null;
   }
+  
+  // This may be overridden by some subclasses
+  protected ColorModel getBufferCM()
+  {
+    return getNativeCM();
+  }
 
   ///////////////////////// DRAWING PRIMITIVES ///////////////////////////////////
 
@@ -1358,9 +1366,6 @@
     int width = b.getWidth();
     int height = b.getHeight();
     
-    boolean wasPremultplied = b.isAlphaPremultiplied();
-    b.coerceData(true);
-
     // If this BufferedImage has a BufferedImageGraphics object, 
     // use the cached CairoSurface that BIG is drawing onto
     
@@ -1380,31 +1385,32 @@
 	((CairoSurface)raster).drawSurface(nativePointer, i2u, alpha,
                                            getInterpolation());
         updateColor();
-        b.coerceData(wasPremultplied);
 	return true;
       }
 	    
     if( bgcolor != null )
       {
-	// Fill a rectangle with the background color 
-	// to composite the image onto.
-	Paint oldPaint = paint;
-	AffineTransform oldTransform = transform;
-	setPaint( bgcolor );
-	setTransform( invertedXform );
-	fillRect(0, 0, width, height);
-	setTransform( oldTransform );
-	setPaint( oldPaint );
+        Color oldColor = bg;
+        setBackground(bgcolor);
+        
+        double[] origin = new double[] {0,0};
+        xform.transform(origin, 0, origin, 0, 1);
+        clearRect((int)origin[0], (int)origin[1], width, height);
+        
+        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.
 
     drawPixels(nativePointer, pixels, width, height, width, i2u, alpha,
                getInterpolation());
 
     // Cairo seems to lose the current color which must be restored.
     updateColor();
-    b.coerceData(wasPremultplied);
     return true;
   }
 
Index: gnu/java/awt/peer/gtk/GtkVolatileImage.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java,v
retrieving revision 1.8
diff -u -r1.8 GtkVolatileImage.java
--- gnu/java/awt/peer/gtk/GtkVolatileImage.java	15 Jun 2006 18:29:34 -0000	1.8
+++ gnu/java/awt/peer/gtk/GtkVolatileImage.java	11 Oct 2006 19:56:45 -0000
@@ -37,13 +37,21 @@
 
 package gnu.java.awt.peer.gtk;
 
-import java.awt.ImageCapabilities;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.GraphicsConfiguration;
+import java.awt.ImageCapabilities;
+import java.awt.Point;
 import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DirectColorModel;
 import java.awt.image.ImageObserver;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
 import java.awt.image.VolatileImage;
+import java.awt.image.WritableRaster;
 
 public class GtkVolatileImage extends VolatileImage
 {
@@ -52,6 +60,12 @@
 
   final GtkComponentPeer component;
 
+  static ColorModel gdkColorModel = new DirectColorModel(32,
+                                                         0x000000FF,
+                                                         0x0000FF00,
+                                                         0x00FF0000,
+                                                         0xFF000000);
+                                                         
   /**
    * Don't touch, accessed from native code.
    */
@@ -62,6 +76,17 @@
   native void destroy(long pointer);
 
   native int[] nativeGetPixels(long pointer);
+  
+  /**
+   * Gets the pixels in the current image from GDK.
+   * 
+   * Note that pixels are in 32-bit RGBA, non-premultiplied, which is different
+   * from Cairo's premultiplied ARGB, which is different from Java's standard
+   * non-premultiplied ARGB.  Caution is advised when using this method, to
+   * ensure that the data format remains consistent with what you expect.
+   *  
+   * @return the current pixels, as reported by GDK.
+   */
   public int[] getPixels()
   {
     return nativeGetPixels(nativePointer);
@@ -113,9 +138,11 @@
 
   public BufferedImage getSnapshot()
   {
-    CairoSurface cs = new CairoSurface( width, height );
-    cs.setPixels( getPixels() );
-    return CairoSurface.getBufferedImage( cs );
+    WritableRaster raster = Raster.createWritableRaster(createGdkSampleModel(width, height),
+                                                        new Point(0, 0));
+    raster.setDataElements(0, 0, getPixels());
+    return new BufferedImage(gdkColorModel, raster,
+                             gdkColorModel.isAlphaPremultiplied(), null);
   }
 
   public Graphics getGraphics()
@@ -167,4 +194,14 @@
   {
     return null;
   }
+  
+  /**
+   * Creates a SampleModel that matches GDK's native format
+   */
+  protected static SampleModel createGdkSampleModel(int w, int h)
+  {
+    return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h,
+                                            new int[]{0x000000FF, 0x0000FF00,
+                                                      0x00FF0000, 0xFF000000});
+  }
 }
Index: gnu/java/awt/peer/gtk/VolatileImageGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java,v
retrieving revision 1.8
diff -u -r1.8 VolatileImageGraphics.java
--- gnu/java/awt/peer/gtk/VolatileImageGraphics.java	3 Oct 2006 19:47:58 -0000	1.8
+++ gnu/java/awt/peer/gtk/VolatileImageGraphics.java	11 Oct 2006 19:56:45 -0000
@@ -282,12 +282,12 @@
     if (buffer == null)
       {
         WritableRaster rst;
-        rst = Raster.createWritableRaster(CairoSurface.
-                                          createNativeSampleModel(owner.width,
+        rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(owner.width,
                                                                   owner.height),
                                           new Point(0,0));
         
-        buffer = new BufferedImage(CairoSurface.nativeColorModel, rst, true,
+        buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst,
+                                   GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(),
                                    new Hashtable());
       }
     else
@@ -301,7 +301,12 @@
   
   protected ColorModel getNativeCM()
   {
-    return CairoSurface.nativeColorModel;
+    // We should really return GtkVolatileImage.gdkColorModel ,
+    // but CairoGraphics2D doesn't handle alpha premultiplication properly (see
+    // the fixme in drawImage) so we use the naive Cairo model instead to trick
+    // the compositing context.
+    // Because getNativeCM() == getBufferCM() for this peer, it doesn't break.
+    return CairoSurface.cairoColorModel;
   }
 }
 
Index: java/awt/image/BufferedImage.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/image/BufferedImage.java,v
retrieving revision 1.20
diff -u -r1.20 BufferedImage.java
--- java/awt/image/BufferedImage.java	2 Oct 2006 21:36:21 -0000	1.20
+++ java/awt/image/BufferedImage.java	11 Oct 2006 19:56:45 -0000
@@ -38,6 +38,7 @@
 
 package java.awt.image;
 
+import gnu.java.awt.Buffers;
 import gnu.java.awt.ComponentDataBlitOp;
 
 import java.awt.Graphics;
@@ -172,7 +173,13 @@
 							  0x0000FF00, 
 							  0x000000FF, 
 							  0xFF000000 } );
-	cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
+        if (premultiplied)
+          cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB),
+                                     32, 0xff0000, 0xff00, 0xff, 0xff000000,
+                                     true,
+                                     Buffers.smallestAppropriateTransferType(32));
+        else
+          cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
 	break;
 
       case BufferedImage.TYPE_4BYTE_ABGR:
@@ -183,7 +190,13 @@
 							  0xFF000000, 
 							  0x00FF0000, 
 							  0x0000FF00 } );
-	cm = new DirectColorModel( 32, 0xff, 0xff00, 0xff0000, 0xff000000 );
+        if (premultiplied)
+          cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB),
+                                     32, 0xff0000, 0xff00, 0xff, 0xff000000,
+                                     true,
+                                     Buffers.smallestAppropriateTransferType(32));
+        else
+          cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
 	break;
 
       case BufferedImage.TYPE_INT_BGR:

Reply via email to