Alright, the first part of custom composites is attached and committed.
They should now work with the VolatileImageGraphics peer, with patches
for the other peers coming soon.

Cheers,
Francis


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

        * gnu/java/awt/peer/gtk/CairoGraphics2D.java
        (compCtx): New field for composite context.
        (copy): Copy composite.
        (dispose): Dispose of composite context.
        (getNativeCM): New method.
        (setComposite): Discard old composite context and set up new context.
        (setRenderingHints): Update composite context.
        * gnu/java/awt/peer/gtk/CairoSurface.java
        (nativeColorModel): New field, renamed from nativeModel.
        (nativeModel): Renamed field to nativeColorModel.
        (CairoSurface(int, int)): Call new method to create sample model.
        (createNativeSampleModel): New method.
        (getBufferedImage): Updated variable name.
        * gnu/java/awt/peer/gtk/VolatileImageGraphics.java
        (buffer): New field.
        (createBuffer): New method.
        (draw): New method.
        (drawComposite): New method.
        (drawGlyphVector): New method.
        (drawImage(Image, AffineTransform, Color, ImageObserver)): New method.
        (drawImage(Image, int, int, ImageObserver)): Check composite.
        (drawImage(Image, int, int, int, int, ImageObserver)): Check composite.
        (fill): New method.
        (getNativeCM): New method.
        * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c
        (Java_gnu_java_awt_peer_gtk_GtkVolatileImage_nativeGetPixels): Use 
        intermediary pixbuf to grab on-screen pixels.

Index: gnu/java/awt/peer/gtk/CairoSurface.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoSurface.java,v
retrieving revision 1.18
diff -u -r1.18 CairoSurface.java
--- gnu/java/awt/peer/gtk/CairoSurface.java	2 Oct 2006 21:36:21 -0000	1.18
+++ gnu/java/awt/peer/gtk/CairoSurface.java	3 Oct 2006 19:11:41 -0000
@@ -40,16 +40,16 @@
 
 import gnu.java.awt.Buffers;
 
-import java.awt.Point;
 import java.awt.Graphics2D;
+import java.awt.Point;
 import java.awt.color.ColorSpace;
-import java.awt.image.DataBuffer;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
 import java.awt.image.DirectColorModel;
+import java.awt.image.SampleModel;
 import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
 import java.nio.ByteOrder;
 import java.util.Hashtable;
 
@@ -73,7 +73,7 @@
   long bufferPointer;
 
   // nativeGetPixels will return [0]=red, [1]=green, [2]=blue, [3]=alpha
-  static ColorModel nativeModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+  static ColorModel nativeColorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
                                                        32,
                                                        0x000000FF,
                                                        0x0000FF00,
@@ -147,10 +147,8 @@
    */
   public CairoSurface(int width, int height)
   {
-    super( new SinglePixelPackedSampleModel
-	   (DataBuffer.TYPE_INT, width, height,
-	    new int[]{ 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }),
-	   null, new Point(0, 0) );
+    super(createNativeSampleModel(width, height),
+	      null, new Point(0, 0));
 
     if(width <= 0 || height <= 0)
       throw new IllegalArgumentException("Image must be at least 1x1 pixels.");
@@ -262,7 +260,7 @@
    */    
   public static BufferedImage getBufferedImage(CairoSurface surface)
   {
-    return new BufferedImage(nativeModel, surface, true, new Hashtable());
+    return new BufferedImage(nativeColorModel, surface, true, new Hashtable());
   }
 
   private class CairoDataBuffer extends DataBuffer
@@ -324,4 +322,14 @@
   {
     copyAreaNative2(bufferPointer, x, y, width, height, dx, dy, stride);
   }
+  
+  /**
+   * Creates a SampleModel that matches Cairo's native format
+   */
+  protected static SampleModel createNativeSampleModel(int w, int h)
+  {
+    return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h,
+                                            new int[]{0x000000FF, 0x0000FF00,
+                                                      0x00FF0000, 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.41
diff -u -r1.41 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java	2 Oct 2006 21:36:21 -0000	1.41
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java	3 Oct 2006 19:11:40 -0000
@@ -45,6 +45,7 @@
 import java.awt.BasicStroke;
 import java.awt.Color;
 import java.awt.Composite;
+import java.awt.CompositeContext;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.GradientPaint;
@@ -163,6 +164,7 @@
    * The current compositing context, if any.
    */
   Composite comp;
+  CompositeContext compCtx;
 
   /**
    * Rendering hint map.
@@ -234,6 +236,7 @@
     nativePointer = init(cairo_t_pointer);
     paint = g.paint;
     stroke = g.stroke;
+    comp = g.comp;
     setRenderingHints(g.hints);
     
     Color foreground;
@@ -268,6 +271,7 @@
     setStroke(stroke);
     setTransformImpl(transform);
     setClip(clip);
+    setComposite(comp);
   }
 
   /**
@@ -288,6 +292,8 @@
   {
     disposeNative(nativePointer);
     nativePointer = 0;
+    if (compCtx != null)
+      compCtx.dispose();
   }
 
   /**
@@ -951,7 +957,13 @@
    */
   public void setComposite(Composite comp)
   {
+    if (this.comp == comp)
+      return;
+    
     this.comp = comp;
+    if (compCtx != null)
+      compCtx.dispose();
+    compCtx = null;
 
     if (comp == null)
       comp = AlphaComposite.SrcOver;
@@ -961,6 +973,7 @@
 	AlphaComposite a = (AlphaComposite) comp;
 	cairoSetOperator(nativePointer, a.getRule());
       }
+      
     else
       {
         // FIXME: this check is only required "if this Graphics2D
@@ -970,9 +983,26 @@
           sm.checkPermission(new AWTPermission("readDisplayPixels"));
 
         // FIXME: implement general Composite support
-        throw new java.lang.UnsupportedOperationException();
+        //throw new java.lang.UnsupportedOperationException();
+        // this is in progress!  yay!
+        compCtx = comp.createContext(getNativeCM(), getNativeCM(), hints);
       }
   }
+  
+  /**
+   * Returns the Colour Model describing the native, raw image data for this
+   * specific peer.
+   *  
+   * @return ColorModel the ColorModel of native data in this peer
+   */
+  /* protected abstract ColorModel getNativeCM(); */
+  protected ColorModel getNativeCM()
+  {
+    // This stub should be removed and the method made abstract once I'm done
+    // implementing custom composites across all the peers... but we need it
+    // for now, so that the build doesn't break.
+    return null;
+  }
 
   ///////////////////////// DRAWING PRIMITIVES ///////////////////////////////////
 
@@ -1237,6 +1267,12 @@
     
     shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
       || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
+    
+    if (compCtx != null)
+      {
+        compCtx.dispose();
+        compCtx = comp.createContext(getNativeCM(), getNativeCM(), this.hints);
+      }
   }
 
   public void addRenderingHints(Map hints)
Index: gnu/java/awt/peer/gtk/VolatileImageGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java,v
retrieving revision 1.7
diff -u -r1.7 VolatileImageGraphics.java
--- gnu/java/awt/peer/gtk/VolatileImageGraphics.java	17 Jul 2006 22:41:03 -0000	1.7
+++ gnu/java/awt/peer/gtk/VolatileImageGraphics.java	3 Oct 2006 19:11:41 -0000
@@ -38,15 +38,31 @@
 
 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.Point;
+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.ColorModel;
 import java.awt.image.ImageObserver;
+import java.awt.image.ImageProducer;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.util.Hashtable;
 
 public class VolatileImageGraphics extends ComponentGraphics
 {
   private GtkVolatileImage owner;
+  private BufferedImage buffer;
 
   public VolatileImageGraphics(GtkVolatileImage img)
   {
@@ -77,10 +93,118 @@
     return new VolatileImageGraphics( this );
   }
 
+  public void draw(Shape s)
+  {
+    if (comp == null || comp instanceof AlphaComposite)
+      super.draw(s);
+    
+    // Custom composite
+    else
+      {
+        // Draw operation to temporary buffer
+        createBuffer();
+
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setColor(this.getColor());
+        g2d.setStroke(this.getStroke());
+        g2d.draw(s);
+        
+        drawComposite(s.getBounds2D(), null);
+      }
+  }
+  
+  public void fill(Shape s)
+  {
+    if (comp == null || comp instanceof AlphaComposite)
+      super.fill(s);
+
+    // Custom composite
+    else
+      {
+        // Draw operation to temporary buffer
+        createBuffer();
+
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        g2d.setPaint(this.getPaint());
+        g2d.setColor(this.getColor());
+        g2d.fill(s);
+        
+        drawComposite(s.getBounds2D(), null);
+      }
+  }
+  
+  public void drawGlyphVector(GlyphVector gv, float x, float y)
+  {
+    if (comp == null || comp instanceof AlphaComposite)
+      super.drawGlyphVector(gv, x, y);
+    
+    // Custom composite
+    else
+      {
+        // Draw operation to temporary buffer
+        createBuffer();
+
+        Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+        
+        g2d.setPaint(this.getPaint());
+        g2d.setColor(this.getColor());
+        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);
+      }
+  }
+
+  protected boolean drawImage(Image img, AffineTransform xform,
+                              Color bgcolor, ImageObserver obs)
+    {
+      if (comp == null || comp instanceof AlphaComposite)
+        return super.drawImage(img, xform, bgcolor, obs);
+      
+      // Custom composite
+      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 dimensions of translation
+          Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY());
+          Point2D pt = new Point2D.Double(bImg.getWidth(), bImg.getHeight());
+          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 from buffer to screen
+          return drawComposite(new Rectangle2D.Double((int)origin.getX(),
+                                                      (int)origin.getY(),
+                                                      (int)pt.getX(),
+                                                      (int)pt.getY()),
+                               obs);
+        }
+    }
+  
   public boolean drawImage(Image img, int x, int y, ImageObserver observer)
   {
-    if( img instanceof GtkVolatileImage )
+    if (img instanceof GtkVolatileImage
+        && (comp == null || comp instanceof AlphaComposite))
       {
 	owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, 
 			    x, y,
@@ -94,7 +218,8 @@
   public boolean drawImage(Image img, int x, int y, int width, int height,
                            ImageObserver observer)
   {
-    if( img instanceof GtkVolatileImage )
+    if ((img instanceof GtkVolatileImage)
+        && (comp == null || comp instanceof AlphaComposite))
       {
 	owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, 
 			    x, y, width, height );
@@ -107,5 +232,76 @@
   {
     return new Rectangle2D.Double(0, 0, owner.width, owner.height);
   }
+  
+  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 current on-screen pixels (destination) and clip to bounds
+    BufferedImage current = owner.getSnapshot();
+
+    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);
+    
+    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(),
+                    buffer2.getRaster());
+    
+    // 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(buffer2,
+                           AffineTransform.getTranslateInstance(bounds.getX(),
+                                                                bounds.getY()),
+                           null, null);
+
+    return rv;
+  }
+  
+  private void createBuffer()
+  {
+    if (buffer == null)
+      {
+        WritableRaster rst;
+        rst = Raster.createWritableRaster(CairoSurface.
+                                          createNativeSampleModel(owner.width,
+                                                                  owner.height),
+                                          new Point(0,0));
+        
+        buffer = new BufferedImage(CairoSurface.nativeColorModel, rst, true,
+                                   new Hashtable());
+      }
+    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 CairoSurface.nativeColorModel;
+  }
 }
 
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c,v
retrieving revision 1.7
diff -u -r1.7 gnu_java_awt_peer_gtk_GtkVolatileImage.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c	5 Sep 2006 21:27:28 -0000	1.7
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c	3 Oct 2006 19:11:41 -0000
@@ -110,10 +110,12 @@
   /* jint *pixeldata, *jpixdata; */
   jint *jpixdata;
   GdkPixmap *pixmap;
+  GdkPixbuf *pixbuf;
   jintArray jpixels;
   int width, height, depth, size;
   jclass cls;
   jfieldID field;
+  guchar *pixels;
 
   cls = (*env)->GetObjectClass (env, obj);
   field = (*env)->GetFieldID (env, cls, "width", "I");
@@ -131,11 +133,19 @@
 
   /* get depth in bytes */
   depth = gdk_drawable_get_depth( pixmap ) >> 3;
-  size = width * height * 4;
+  size = width * height;
   jpixels = (*env)->NewIntArray ( env, size );
   jpixdata = (*env)->GetIntArrayElements (env, jpixels, NULL);
-  /*  memcpy (jpixdata, pixeldata, size * sizeof( jint )); */
-
+  
+  pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE, 8, width, height );
+  gdk_pixbuf_get_from_drawable( pixbuf, pixmap, NULL, 0, 0, 0, 0, width, height );
+  
+  if (pixbuf != NULL)
+    {
+      pixels = gdk_pixbuf_get_pixels(pixbuf);
+      memcpy (jpixdata, pixels, size * sizeof(jint));
+    }
+    
   (*env)->ReleaseIntArrayElements (env, jpixels, jpixdata, 0);
 
   gdk_threads_leave();

Reply via email to