GdkGraphics2D.java, [...]: New files.
authorGraydon Hoare <graydon@redhat.com>
Wed, 17 Sep 2003 20:03:02 +0000 (20:03 +0000)
committerGraydon Hoare <graydon@gcc.gnu.org>
Wed, 17 Sep 2003 20:03:02 +0000 (20:03 +0000)
2003-09-17  Graydon Hoare  <graydon@redhat.com>

* gnu/java/awt/peer/gtk/GdkGraphics2D.java,
gnu/java/awt/peer/gtk/GdkPixbufDecoder.java,
jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics2D.c,
jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c:
New files.

From-SVN: r71475

libjava/ChangeLog
libjava/gnu/java/awt/peer/gtk/GdkGraphics2D.java [new file with mode: 0644]
libjava/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java [new file with mode: 0644]
libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics2D.c [new file with mode: 0644]
libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c [new file with mode: 0644]

index 6c208a493ccd83582983847346eb244372c1b507..9b98128111b77a4a691a6c13b914c02338c88cd4 100644 (file)
@@ -1,3 +1,11 @@
+2003-09-17  Graydon Hoare  <graydon@redhat.com>
+
+       * gnu/java/awt/peer/gtk/GdkGraphics2D.java,
+       gnu/java/awt/peer/gtk/GdkPixbufDecoder.java,
+       jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics2D.c,
+       jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c: 
+       New files.
+
 2003-09-16  Graydon Hoare  <graydon@redhat.com>
 
        * java/awt/BufferedImage.java (setData): Support non-component
diff --git a/libjava/gnu/java/awt/peer/gtk/GdkGraphics2D.java b/libjava/gnu/java/awt/peer/gtk/GdkGraphics2D.java
new file mode 100644 (file)
index 0000000..11c0371
--- /dev/null
@@ -0,0 +1,1146 @@
+/* GdkGraphics2D.java
+   Copyright (C) 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.peer.gtk;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.font.*;
+import java.awt.color.*;
+import java.awt.image.*;
+import java.awt.image.renderable.*;
+
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+import java.lang.Integer;
+import gnu.classpath.Configuration;
+
+public class GdkGraphics2D extends Graphics2D
+{
+
+  //////////////////////////////////////
+  ////// State Management Methods //////
+  //////////////////////////////////////
+
+  static 
+  {
+    if (Configuration.INIT_LOAD_LIBRARY)
+      {
+        System.loadLibrary("gtkpeer");
+      }
+    initStaticState ();
+  }
+  native static void initStaticState ();
+  private final int native_state = GtkGenericPeer.getUniqueInteger();  
+
+  private Paint paint;
+  private Stroke stroke;
+  private Color fg;
+  private Color bg;
+  private Shape clip;
+  private AffineTransform transform;
+  private GtkComponentPeer component;
+  private GdkFont font;  
+
+  native private int[] initState (GtkComponentPeer component);
+  native private void initState (int width, int height);
+  native private void copyState (GdkGraphics2D g);
+  native public void dispose ();
+
+  public void finalize ()
+  {
+    dispose();
+  }
+
+  public Graphics create ()
+  {
+    return new GdkGraphics2D (this);
+  }
+
+  public Graphics create (int x, int y, int width, int height)
+  {
+    return new GdkGraphics2D (width, height);
+  }
+
+  GdkGraphics2D (GdkGraphics2D g)
+  {
+    paint = g.paint;
+    stroke = g.stroke;
+
+    if (g.fg.getAlpha() != -1)
+      fg = new Color (g.fg.getRed (), g.fg.getGreen (), 
+                      g.fg.getBlue (), g.fg.getAlpha ());
+    else 
+      fg = new Color (g.fg.getRGB ());
+
+    if (g.bg.getAlpha() != -1)
+      bg = new Color(g.bg.getRed (), g.bg.getGreen (), 
+                     g.bg.getBlue (), g.bg.getAlpha ());
+    else
+      bg = new Color (g.bg.getRGB ());
+
+    if (g.clip == null)
+      clip = null;
+    else
+      clip = new Rectangle (g.getClipBounds ());
+
+    if (g.transform == null)
+      transform = null;
+    else
+      transform = new AffineTransform (g.transform);
+
+    component = g.component;
+    copyState (g);
+
+    setColor (fg);
+    setClip (clip);
+    setTransform (transform);
+  }
+
+  GdkGraphics2D (int width, int height)
+  {
+    initState (width, height);
+    bg = Color.black;
+    fg = Color.black;
+    transform = new AffineTransform ();
+  }
+
+  GdkGraphics2D (GtkComponentPeer component)
+  {
+    this.component = component;
+    int rgb[] = initState (component);
+    fg = new Color (rgb[0], rgb[1], rgb[2]);
+    bg = new Color (rgb[3], rgb[4], rgb[5]);
+    transform = new AffineTransform ();
+  }
+
+
+  ////////////////////////////////////
+  ////// Native Drawing Methods //////
+  ////////////////////////////////////
+
+  // GDK drawing methods
+  private native void gdkDrawDrawable (GdkGraphics2D other, int x, int y);
+
+  // drawing utility methods
+  private native void drawPixels (int pixels[], int w, int h, int stride);
+  private native void setTexturePixels (int pixels[], int w, int h, int stride);
+  private native void setGradient (double x1, double y1,
+                                   double x2, double y2,
+                                   int r1, int g1, int b1, int a1,
+                                   int r2, int g2, int b2, int a2,
+                                   boolean cyclic);
+
+  // simple passthroughs to cairo
+  private native void cairoSave ();
+  private native void cairoRestore ();
+  private native void cairoSetMatrix (double m00, double m10, 
+                                      double m01, double m11,
+                                      double m02, double m12);
+  private native void cairoSetOperator (int cairoOperator);
+  private native void cairoSetRGBColor (double red, double green, double blue);
+  private native void cairoSetAlpha (double alpha);
+  private native void cairoSetFillRule (int cairoFillRule);
+  private native void cairoSetLineWidth (double width);
+  private native void cairoSetLineCap (int cairoLineCap);
+  private native void cairoSetLineJoin (int cairoLineJoin);
+  private native void cairoSetDash (double dashes[], int ndash, double offset);
+  private native void cairoSetMiterLimit (double limit);
+  private native void cairoTranslate (double tx, double ty);
+  private native void cairoScale (double sx, double sy);
+  private native void cairoRotate (double angle);
+  private native void cairoNewPath ();
+  private native void cairoMoveTo (double x, double y);
+  private native void cairoLineTo (double x, double y);
+  private native void cairoCurveTo (double x1, double y1,
+                                 double x2, double y2,
+                                 double x3, double y3);  
+  private native void cairoRelMoveTo (double dx, double dy);
+  private native void cairoRelLineTo (double dx, double dy);
+  private native void cairoRelCurveTo (double dx1, double dy1,
+                                    double dx2, double dy2,
+                                    double dx3, double dy3);
+  private native void cairoRectangle (double x, double y, 
+                                   double width, double height);
+  private native void cairoClosePath ();
+  private native void cairoStroke ();
+  private native void cairoFill ();
+  private native void cairoClip ();
+
+
+  /////////////////////////////////////////////
+  ////// General Drawing Support Methods //////
+  /////////////////////////////////////////////
+
+  double x;
+  double y;
+  private void setPos (double nx, double ny)
+  {
+    x = nx;
+    y = ny;
+  }
+
+  private void walkPath(PathIterator p)
+  {
+    double coords[] = new double[6];
+
+    cairoSetFillRule (p.getWindingRule ());
+    for ( ; ! p.isDone (); p.next())
+      {
+        int seg = p.currentSegment (coords);
+        switch(seg)
+          {
+
+          case PathIterator.SEG_MOVETO:
+            setPos(coords[0], coords[1]);
+            cairoMoveTo (coords[0], coords[1]);
+            break;
+
+          case PathIterator.SEG_LINETO:
+            setPos(coords[0], coords[1]);
+            cairoLineTo (coords[0], coords[1]);
+            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) * (coords[0] - x);
+            double y1 = y + (2.0/3.0) * (coords[1] - y);
+            
+            double x2 = x1 + (1.0/3.0) * (coords[2] - x);
+            double y2 = y1 + (1.0/3.0) * (coords[3] - y);
+
+            setPos(coords[2], coords[3]);
+            cairoCurveTo (x1, y1,
+                          x2, y2,
+                          coords[2], coords[3]);
+            break;
+
+          case PathIterator.SEG_CUBICTO:
+            setPos(coords[4], coords[5]);
+            cairoCurveTo (coords[0], coords[1],
+                          coords[2], coords[3],
+                          coords[4], coords[5]);
+            break;
+
+          case PathIterator.SEG_CLOSE:
+            cairoClosePath ();
+            break;
+          }
+      }    
+  }
+
+
+  //////////////////////////////////////////////////
+  ////// Implementation of Graphics2D Methods //////
+  //////////////////////////////////////////////////
+
+  public void draw (Shape s)
+  {
+
+    if (stroke != null &&
+        !(stroke instanceof BasicStroke))
+      {
+        fill (stroke.createStrokedShape (s));
+        return;
+      }
+
+    cairoSave ();
+    cairoNewPath ();
+    if (s instanceof Rectangle2D)
+      {
+        Rectangle2D r = (Rectangle2D)s;
+        cairoRectangle (r.getX (), r.getY (), r.getWidth (), r.getHeight ());
+      }
+    else      
+      walkPath (s.getPathIterator (null));
+    cairoStroke ();
+    cairoRestore ();
+  }
+
+  public void fill(Shape s)
+  {
+    cairoSave();
+    cairoNewPath ();
+    if (s instanceof Rectangle2D)
+      {
+        Rectangle2D r = (Rectangle2D)s;
+        cairoRectangle (r.getX (), r.getY (), r.getWidth (), r.getHeight ());
+      }
+    else      
+      walkPath (s.getPathIterator (null));
+    cairoFill ();
+    cairoRestore ();
+  }
+
+  public void clip (Shape s)
+  {
+    clip = s;
+    cairoNewPath ();
+    if (s instanceof Rectangle2D)
+      {
+        Rectangle2D r = (Rectangle2D)s;
+        cairoRectangle (r.getX (), r.getY (), 
+                        r.getWidth (), r.getHeight ());
+      }
+    else      
+      walkPath (s.getPathIterator (null));
+    cairoClosePath ();
+    cairoClip ();
+  }
+
+  public Paint getPaint ()
+  {
+    return paint;
+  }
+
+  public AffineTransform getTransform ()
+  {
+    return transform;
+  }
+
+  public void setPaint (Paint p)
+  {
+    paint = p;
+    if (paint instanceof Color)
+      {
+        setColor ((Color) paint);
+      }
+    else if (paint instanceof TexturePaint)
+      {
+        TexturePaint tp = (TexturePaint) paint;
+        BufferedImage img = tp.getImage ();
+        int pixels[] = img.getRGB(0, 0, img.getWidth (), 
+                                  img.getHeight (), null, 
+                                  0, img.getWidth ());
+        setTexturePixels (pixels, img.getWidth (), 
+                          img.getHeight (), img.getWidth ());
+      }
+    else if (paint instanceof GradientPaint)
+      {
+        GradientPaint gp = (GradientPaint) paint;
+        Point2D p1 = gp.getPoint1 ();
+        Point2D p2 = gp.getPoint2 ();
+        Color c1 = gp.getColor1 ();
+        Color c2 = gp.getColor2 ();        
+        setGradient (p1.getX (), p1.getY (),
+                     p2.getX (), p2.getY (),
+                     c1.getRed (), c1.getGreen (), 
+                     c1.getBlue (), c1.getAlpha (),
+                     c2.getRed (), c2.getGreen (), 
+                     c2.getBlue (), c2.getAlpha (),
+                     gp.isCyclic ());
+      }
+    else
+      throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void setTransform (AffineTransform tx)
+  {
+    transform = tx;
+    if (transform != null)
+      {
+        double m[] = new double[6];
+        transform.getMatrix (m);
+        cairoSetMatrix (m[0], m[1], m[2], m[3], m[4], m[5]);
+      }
+  }
+
+  public void transform (AffineTransform tx)
+  {
+    if (transform == null)
+      transform = new AffineTransform (tx);
+    else
+      transform.concatenate (tx);
+    setTransform (transform);
+  }
+
+  public void rotate(double theta)
+  {
+    if (transform != null)
+      transform.rotate (theta);
+    cairoRotate (theta);
+  }
+
+  public void rotate(double theta, double x, double y)
+  {
+    if (transform != null)
+      transform.rotate (theta, x, y);
+    cairoTranslate (x, y);
+    cairoRotate (theta);
+    cairoTranslate (-x, -y);
+  }
+
+  public void scale(double sx, double sy)
+  {
+    if (transform != null)
+      transform.scale (sx, sy);
+    cairoScale (sx, sy);
+  }
+
+  public void translate (double tx, double ty)
+  {
+    if (transform != null)
+      transform.translate (tx, ty);
+    cairoTranslate (tx, ty);
+  }
+
+  public void translate (int x, int y)
+  {
+    translate ((double) x, (double) y);
+  }
+
+  public Stroke getStroke()
+  {
+    return stroke;
+  }
+
+  public void setStroke (Stroke st)
+  {
+    stroke = st;
+    if (stroke instanceof BasicStroke)
+      {
+        BasicStroke bs = (BasicStroke) stroke;
+        cairoSetLineCap (bs.getEndCap());
+        cairoSetLineWidth (bs.getLineWidth());
+        cairoSetLineJoin (bs.getLineJoin());
+        cairoSetMiterLimit (bs.getMiterLimit());
+        float dashes[] = bs.getDashArray();
+        if (dashes != null)
+          {
+            double double_dashes[] = new double[dashes.length];
+            for (int i = 0; i < dashes.length; i++)
+              double_dashes[i] = dashes[i];
+            cairoSetDash (double_dashes, double_dashes.length, 
+                          (double) bs.getDashPhase ());        
+          }
+      }
+  }
+
+
+  ////////////////////////////////////////////////
+  ////// Implementation of Graphics Methods //////
+  ////////////////////////////////////////////////
+
+  public void setPaintMode () 
+  { 
+    setComposite (java.awt.AlphaComposite.Xor); 
+  }
+
+  public void setXORMode (Color c) 
+  { 
+    setComposite (new BitwiseXorComposite (c)); 
+  }
+
+  public void setColor (Color c)
+  {
+    fg = c;
+    cairoSetRGBColor (fg.getRed() / 255.0, 
+                      fg.getGreen() / 255.0, 
+                      fg.getBlue() / 255.0);
+    cairoSetAlpha ((fg.getAlpha() & 255) / 255.0);
+  }
+
+  public Color getColor ()
+  {
+    return fg;
+  }
+
+  public void clipRect (int x, int y, int width, int height)
+  {
+    // this is *slightly* different than all the other clip functions: it
+    // intersects the clip area with the new clip rectangle. obviously.  of
+    // course, since Shape doesn't *have* any way of intersecting with a
+    // rectangle, we will promote the current clipping region to its
+    // bounding rectangle and then intersect with that.
+    if (clip == null)
+      {
+        cairoNewPath ();
+        cairoRectangle (x, y, width, height);
+        cairoClosePath ();
+        cairoClip ();
+        clip = new Rectangle (x, y, width, height);
+      }
+    else
+      {
+        clip (clip.getBounds ().intersection 
+              (new Rectangle (x, y, width, height)));
+      }
+  }
+
+  public Shape getClip ()
+  {
+    return clip;
+  }
+
+  public Rectangle getClipBounds ()
+  {
+    if (clip == null)
+      return null;
+    else
+      return clip.getBounds ();
+  }
+
+  public void setClip (int x, int y, int width, int height)
+  {
+    cairoNewPath ();
+    cairoRectangle (x, y, width, height);
+    cairoClosePath ();
+    cairoClip ();
+    clip = new Rectangle (x, y, width, height);
+  }
+
+  public void setClip (Shape s)
+  {
+    clip (s);
+  }
+
+  public void draw3DRect(int x, int y, int width, 
+                         int height, boolean raised)
+  {
+    Color std = fg;
+    Color light = std.brighter();
+    Color dark = std.darker();
+
+    if (!raised)
+      {
+        Color t = light;
+        light = dark;
+        dark = t;
+      }
+    
+    double x1 = (double) x;
+    double x2 = (double) x + width;
+
+    double y1 = (double) y;
+    double y2 = (double) y + height;
+
+    cairoSave ();
+    
+    cairoNewPath ();
+    setColor (light);
+    cairoMoveTo (x1, y1);
+    cairoLineTo (x2, y1);
+    cairoLineTo (x2, y2);
+    cairoStroke ();
+    
+    cairoNewPath ();
+    setColor (dark);
+    cairoMoveTo (x1, y1);
+    cairoLineTo (x1, y2);
+    cairoLineTo (x2, y2);
+    cairoStroke ();
+    
+    cairoRestore ();    
+    setColor (std);
+
+  }
+
+  public void fill3DRect(int x, int y, int width, 
+                         int height, boolean raised)
+  {
+    double step = 1.0;
+    if (stroke != null && stroke instanceof BasicStroke)
+      {
+        BasicStroke bs = (BasicStroke) stroke;
+        step = bs.getLineWidth();
+      }
+
+    Color bright = fg.brighter ();
+    Color dark = fg.darker ();
+      
+    draw3DRect (x, y, width, height, raised);
+    
+    cairoSave ();
+    cairoTranslate (step/2.0, step/2.0);
+    cairoNewPath ();
+    cairoRectangle ((double) x, (double) y, 
+                    ((double) width) - step, 
+                    ((double) height) - step );
+    cairoClosePath ();
+    cairoFill ();
+    cairoRestore ();
+  }
+
+
+  public void drawRect (int x, int y, int width, int height)
+  {
+    draw(new Rectangle (x, y, width, height));
+  }
+
+  public void fillRect (int x, int y, int width, int height)
+  {
+    fill(new Rectangle (x, y, width, height));
+  }
+
+  public void clearRect (int x, int y, int width, int height)
+  {
+    cairoSave ();
+    cairoSetRGBColor (bg.getRed() / 255.0, 
+                      bg.getGreen() / 255.0, 
+                      bg.getBlue() / 255.0);
+    cairoSetAlpha (1.0);
+    cairoNewPath ();
+    cairoRectangle (x, y, width, height);
+    cairoClosePath ();
+    cairoFill ();
+    cairoRestore ();
+  }
+
+  public void setBackground(Color c)
+  {
+    bg = c;
+  }
+
+
+  public Color getBackground()
+  {
+    return bg;
+  }
+
+
+  private void doPolygon(int[] xPoints, int[] yPoints, int nPoints, 
+                         boolean close, boolean fill)
+  {    
+    if (nPoints < 1)
+      return;
+    GeneralPath gp = new GeneralPath ();
+    gp.moveTo ((float)xPoints[0], (float)yPoints[0]);
+    for (int i = 1; i < nPoints; i++)
+      gp.lineTo ((float)xPoints[i], (float)yPoints[i]);
+    
+    if (close)
+      gp.closePath ();
+
+    Shape sh = gp;
+    if (fill == false &&
+        stroke != null &&
+        !(stroke instanceof BasicStroke))
+      {
+        sh = stroke.createStrokedShape (gp);
+        fill = true;
+      }
+    
+    if (fill) 
+      fill (sh);
+    else 
+      draw (sh);
+  }
+
+  public void drawLine (int x1, int y1, int x2, int y2)
+  {
+    int xp[] = new int[2];
+    int yp[] = new int[2];
+
+    xp[0] = x1;
+    xp[1] = x2;
+    yp[0] = y1;
+    yp[1] = y2;
+    
+    doPolygon (xp, yp, 2, false, false);
+  }
+
+  public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
+  {
+    doPolygon (xPoints, yPoints, nPoints, true, true);
+  }
+  
+  public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
+  {    
+    doPolygon (xPoints, yPoints, nPoints, true, false);
+  }
+
+  public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
+  {
+    doPolygon (xPoints, yPoints, nPoints, false, false);
+  }
+
+  private boolean drawRaster (ColorModel cm, Raster r)
+  {
+    if (r == null)
+      return false;
+
+    SampleModel sm = r.getSampleModel ();
+    DataBuffer db = r.getDataBuffer ();
+
+    if (db == null || sm == null)
+      return false;
+
+    if (cm == null)
+      cm = ColorModel.getRGBdefault ();
+
+    int pixels[] = null;
+
+    if (sm.getDataType () == DataBuffer.TYPE_INT &&
+        db instanceof DataBufferInt &&
+        db.getNumBanks () == 1)
+      {
+        // single bank, ARGB-ints buffer in sRGB space
+        DataBufferInt dbi = (DataBufferInt)db;
+        pixels = dbi.getData ();
+      }
+    else
+      pixels = r.getPixels (0, 0, r.getWidth (), r.getHeight (), pixels);
+    
+    ColorSpace cs = cm.getColorSpace ();
+    if (cs != null && 
+        cs.getType () != ColorSpace.CS_sRGB)
+      {
+        int pixels2[] = new int[pixels.length];        
+        for (int i = 0; i < pixels2.length; i++)
+          pixels2[i] = cm.getRGB (pixels[i]);        
+        pixels = pixels2;
+      }
+    
+    cairoSave ();
+    cairoTranslate (x, y);
+    drawPixels (pixels, r.getWidth (), r.getHeight (), r.getWidth ());
+    cairoRestore ();    
+    return true;
+  }
+
+  public boolean drawImage (Image img, int x, int y, 
+                            ImageObserver observer)
+  {
+    if (img instanceof GtkOffScreenImage &&
+        img.getGraphics () instanceof GdkGraphics2D &&            
+        (transform == null || transform.isIdentity ())) 
+      {
+        // we are being asked to flush a double buffer from Gdk
+        GdkGraphics2D g2 = (GdkGraphics2D) img.getGraphics ();
+        gdkDrawDrawable (g2, x, y);
+        return true;
+      }
+    else
+      {
+        if (img instanceof BufferedImage)
+          {
+            // draw an image which has actually been loaded into memory fully
+            BufferedImage b = (BufferedImage) img;
+            return drawRaster (b.getColorModel (), b.getData ());
+          }        
+        else
+          {
+            // begin progressive loading in a separate thread
+            new PainterThread (this, img);
+            return false;
+          }
+      }
+  }
+
+
+  ////////////////////////////////////////
+  ////// Supporting Private Classes //////
+  ////////////////////////////////////////
+  
+  private class PainterThread implements Runnable, ImageConsumer
+  {
+
+    // this is a helper which is spun off when someone tries to do
+    // Graphics2D.drawImage on an image we cannot determine to be either
+    // one of our own offscreen images or a BufferedImage; that is, when
+    // someone wants to draw an image which is possibly still loading over
+    // a network or something. you run it in a separate thread and it
+    // writes through to the underlying Graphics2D as pixels becomg
+    // available.
+
+    GdkGraphics2D gr;
+    Image image;
+    ColorModel defaultModel;
+
+    public PainterThread (GdkGraphics2D g, Image im)
+    {
+      image = im;
+      this.gr = (GdkGraphics2D) g.create ();
+      new Thread (this).start ();
+    }
+    
+    public void imageComplete (int status)
+    {
+    }
+    
+    public void setColorModel (ColorModel model) 
+    {
+      defaultModel = model;
+    }
+    
+    public void setDimensions (int width, int height)
+    {
+    }
+    
+    public void setHints (int hintflags)
+    {
+    }
+    
+    public void setPixels (int x, int y, int w, int h, ColorModel model, 
+                           byte[] pixels, int off, int scansize)
+    {
+    }
+    
+    public void setPixels (int x, int y, int w, int h, ColorModel model, 
+                           int[] pixels, int off, int scansize)
+      {
+        gr.cairoSave ();
+        gr.cairoTranslate (x, y);
+
+        if (model == null)
+          model = defaultModel;
+
+        int pixels2[];
+        if (model != null)
+          {
+            pixels2 = new int[pixels.length];
+            for (int yy = 0; yy < h; yy++)
+              for (int xx = 0; xx < w; xx++)
+                {
+                  int i = yy * scansize + xx;
+                  pixels2[i] = model.getRGB (pixels[i]);
+                }
+          }
+        else
+          pixels2 = pixels;
+
+        gr.drawPixels (pixels2, w, h, scansize);
+        gr.cairoRestore ();
+      }
+
+    public void setProperties (java.util.Hashtable props)
+    {
+    }
+    
+    public void run ()
+    {
+      image.getSource ().startProduction (this);
+      gr.dispose ();
+    }
+  }
+
+
+  private class BitwiseXorComposite implements Composite
+  {
+    // this is a special class which does a bitwise XOR composite, for
+    // backwards compatibility sake. it does *not* implement the
+    // porter-duff XOR operator.  the porter-duff XOR is unrelated to
+    // bitwise XOR; it just happens to have a similar name but it
+    // represents a desire to composite the exclusive or of overlapping
+    // subpixel regions. bitwise XOR is for drawing "highlights" such as
+    // cursors (in a cheap oldskool bitblit fashion) by inverting colors
+    // temporarily and then inverting them back.
+
+    Color xorColor;
+    
+    class BitwiseXorCompositeContext implements CompositeContext
+    {
+      ColorModel srcColorModel;
+      ColorModel dstColorModel;
+      
+      public BitwiseXorCompositeContext (ColorModel s,
+                                         ColorModel d)
+      {
+        srcColorModel = s;
+        dstColorModel = d;
+      }
+
+      public void dispose () 
+      {
+      }
+
+      public void compose (Raster src,
+                           Raster dstIn,
+                           WritableRaster dstOut)
+      {
+        Rectangle srcRect = src.getBounds ();
+        Rectangle dstInRect = dstIn.getBounds ();
+        Rectangle dstOutRect = dstOut.getBounds ();
+        
+        int xp = xorColor.getRGB ();
+        int x = 0, y = 0;
+        int w = Math.min (Math.min (srcRect.width, dstOutRect.width), dstInRect.width);
+        int h = Math.min (Math.min (srcRect.height, dstOutRect.height), dstInRect.height);
+        Object srcPix = null, dstPix = null;
+
+        for (y = 0; y < h; y++)
+          for (x = 0; x < w; x++)
+            {
+              srcPix = src.getDataElements (x + srcRect.x, y + srcRect.y, srcPix);
+              dstPix = dstIn.getDataElements (x + dstInRect.x, y + dstInRect.y, dstPix);
+              int sp = srcColorModel.getRGB (srcPix);
+              int dp = dstColorModel.getRGB (dstPix);
+              int rp = sp ^ xp ^ dp;
+              dstOut.setDataElements (x + dstOutRect.x, y + dstOutRect.y, 
+                                      dstColorModel.getDataElements (rp, null));
+            }
+      }
+    }
+    
+    public BitwiseXorComposite (Color c)
+    {
+      xorColor = c;
+    }
+    
+    public CompositeContext createContext (ColorModel srcColorModel, 
+                                           ColorModel dstColorModel, 
+                                           RenderingHints hints) 
+    {
+      return new BitwiseXorCompositeContext (srcColorModel, dstColorModel);
+    }
+  }  
+
+
+  ///////////////////////////////////////////////
+  ////// Unimplemented Stubs and Overloads //////
+  ///////////////////////////////////////////////
+
+  public boolean drawImage(Image image, 
+                           AffineTransform xform,
+                           ImageObserver obs)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+  
+  public void drawImage(BufferedImage image,
+                        BufferedImageOp op,
+                        int x,
+                        int y)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+  
+  public void drawRenderedImage(RenderedImage image,
+                                AffineTransform xform)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+  
+  public void drawRenderableImage(RenderableImage image,
+                                  AffineTransform xform)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawString(String text, float x, float y)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+  
+  public void drawString(AttributedCharacterIterator iterator,
+                         float x, float y)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+    
+  public boolean hit(Rectangle rect, Shape text,
+                     boolean onStroke)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public GraphicsConfiguration getDeviceConfiguration()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void setComposite(Composite comp)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void setRenderingHint(RenderingHints.Key hintKey,
+                               Object hintValue)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public Object getRenderingHint(RenderingHints.Key hintKey)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+  
+  public void setRenderingHints(Map hints)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void addRenderingHints(Map hints)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public RenderingHints getRenderingHints()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void shear(double shearX, double shearY)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public Composite getComposite()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public FontRenderContext getFontRenderContext ()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawGlyphVector (GlyphVector g, float x, float y)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void copyArea (int x, int y, int width, int height, int dx, int dy)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawArc (int x, int y, int width, int height,
+                       int startAngle, int arcAngle)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public boolean drawImage (Image img, int x, int y, Color bgcolor, 
+                            ImageObserver observer)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public boolean drawImage (Image img, int x, int y, int width, int height, 
+                            Color bgcolor, ImageObserver observer)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public boolean drawImage (Image img, int x, int y, int width, int height, 
+                            ImageObserver observer)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public boolean drawImage (Image img, int dx1, int dy1, int dx2, int dy2, 
+                            int sx1, int sy1, int sx2, int sy2, 
+                            Color bgcolor, ImageObserver observer)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public boolean drawImage (Image img, int dx1, int dy1, int dx2, int dy2, 
+                            int sx1, int sy1, int sx2, int sy2, 
+                            ImageObserver observer) 
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawOval(int x, int y, int width, int height)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawRoundRect(int x, int y, int width, int height, 
+                            int arcWidth, int arcHeight)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawString (String str, int x, int y)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void drawString (AttributedCharacterIterator ci, int x, int y)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void fillArc (int x, int y, int width, int height, 
+                       int startAngle, int arcAngle)
+  {
+    cairoNewPath ();
+    walkPath (new Arc2D.Double((double)x, (double)y, 
+                               (double)width, (double)height,
+                               (double)startAngle, (double)arcAngle,
+                               Arc2D.PIE).getPathIterator (null));
+    cairoClosePath ();
+    cairoFill ();
+  }
+
+  public void fillOval(int x, int y, int width, int height)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void fillRoundRect (int x, int y, int width, int height, 
+                             int arcWidth, int arcHeight)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public Font getFont ()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public FontMetrics getFontMetrics ()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public FontMetrics getFontMetrics (Font f)
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+  public void setFont (Font f)
+  {
+    if (f instanceof GdkFont)
+      font = (GdkFont) f;
+    else
+      font = new GdkFont (f.getAttributes ());
+  }
+
+  public String toString()
+  {
+    throw new java.lang.UnsupportedOperationException ();
+  }
+
+}
diff --git a/libjava/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java b/libjava/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java
new file mode 100644 (file)
index 0000000..55f3338
--- /dev/null
@@ -0,0 +1,217 @@
+/* GdkPixbufDecoder.java -- Image data decoding object
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   
+   This file is part of GNU Classpath.
+   
+   GNU Classpath is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+   
+   GNU Classpath is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with GNU Classpath; see the file COPYING.  If not, write to the
+   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+   
+   Linking this library statically or dynamically with other modules is
+   making a combined work based on this library.  Thus, the terms and
+   conditions of the GNU General Public License cover the whole
+   combination.
+   
+   As a special exception, the copyright holders of this library give you
+   permission to link this library with independent modules to produce an
+   executable, regardless of the license terms of these independent
+   modules, and to copy and distribute the resulting executable under
+   terms of your choice, provided that you also meet, for each linked
+   independent module, the terms and conditions of the license of that
+   module.  An independent module is a module which is not derived from
+   or based on this library.  If you modify this library, you may extend
+   this exception to your version of the library, but you are not
+   obligated to do so.  If you do not wish to do so, delete this
+   exception statement from your version. */
+
+
+package gnu.java.awt.peer.gtk;
+
+import java.awt.image.*;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Vector;
+import java.util.Hashtable;
+import gnu.classpath.Configuration;
+
+public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
+{
+  static 
+  {
+    if (Configuration.INIT_LOAD_LIBRARY)
+      {
+        System.loadLibrary("gtkpeer");
+      }
+    initStaticState ();
+  }
+  native static void initStaticState ();
+  private final int native_state = GtkGenericPeer.getUniqueInteger ();
+
+  // the current set of ImageConsumers for this decoder
+  Vector curr;
+
+  // interface to GdkPixbuf
+  native void initState ();
+  native void pumpBytes (byte bytes[], int len);
+  native void finish ();
+  
+  // gdk-pixbuf provids data in RGBA format
+  static final ColorModel cm = new DirectColorModel (32, 0xff000000, 
+                                                     0x00ff0000, 
+                                                     0x0000ff00, 
+                                                     0x000000ff);
+  public GdkPixbufDecoder (String filename)
+  {
+    super (filename);
+    initState ();
+  }
+  
+  public GdkPixbufDecoder (URL url)
+  {
+    super (url);
+    initState ();
+  }
+
+  // called back by native side
+  void areaPrepared (int width, int height)
+  {
+
+    if (curr == null)
+      return;
+
+    for (int i = 0; i < curr.size (); i++)
+      {
+        ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
+        ic.setDimensions (width, height);
+        ic.setColorModel (cm);
+        ic.setHints (ImageConsumer.RANDOMPIXELORDER);
+      }
+  }
+  
+  // called back by native side
+  void areaUpdated (int x, int y, int width, int height, 
+                    int pixels[], int scansize)
+  {
+    if (curr == null)
+      return;
+    
+    for (int i = 0; i < curr.size (); i++)
+      {
+        ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
+        ic.setPixels (x, y, width, height, cm, pixels, 0, scansize);
+      }
+  }
+  
+  // called from an async image loader of one sort or another, this method
+  // repeatedly reads bytes from the input stream and passes them through a
+  // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn
+  // decodes the image data and calls back areaPrepared and areaUpdated on
+  // this object, feeding back decoded pixel blocks, which we pass to each
+  // of the ImageConsumers in the provided Vector.
+
+  void produce (Vector v, FileInputStream is) throws IOException
+  {
+    curr = v;
+
+    byte bytes[] = new byte[4096];
+    int len = 0;
+    while ((len = is.read (bytes)) != -1)
+      pumpBytes (bytes, len);
+    
+    for (int i = 0; i < curr.size (); i++)
+      {
+        ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
+        ic.imageComplete (ImageConsumer.STATICIMAGEDONE);
+      }
+
+    curr = null;
+  }
+
+  // remaining helper class and static method is a convenience for the Gtk
+  // peers, for loading a BufferedImage in off a disk file. one would think
+  // this ought to be fairly straightforward, but it does not appear
+  // anywhere else I can find.
+
+  private class BufferedImageBuilder implements ImageConsumer
+  {
+    BufferedImage bufferedImage;
+    ColorModel defaultModel;
+
+    public BufferedImage getBufferedImage()
+    {
+      return bufferedImage;
+    }
+
+    public void setDimensions(int width, int height)
+    {
+      bufferedImage = new BufferedImage (width, height, BufferedImage.TYPE_INT_ARGB);
+    }
+    
+    public void setProperties(Hashtable props) {}
+
+    public void setColorModel(ColorModel model) 
+    {
+      defaultModel = model;
+    }
+
+    public void setHints(int flags) {}
+
+    public void setPixels(int x, int y, int w, int h, 
+                          ColorModel model, byte[] pixels, 
+                          int offset, int scansize)
+    {
+    }      
+
+    public void setPixels(int x, int y, int w, int h, 
+                          ColorModel model, int[] pixels, 
+                          int offset, int scansize)
+    {
+      if (bufferedImage != null)
+        {
+
+          if (model == null)
+            model = defaultModel;
+
+          int pixels2[];
+          if (model != null)
+            {
+              pixels2 = new int[pixels.length];
+              for (int yy = 0; yy < h; yy++)
+                for (int xx = 0; xx < w; xx++)
+                  {
+                    int i = yy * scansize + xx;
+                    pixels2[i] = model.getRGB (pixels[i]);
+                  }
+            }
+          else
+            pixels2 = pixels;
+
+          bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
+        }
+    }
+
+    public void imageComplete(int status) {}
+  }
+
+  public static BufferedImage createBufferedImage (String filename)
+  {
+    BufferedImageBuilder bb = new BufferedImageBuilder ();
+    GdkPixbufDecoder dec = new GdkPixbufDecoder (filename);
+    dec.startProduction (bb);
+    return bb.getBufferedImage ();
+  }
+
+}
diff --git a/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics2D.c b/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics2D.c
new file mode 100644 (file)
index 0000000..04eb2e5
--- /dev/null
@@ -0,0 +1,1069 @@
+/* gnu_java_awt_peer_gtk_GdkGraphics2d.c
+   Copyright (C) 2003 Free Software Foundation, Inc.
+
+   This file is part of GNU Classpath.
+   
+   GNU Classpath is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+   
+   GNU Classpath is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with GNU Classpath; see the file COPYING.  If not, write to the
+   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+   
+   Linking this library statically or dynamically with other modules is
+   making a combined work based on this library.  Thus, the terms and
+   conditions of the GNU General Public License cover the whole
+   combination.
+   
+   As a special exception, the copyright holders of this library give you
+   permission to link this library with independent modules to produce an
+   executable, regardless of the license terms of these independent
+   modules, and to copy and distribute the resulting executable under
+   terms of your choice, provided that you also meet, for each linked
+   independent module, the terms and conditions of the license of that
+   module.  An independent module is a module which is not derived from
+   or based on this library.  If you modify this library, you may extend
+   this exception to your version of the library, but you are not
+   obligated to do so.  If you do not wish to do so, delete this
+   exception statement from your version. */
+
+#include "gtkpeer.h"
+#include "gnu_java_awt_peer_gtk_GdkGraphics2D.h"
+#include <gdk/gdktypes.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+#include <cairo.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct state_table *native_graphics2d_state_table;
+
+#define NSA_G2D_INIT(env, clazz) \
+  native_graphics2d_state_table = init_state_table (env, clazz)
+
+#define NSA_GET_G2D_PTR(env, obj) \
+  get_state (env, obj, native_graphics2d_state_table)
+
+#define NSA_SET_G2D_PTR(env, obj, ptr) \
+  set_state (env, obj, native_graphics2d_state_table, (void *)ptr)
+
+#define NSA_DEL_G2D_PTR(env, obj) \
+  remove_state_slot (env, obj, native_graphics2d_state_table)
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initStaticState 
+  (JNIEnv *env, jclass clazz)
+{
+    NSA_G2D_INIT (env, clazz);
+}
+
+/* these public final constants are part of the java2d public API, so we
+   write them explicitly here to save fetching them from the constant pool
+   all the time. */
+
+#ifndef min
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#endif
+
+enum java_awt_alpha_composite_rule
+  {
+    java_awt_alpha_composite_CLEAR = 1,
+    java_awt_alpha_composite_SRC = 2,
+    java_awt_alpha_composite_SRC_OVER = 3,
+    java_awt_alpha_composite_DST_OVER = 4,
+    java_awt_alpha_composite_SRC_IN = 5,
+    java_awt_alpha_composite_DST_IN = 6,
+    java_awt_alpha_composite_SRC_OUT = 7,
+    java_awt_alpha_composite_DST_OUT = 8,
+    java_awt_alpha_composite_DST = 9,
+    java_awt_alpha_composite_SRC_ATOP = 10,
+    java_awt_alpha_composite_DST_ATOP = 11,
+    java_awt_alpha_composite_XOR = 12
+  };
+
+enum java_awt_basic_stroke_join_rule
+  {
+    java_awt_basic_stroke_JOIN_MITER = 0,
+    java_awt_basic_stroke_JOIN_ROUND = 1,
+    java_awt_basic_stroke_JOIN_BEVEL = 2
+  };
+
+enum java_awt_basic_stroke_cap_rule
+  {
+    java_awt_basic_stroke_CAP_BUTT = 0,
+    java_awt_basic_stroke_CAP_ROUND = 1,
+    java_awt_basic_stroke_CAP_SQUARE = 2
+  };
+
+enum java_awt_geom_path_iterator_winding_rule
+  {
+    java_awt_geom_path_iterator_WIND_EVEN_ODD = 0,
+    java_awt_geom_path_iterator_WIND_NON_ZERO = 1
+  };
+
+
+static void 
+grab_current_drawable (GtkWidget *widget, GdkDrawable **draw, GdkWindow **win)
+{  
+  g_assert (widget != NULL);
+  g_assert (draw != NULL);
+  g_assert (win != NULL);
+
+  if (GTK_IS_WINDOW (widget))
+    {
+      *win = find_gtk_layout (widget)->bin_window;
+    }
+  else if (GTK_IS_LAYOUT (widget))
+    {
+      *win = GTK_LAYOUT (widget)->bin_window;
+    }
+  else
+    {
+      *win = widget->window;
+    }
+
+  *draw = *win;
+  gdk_window_get_internal_paint_info (*win, draw, 0, 0);
+  g_object_ref (*draw);
+}
+
+
+static int
+x_server_has_render_extension (void)
+{
+  int ev = 0, err = 0; 
+  return (int) XRenderQueryExtension (GDK_DISPLAY (), &ev, &err);
+}
+
+
+static void
+init_graphics2d_as_pixbuf (struct graphics2d *gr)
+{
+  gint width, height;
+  gint bits_per_sample = 8;
+  gint total_channels = 4;
+  gboolean has_alpha = TRUE;
+  
+  g_assert (gr != NULL);
+  g_assert (gr->drawable != NULL);
+
+  if (gr->debug) printf ("initializing graphics2d as pixbuf\n");
+  gdk_drawable_get_size (gr->drawable, &width, &height);
+  gr->drawbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
+                               has_alpha, bits_per_sample,
+                               width, height);
+  g_assert (gr->drawbuf != NULL);
+  g_assert (gdk_pixbuf_get_bits_per_sample (gr->drawbuf) == bits_per_sample);
+  g_assert (gdk_pixbuf_get_n_channels (gr->drawbuf) == total_channels);
+  
+  gr->surface = cairo_surface_create_for_image (gdk_pixbuf_get_pixels (gr->drawbuf), 
+                                               CAIRO_FORMAT_ARGB32, 
+                                               gdk_pixbuf_get_width (gr->drawbuf), 
+                                               gdk_pixbuf_get_height (gr->drawbuf), 
+                                               gdk_pixbuf_get_rowstride (gr->drawbuf));      
+  g_assert (gr->surface != NULL);
+  g_assert (gr->cr != NULL);
+  cairo_set_target_surface (gr->cr, gr->surface);
+}
+
+static void
+init_graphics2d_as_renderable (struct graphics2d *gr)
+{
+  Drawable draw;
+  Display * dpy;
+  Visual * vis;
+  
+  g_assert (gr != NULL);
+  g_assert (gr->drawable != NULL);
+
+  gr->drawbuf = NULL;
+  
+  if (gr->debug) printf ("initializing graphics2d as renderable\n");
+  draw = gdk_x11_drawable_get_xid (gr->drawable);
+  
+  dpy = gdk_x11_drawable_get_xdisplay (gr->drawable);
+  g_assert (dpy != NULL);
+  
+  vis = gdk_x11_visual_get_xvisual (gdk_drawable_get_visual (gr->drawable));
+  g_assert (vis != NULL);
+  
+  gr->surface = cairo_surface_create_for_drawable (dpy, draw, vis, 
+                                                  CAIRO_FORMAT_ARGB32,
+                                                  DefaultColormap (dpy, DefaultScreen (dpy)));
+  g_assert (gr->surface != NULL);
+  g_assert (gr->cr != NULL);
+  cairo_set_target_surface (gr->cr, gr->surface);
+}
+
+static void
+begin_drawing_operation (struct graphics2d * gr)
+{  
+  gdk_threads_enter ();
+  if (gr->drawbuf)
+    {
+
+      gint drawable_width, drawable_height;
+      gint pixbuf_width, pixbuf_height;
+      gint width, height;
+      
+      gdk_drawable_get_size (gr->drawable, &drawable_width, &drawable_height);
+      pixbuf_width = gdk_pixbuf_get_width (gr->drawbuf);
+      pixbuf_height = gdk_pixbuf_get_height (gr->drawbuf);
+      width = min (drawable_width, pixbuf_width);
+      height = min (drawable_height, pixbuf_height);
+
+      gdk_pixbuf_get_from_drawable (gr->drawbuf, /* destination pixbuf */
+                                   gr->drawable, 
+                                   NULL, /* colormap */
+                                   0, 0, 0, 0,
+                                   width, height); 
+      
+      if (gr->debug) printf ("copied (%d, %d) pixels from GDK drawable to pixbuf\n",
+                            width, height);      
+    }
+}
+
+static void
+end_drawing_operation (struct graphics2d * gr)
+{
+  if (gr->drawbuf)
+    { 
+      gint drawable_width, drawable_height;
+      gint pixbuf_width, pixbuf_height;
+      gint width, height;
+      
+      gdk_drawable_get_size (gr->drawable, &drawable_width, &drawable_height);
+      pixbuf_width = gdk_pixbuf_get_width (gr->drawbuf);
+      pixbuf_height = gdk_pixbuf_get_height (gr->drawbuf);
+      width = min (drawable_width, pixbuf_width);
+      height = min (drawable_height, pixbuf_height);
+
+      gdk_draw_pixbuf (gr->drawable, NULL, gr->drawbuf,
+                      0, 0, 0, 0, 
+                      width, height, 
+                      GDK_RGB_DITHER_NORMAL, 0, 0);
+
+      if (gr->debug) printf ("copied (%d, %d) pixels from pixbuf to GDK drawable\n",
+                            width, height);
+    }
+  gdk_threads_leave ();
+}
+
+
+static void 
+update_pattern_transform (struct graphics2d *gr)
+{
+  double a, b, c, d, tx, ty;
+  cairo_matrix_t *mat = NULL;
+
+  g_assert (gr != NULL);
+  if (gr->pattern == NULL)
+    return;
+
+  return;
+  /* temporarily disabled: ambiguous behavior */
+  /*   cairo_get_matrix (gr->cr, &a, &b, &c, &d, &tx, &ty); */
+  mat = cairo_matrix_create ();
+  g_assert (mat != NULL);
+  cairo_matrix_set_affine (mat, a, b, c, d, tx, ty);
+  cairo_surface_set_matrix (gr->pattern, mat);
+  cairo_matrix_destroy (mat);
+}
+
+static void
+check_for_debug (struct graphics2d *gr)
+{
+  gr->debug = (gboolean)(getenv("DEBUGJ2D") != NULL);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_copyState
+  (JNIEnv *env, jobject obj, jobject old)
+{
+  struct graphics2d *g = NULL, *g_old = NULL;
+
+  g = (struct graphics2d *) malloc (sizeof (struct graphics2d));
+  g_assert (g != NULL);
+  memset (g, 0, sizeof(struct graphics2d));
+
+  g_old = (struct graphics2d *) NSA_GET_G2D_PTR (env, old);
+  g_assert (g_old != NULL);
+
+  g->drawable = g_old->drawable;
+  g->debug = g_old->debug; 
+
+  gdk_threads_enter ();
+  g_object_ref (g->drawable);
+  
+  g->cr = cairo_create();
+  g_assert (g->cr != NULL);
+
+  if (x_server_has_render_extension ())
+    init_graphics2d_as_renderable (g);
+  else
+    init_graphics2d_as_pixbuf (g);
+
+  cairo_surface_set_filter (g->surface, CAIRO_FILTER_BILINEAR);
+
+  gdk_threads_leave ();
+
+  NSA_SET_G2D_PTR (env, obj, g);
+}
+
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__II
+  (JNIEnv *env, jobject obj, jint width, jint height)
+{
+  struct graphics2d *gr;
+
+  gdk_threads_enter ();
+  
+  gr = (struct graphics2d *) malloc (sizeof (struct graphics2d));
+  g_assert (gr != NULL);
+  memset (gr, 0, sizeof(struct graphics2d));
+
+  check_for_debug (gr);  
+
+  if (gr->debug) printf ("constructing offscreen drawable of size (%d,%d)\n",
+                        width, height);
+  
+  gr->drawable = (GdkDrawable *) gdk_pixmap_new (NULL, width, height, 
+                                                gdk_rgb_get_visual ()->depth);
+  g_assert (gr->drawable != NULL);
+
+  gr->cr = cairo_create();
+  g_assert (gr->cr != NULL);
+
+  if (x_server_has_render_extension ())
+    init_graphics2d_as_renderable (gr);
+  else
+    init_graphics2d_as_pixbuf (gr);
+
+  gdk_threads_leave ();
+  if (gr->debug) printf ("constructed offscreen drawable of size (%d,%d)\n",
+                        width, height);
+  NSA_SET_G2D_PTR (env, obj, gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_gdkDrawDrawable
+  (JNIEnv *env, jobject self, jobject other, jint x, jint y)
+{
+  struct graphics2d *src = NULL, *dst = NULL;
+  gint s_height, s_width, d_height, d_width, height, width;
+  GdkGC *gc;
+
+  src = (struct graphics2d *)NSA_GET_G2D_PTR (env, other);
+  dst = (struct graphics2d *)NSA_GET_G2D_PTR (env, self);
+  g_assert (src != NULL);
+  g_assert (dst != NULL);  
+
+  if (src->debug) printf ("copying from offscreen drawable\n");
+
+  gdk_threads_enter ();
+  gdk_drawable_get_size (src->drawable, &s_width, &s_height);
+  gdk_drawable_get_size (dst->drawable, &d_width, &d_height);
+  width = min (s_width, d_width);
+  height = min (s_width, d_height);
+
+  gc = gdk_gc_new (dst->drawable);
+  g_assert (gc != NULL);
+
+  gdk_draw_drawable(dst->drawable, gc, src->drawable,
+                   0, 0, x, y, width, height);
+
+  g_object_unref (gc);
+
+  if (src->debug) printf ("copied %d x %d pixels from offscreen drawable\n", width, height);
+  gdk_threads_leave ();  
+}
+
+static jintArray
+current_colors_of_widget (GtkWidget *widget, JNIEnv *env)
+{
+  GdkColor color;
+  jintArray array;
+  jint *rgb;
+
+  g_assert (widget != NULL);
+  g_assert (env != NULL);
+
+  color = widget->style->fg[GTK_STATE_NORMAL];
+  array = (*env)->NewIntArray (env, 6);
+
+  rgb = (*env)->GetIntArrayElements (env, array, NULL);
+  rgb[0] = color.red >> 8;
+  rgb[1] = color.green >> 8;
+  rgb[2] = color.blue >> 8;
+
+  color = widget->style->bg[GTK_STATE_NORMAL];
+  rgb[3] = color.red >> 8;
+  rgb[4] = color.green >> 8;
+  rgb[5] = color.blue >> 8;
+
+  (*env)->ReleaseIntArrayElements (env, array, rgb, 0);
+  
+  return array;
+}
+
+JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__Lgnu_java_awt_peer_gtk_GtkComponentPeer_2
+  (JNIEnv *env, jobject obj, jobject peer)
+{
+  struct graphics2d *gr = NULL;
+  GtkWidget *widget = NULL;
+  void *ptr = NULL;
+  jintArray color;
+
+  ptr = NSA_GET_PTR (env, peer);
+  g_assert (ptr != NULL);
+  gdk_threads_enter ();
+
+  gr = (struct graphics2d *) malloc (sizeof (struct graphics2d));
+  g_assert (gr != NULL);
+  memset (gr, 0, sizeof(struct graphics2d));
+
+  check_for_debug (gr);
+
+  gr->cr = cairo_create();
+  g_assert (gr->cr != NULL);
+
+  widget = GTK_WIDGET (ptr);
+  g_assert (widget != NULL);
+
+  grab_current_drawable (widget, &(gr->drawable), &(gr->win));
+  g_assert (gr->drawable != NULL);
+
+  if (x_server_has_render_extension ())
+    init_graphics2d_as_renderable (gr);
+  else
+    init_graphics2d_as_pixbuf (gr);
+
+  color = current_colors_of_widget (widget, env);
+
+  gdk_threads_leave ();
+  NSA_SET_G2D_PTR (env, obj, gr);
+  return color;
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_dispose
+  (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+
+  gr = (struct graphics2d *) NSA_DEL_G2D_PTR (env, obj);
+  if (gr == NULL) 
+    return; /* dispose has been called more than once */
+  
+  gdk_threads_enter ();
+
+  if (gr->surface)
+    cairo_surface_destroy (gr->surface);
+
+  cairo_destroy (gr->cr);
+
+  if (gr->drawbuf)
+    g_object_unref (gr->drawbuf); 
+
+  g_object_unref (gr->drawable);
+  free (gr);
+
+  if (gr->pattern)
+    cairo_surface_destroy (gr->pattern);
+
+  if (gr->pattern_pixels)
+    free (gr->pattern_pixels);
+
+  if (gr->debug) printf ("disposed of graphics2d\n");
+
+  gdk_threads_leave ();
+}
+
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setGradient 
+  (JNIEnv *env, jobject obj, 
+   jdouble x1, jdouble y1, 
+   jdouble x2, jdouble y2,
+   jint r1, jint g1, jint b1, jint a1,
+   jint r2, jint g2, jint b2, jint a2,
+   jboolean cyclic)
+{
+  struct graphics2d *gr = NULL;
+  cairo_surface_t *surf = NULL;
+  cairo_matrix_t *mat = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+
+  if (gr->debug) printf ("setGradient (%f,%f) -> (%f,%f); (%d,%d,%d,%d) -> (%d,%d,%d,%d)\n",
+                        x1, y1, 
+                        x2, y2, 
+                        r1, g1, b1, a1, 
+                        r2, g2, b2, a2);
+  
+  cairo_save (gr->cr);
+  
+  if (cyclic)
+    surf = cairo_surface_create_similar (gr->surface, CAIRO_FORMAT_ARGB32, 3, 2);
+  else
+    surf = cairo_surface_create_similar (gr->surface, CAIRO_FORMAT_ARGB32, 2, 2);      
+  g_assert (surf != NULL);
+
+  cairo_set_target_surface (gr->cr, surf);
+  
+  cairo_identity_matrix (gr->cr);
+
+  cairo_set_rgb_color (gr->cr, r1 / 255.0, g1 / 255.0, b1 / 255.0);
+  cairo_set_alpha (gr->cr, a1 / 255.0);
+  cairo_rectangle (gr->cr, 0, 0, 1, 2);
+  cairo_fill (gr->cr);
+    
+  cairo_set_rgb_color (gr->cr, r2 / 255.0, g2 / 255.0, b2 / 255.0);
+  cairo_set_alpha (gr->cr, a2 / 255.0);
+  cairo_rectangle (gr->cr, 1, 0, 1, 2);
+  cairo_fill (gr->cr);
+
+  if (cyclic)
+    {
+      cairo_set_rgb_color (gr->cr, r1 / 255.0, g1 / 255.0, b1 / 255.0);
+      cairo_set_alpha (gr->cr, a1 / 255.0);
+      cairo_rectangle (gr->cr, 2, 0, 1, 2);
+      cairo_fill (gr->cr);
+    }
+
+  mat = cairo_matrix_create ();
+  g_assert (mat != NULL);
+
+  /* 
+     consider the vector [x2 - x1, y2 - y1] = [p,q]
+
+     this is a line in space starting at an 'origin' x1, y1.
+
+     it can also be thought of as a "transformed" unit vector in either the
+     x or y directions. we have just *drawn* our gradient as a unit vector
+     (well, a 2-3x unit vector) in the x dimension. so what we want to know
+     is which transformation turns our existing unit vector into [p,q].
+
+     which means solving for M in 
+     [p,q] = M[1,0]
+
+     [p,q] = |a b| [1,0]
+             |c d|      
+
+     [p,q] = [a,c], with b = d = 0.
+
+     what does this mean? it means that our gradient is 1-dimensional; as
+     you move through the x axis of our 2 or 3 pixel gradient from logical
+     x positions 0 to 1, the transformation of your x coordinate under the
+     matrix M causes you to accumulate both x and y values in fill
+     space. the y value of a gradient coordinate is ignored, since the
+     gradient is one dimensional. which is correct.
+
+     unfortunately we want the opposite transformation, it seems, because of
+     the way cairo is going to use this transformation. I'm a bit confused by
+     that, but it seems to work right, so we take reciprocals of values and
+     negate offsets. oh well.
+     
+   */
+
+  double a = (x2 - x1 == 0.) ? 0. : ((cyclic ? 3.0 : 2.0) / (x2 - x1));
+  double c = (y2 - y1 == 0.) ? 0. : (1. / (y2 - y1));
+  double dx = (x1 == 0.) ? 0. : 1. / x1;
+  double dy = (y1 == 0.) ? 0. : 1. / y1;
+
+  cairo_matrix_set_affine (mat,
+                          a, 0.,
+                          c, 0.,
+                          dx, dy);
+
+  cairo_surface_set_matrix (surf, mat);
+  cairo_matrix_destroy (mat);
+  cairo_surface_set_filter (surf, CAIRO_FILTER_BILINEAR);
+
+  /* FIXME: repeating gradients (not to mention hold gradients) don't seem to work. */
+  /*   cairo_surface_set_repeat (surf, cyclic ? 1 : 0); */
+
+  if (gr->pattern)
+    cairo_surface_destroy (gr->pattern);
+
+  if (gr->pattern_pixels)
+    {
+      free (gr->pattern_pixels);
+      gr->pattern_pixels = NULL;
+    }
+
+  gr->pattern = surf;  
+
+  cairo_restore (gr->cr);    
+  cairo_set_pattern (gr->cr, gr->pattern);
+
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setTexturePixels 
+  (JNIEnv *env, jobject obj, jintArray jarr, jint w, jint h, jint stride)
+{
+  struct graphics2d *gr = NULL;
+  jint *jpixels = NULL;
+
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+
+  if (gr->debug) printf ("setTexturePixels (%d pixels, %dx%d, stride: %d)\n",
+                        (*env)->GetArrayLength (env, jarr), w, h, stride);
+
+  if (gr->pattern)
+    cairo_surface_destroy (gr->pattern);
+
+  if (gr->pattern_pixels)
+    free (gr->pattern_pixels);
+
+  gr->pattern = NULL;
+  gr->pattern_pixels = NULL;
+
+  gr->pattern_pixels = (char *) malloc (h * stride * 4);
+  g_assert (gr->pattern_pixels != NULL);
+
+  jpixels = (*env)->GetIntArrayElements (env, jarr, NULL);
+  g_assert (jpixels != NULL);
+  memcpy (gr->pattern_pixels, jpixels, h * stride * 4);
+  (*env)->ReleaseIntArrayElements (env, jarr, jpixels, 0);
+
+  gr->pattern = cairo_surface_create_for_image (gr->pattern_pixels, 
+                                               CAIRO_FORMAT_ARGB32, 
+                                               w, h, stride * 4);
+  g_assert (gr->pattern != NULL);
+  cairo_surface_set_repeat (gr->pattern, 1);
+  cairo_set_pattern (gr->cr, gr->pattern);
+
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_drawPixels 
+  (JNIEnv *env, jobject obj, jintArray jarr, jint w, jint h, jint stride)
+{
+  struct graphics2d *gr = NULL;
+  jint *jpixels = NULL;
+
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+
+  if (gr->debug) printf ("drawPixels (%d pixels, %dx%d, stride: %d)\n",
+                        (*env)->GetArrayLength (env, jarr), w, h, stride);
+
+  jpixels = (*env)->GetIntArrayElements (env, jarr, NULL);
+  g_assert (jpixels != NULL);
+
+  begin_drawing_operation (gr);
+
+ {
+   cairo_surface_t *surf = cairo_surface_create_for_image ((char *)jpixels, 
+                                                          CAIRO_FORMAT_ARGB32, 
+                                                          w, h, stride * 4);   
+   cairo_surface_set_filter (surf, CAIRO_FILTER_BILINEAR);
+   cairo_show_surface (gr->cr, surf, w, h);
+   cairo_surface_destroy (surf);
+ }
+
+  end_drawing_operation (gr);
+
+  (*env)->ReleaseIntArrayElements (env, jarr, jpixels, 0);
+
+}
+
+/* passthrough methods to cairo */
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSave 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_save\n");
+  cairo_save (gr->cr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRestore 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_restore\n");
+  cairo_restore (gr->cr);
+  update_pattern_transform (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMatrix 
+   (JNIEnv *env, jobject obj,
+    jdouble m00, jdouble m10,
+    jdouble m01, jdouble m11,
+    jdouble m02, jdouble m12)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_matrix\n");
+
+  {
+    cairo_matrix_t * mat = cairo_matrix_create ();
+    cairo_matrix_set_affine (mat,
+                            m00, m10,
+                            m01, m11,
+                            m02, m12);
+    cairo_set_matrix (gr->cr, mat);
+    cairo_matrix_destroy (mat);
+  }
+  update_pattern_transform (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetOperator 
+   (JNIEnv *env, jobject obj, jint op)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_operator %d\n", op);
+  switch ((enum java_awt_alpha_composite_rule) op)
+    {
+    case java_awt_alpha_composite_CLEAR: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_CLEAR);
+      break;
+      
+    case java_awt_alpha_composite_SRC: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_SRC);
+      break;
+      
+    case java_awt_alpha_composite_SRC_OVER: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_OVER);
+      break;
+
+    case java_awt_alpha_composite_DST_OVER: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_OVER_REVERSE);
+      break;
+
+    case java_awt_alpha_composite_SRC_IN: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_IN);
+      break;
+
+    case java_awt_alpha_composite_DST_IN: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_IN_REVERSE);
+      break;
+
+    case java_awt_alpha_composite_SRC_OUT: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_OUT);
+      break;
+
+    case java_awt_alpha_composite_DST_OUT: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_OUT_REVERSE);
+      break;
+
+    case java_awt_alpha_composite_DST: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_DST);
+      break;
+
+    case java_awt_alpha_composite_SRC_ATOP: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_ATOP);
+      break;
+
+    case java_awt_alpha_composite_DST_ATOP: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_ATOP_REVERSE);
+      break;
+
+    case java_awt_alpha_composite_XOR: 
+      cairo_set_operator (gr->cr, CAIRO_OPERATOR_XOR);
+      break;
+    }
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetRGBColor 
+   (JNIEnv *env, jobject obj, jdouble r, jdouble g, jdouble b)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+
+  /* this is a very weird fact: GDK Pixbufs and RENDER drawables consider
+     colors in opposite pixel order. I have no idea why.  thus when you
+     draw to a PixBuf, you must exchange the R and B components of your
+     color. */
+
+  if (gr->debug) printf ("cairo_set_rgb_color (%f, %f, %f)\n", r, g, b);
+
+  if (gr->drawbuf)
+    cairo_set_rgb_color (gr->cr, b, g, r);
+  else
+    cairo_set_rgb_color (gr->cr, r, g, b);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetAlpha 
+   (JNIEnv *env, jobject obj, jdouble a)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_alpha %f\n", a);
+  cairo_set_alpha (gr->cr, a);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetFillRule 
+   (JNIEnv *env, jobject obj, jint rule)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  if (gr->debug) printf ("cairo_set_fill_rule %d\n", rule);
+  g_assert (gr != NULL);
+  switch ((enum java_awt_geom_path_iterator_winding_rule) rule)
+    {
+    case java_awt_geom_path_iterator_WIND_NON_ZERO:
+      cairo_set_fill_rule (gr->cr, CAIRO_FILL_RULE_WINDING);
+      break;
+    case java_awt_geom_path_iterator_WIND_EVEN_ODD:
+      cairo_set_fill_rule (gr->cr, CAIRO_FILL_RULE_EVEN_ODD);
+      break;
+    }  
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineWidth 
+   (JNIEnv *env, jobject obj, jdouble width)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_line_width %f\n", width);
+  cairo_set_line_width (gr->cr, width);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineCap 
+   (JNIEnv *env, jobject obj, jint cap)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_line_cap %d\n", cap);
+  switch ((enum java_awt_basic_stroke_cap_rule) cap)
+    {
+    case java_awt_basic_stroke_CAP_BUTT: 
+      cairo_set_line_cap (gr->cr, CAIRO_LINE_CAP_BUTT);
+      break;
+
+    case java_awt_basic_stroke_CAP_ROUND: 
+      cairo_set_line_cap (gr->cr, CAIRO_LINE_CAP_ROUND);
+      break;
+
+    case java_awt_basic_stroke_CAP_SQUARE: 
+      cairo_set_line_cap (gr->cr, CAIRO_LINE_CAP_SQUARE);
+      break;
+    }
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineJoin 
+   (JNIEnv *env, jobject obj, jint join)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_line_join %d\n", join);
+  switch ((enum java_awt_basic_stroke_join_rule) join)
+    {
+    case java_awt_basic_stroke_JOIN_MITER:
+      cairo_set_line_join (gr->cr, CAIRO_LINE_JOIN_MITER);
+      break;
+
+    case java_awt_basic_stroke_JOIN_ROUND:
+      cairo_set_line_join (gr->cr, CAIRO_LINE_JOIN_ROUND);
+      break;
+
+    case java_awt_basic_stroke_JOIN_BEVEL:
+      cairo_set_line_join (gr->cr, CAIRO_LINE_JOIN_BEVEL);
+      break;
+    }
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetDash 
+   (JNIEnv *env, jobject obj, jdoubleArray dashes, jint ndash, jdouble offset)
+{
+  struct graphics2d *gr = NULL;
+  jdouble *dasharr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_dash\n");
+  dasharr = (*env)->GetDoubleArrayElements (env, dashes, NULL);  
+  g_assert (dasharr != NULL);
+  cairo_set_dash (gr->cr, dasharr, ndash, offset);
+  (*env)->ReleaseDoubleArrayElements (env, dashes, dasharr, 0);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMiterLimit 
+   (JNIEnv *env, jobject obj, jdouble miter)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_set_miter_limit %f\n", miter);
+  cairo_set_miter_limit (gr->cr, miter);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoTranslate 
+   (JNIEnv *env, jobject obj, jdouble dx, jdouble dy)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_translate (%f, %f)\n", dx, dy);
+  cairo_translate (gr->cr, dx, dy);
+  update_pattern_transform (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoScale 
+   (JNIEnv *env, jobject obj, jdouble sx, jdouble sy)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_scale (%f, %f)\n", sx, sy);
+  cairo_scale (gr->cr, sx, sy);
+  update_pattern_transform (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRotate 
+   (JNIEnv *env, jobject obj, jdouble angle)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_rotate %f\n", angle);
+  cairo_rotate (gr->cr, angle);
+  update_pattern_transform (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoNewPath 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_new_path\n");
+  cairo_new_path (gr->cr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoMoveTo 
+   (JNIEnv *env, jobject obj, jdouble x, jdouble y)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_move_to (%f, %f)\n", x, y);
+  cairo_move_to (gr->cr, x, y);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoLineTo 
+   (JNIEnv *env, jobject obj, jdouble x, jdouble y)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_line_to (%f, %f)\n", x, y);
+  cairo_line_to (gr->cr, x, y);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoCurveTo 
+   (JNIEnv *env, jobject obj, jdouble x1, jdouble y1, jdouble x2, jdouble y2, jdouble x3, jdouble y3)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_curve_to (%f, %f), (%f, %f), (%f, %f)\n", x1, y1, x2, y2, x3, y3);
+  cairo_curve_to (gr->cr, x1, y1, x2, y2, x3, y3);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelMoveTo 
+   (JNIEnv *env, jobject obj, jdouble dx, jdouble dy)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_rel_move_to (%f, %f)\n", dx, dy);
+  cairo_rel_move_to (gr->cr, dx, dy);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelLineTo 
+   (JNIEnv *env, jobject obj, jdouble dx, jdouble dy)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_rel_line_to (%f, %f)\n", dx, dy);
+  cairo_rel_line_to (gr->cr, dx, dy);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelCurveTo 
+   (JNIEnv *env, jobject obj, jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dx3, jdouble dy3)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_rel_curve_to (%f, %f), (%f, %f), (%f, %f)\n", dx1, dy1, dx2, dy2, dx3, dy3);
+  cairo_rel_curve_to (gr->cr, dx1, dy1, dx2, dy2, dx3, dy3);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRectangle 
+   (JNIEnv *env, jobject obj, jdouble x, jdouble y, jdouble width, jdouble height)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_rectangle (%f, %f) (%f, %f)\n", x, y, width, height);
+  cairo_rectangle (gr->cr, x, y, width, height);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClosePath 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_close_path\n");
+  cairo_close_path (gr->cr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoStroke 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_stroke\n");
+  begin_drawing_operation (gr);
+  cairo_stroke (gr->cr);
+  end_drawing_operation (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoFill 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_fill\n");
+  begin_drawing_operation (gr);
+  cairo_fill (gr->cr);
+  end_drawing_operation (gr);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClip 
+   (JNIEnv *env, jobject obj)
+{
+  struct graphics2d *gr = NULL;
+  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
+  g_assert (gr != NULL);
+  if (gr->debug) printf ("cairo_clip\n");
+  cairo_clip (gr->cr);
+}
+
diff --git a/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c b/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c
new file mode 100644 (file)
index 0000000..0d75572
--- /dev/null
@@ -0,0 +1,236 @@
+/* gdkpixbufdecoder.c
+   Copyright (C) 1999, 2003 Free Software Foundation, Inc.
+
+   This file is part of GNU Classpath.
+   
+   GNU Classpath is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+   
+   GNU Classpath is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with GNU Classpath; see the file COPYING.  If not, write to the
+   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+   
+   Linking this library statically or dynamically with other modules is
+   making a combined work based on this library.  Thus, the terms and
+   conditions of the GNU General Public License cover the whole
+   combination.
+   
+   As a special exception, the copyright holders of this library give you
+   permission to link this library with independent modules to produce an
+   executable, regardless of the license terms of these independent
+   modules, and to copy and distribute the resulting executable under
+   terms of your choice, provided that you also meet, for each linked
+   independent module, the terms and conditions of the license of that
+   module.  An independent module is a module which is not derived from
+   or based on this library.  If you modify this library, you may extend
+   this exception to your version of the library, but you are not
+   obligated to do so.  If you do not wish to do so, delete this
+   exception statement from your version. */
+
+
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixbuf-loader.h>
+
+#include "gtkpeer.h"
+#include "gnu_java_awt_peer_gtk_GdkPixbufDecoder.h"
+
+struct state_table *native_pixbufdecoder_state_table;
+
+#define NSA_PB_INIT(env, clazz) \
+  native_pixbufdecoder_state_table = init_state_table (env, clazz)
+
+#define NSA_GET_PB_PTR(env, obj) \
+  get_state (env, obj, native_pixbufdecoder_state_table)
+
+#define NSA_SET_PB_PTR(env, obj, ptr) \
+  set_state (env, obj, native_pixbufdecoder_state_table, (void *)ptr)
+
+#define NSA_DEL_PB_PTR(env, obj) \
+  remove_state_slot (env, obj, native_pixbufdecoder_state_table)
+
+
+jmethodID areaPreparedID;
+jmethodID areaUpdatedID;
+
+static void
+area_prepared (GdkPixbufLoader *loader, 
+              jobject *decoder)
+{
+  jint width, height;
+
+  GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+  if (pixbuf == NULL)
+    return;
+
+  width = gdk_pixbuf_get_width (pixbuf); 
+  height = gdk_pixbuf_get_height (pixbuf), 
+
+  gdk_threads_leave ();
+
+  g_assert (decoder != NULL);
+
+  (*gdk_env)->CallVoidMethod (gdk_env,
+                             *decoder,
+                             areaPreparedID,
+                             width, height);
+
+  gdk_threads_enter ();
+}
+
+static void
+area_updated (GdkPixbufLoader *loader, 
+             gint x, gint y, 
+             gint width, gint height,
+             jobject *decoder)
+{
+  jint stride_bytes, stride_pixels, n_channels, n_pixels;
+  int i, px;
+  jintArray jpixels;  
+  jint *java_pixels;
+  guchar *gdk_pixels;
+
+  GdkPixbuf *pixbuf_no_alpha = NULL;
+  GdkPixbuf *pixbuf = NULL;
+  
+  pixbuf_no_alpha = gdk_pixbuf_loader_get_pixbuf (loader);
+  if (pixbuf_no_alpha == NULL)
+    return;
+
+  pixbuf = gdk_pixbuf_add_alpha(pixbuf_no_alpha, FALSE, 0, 0, 0);
+  g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
+  
+  stride_bytes = gdk_pixbuf_get_rowstride (pixbuf);
+  n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+  stride_pixels =  stride_bytes / n_channels;
+  n_pixels = height * stride_pixels;
+  gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+  jpixels = (*gdk_env)->NewIntArray (gdk_env, n_pixels);
+  java_pixels = (*gdk_env)->GetIntArrayElements (gdk_env, jpixels, NULL);
+
+  memcpy (java_pixels, 
+         gdk_pixels + (y * stride_bytes), 
+         (height * stride_bytes));
+
+  for (i = 0; i < n_pixels; ++i)
+    {
+      px = java_pixels[i];
+
+      /* move alpha around (GdkPixbufLoader results are AGBR not GBRA, in
+        the lsb sense) */
+      /* px = ((px >> 24) & 0xff) | ((px << 8) & 0xffffff00); */
+
+      /* it appears to require a full byte swap, now, not just a shift to
+        the A channel. why did this change? don't know. */
+      px = ((px >>  8) & 0x00ff00ff) | ((px <<  8) & 0xff00ff00); 
+      px = ((px >> 16) & 0x0000ffff) | ((px << 16) & 0xffff0000); 
+
+      java_pixels[i] = px;
+    }
+
+  g_object_unref (pixbuf);
+
+  gdk_threads_leave ();
+
+  (*gdk_env)->ReleaseIntArrayElements (gdk_env, jpixels, java_pixels, 0);
+  (*gdk_env)->CallVoidMethod (gdk_env, 
+                             *decoder, 
+                             areaUpdatedID,
+                             (jint) x, (jint) y,
+                             (jint) width, (jint) height,
+                             jpixels,
+                             stride_pixels);
+  gdk_threads_enter ();
+}
+
+static void
+closed (GdkPixbufLoader *loader, jobject *decoder)
+{
+  gdk_threads_leave ();
+  (*gdk_env)->DeleteGlobalRef (gdk_env, *decoder); 
+  free (decoder);
+  gdk_threads_enter ();
+}
+
+
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initState
+  (JNIEnv *env, jobject obj)
+{
+  GdkPixbufLoader *loader = NULL;
+  jobject *decoder = NULL;
+
+  decoder = (jobject *) malloc (sizeof (jobject));
+  g_assert (decoder != NULL);
+  *decoder = (*env)->NewGlobalRef (env, obj);
+
+  gdk_threads_enter ();
+  loader = gdk_pixbuf_loader_new ();
+  g_assert (loader != NULL);  
+  g_signal_connect (loader, "area-prepared", G_CALLBACK (area_prepared), decoder);  
+  g_signal_connect (loader, "area-updated", G_CALLBACK (area_updated), decoder);
+  g_signal_connect (loader, "closed", G_CALLBACK (closed), decoder);
+  gdk_threads_leave ();
+
+  NSA_SET_PB_PTR (env, obj, loader);
+}
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState 
+  (JNIEnv *env, jclass clazz)
+{
+  areaPreparedID = (*env)->GetMethodID (env, clazz, 
+                                       "areaPrepared", 
+                                       "(II)V");
+
+  areaUpdatedID = (*env)->GetMethodID (env, clazz,
+                                      "areaUpdated",
+                                      "(IIII[II)V");
+  NSA_PB_INIT (env, clazz);
+}
+
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish
+  (JNIEnv *env, jobject obj)
+{
+  GdkPixbufLoader *loader = NULL;
+
+  loader = (GdkPixbufLoader *)NSA_DEL_PB_PTR (env, obj);
+  if (loader == NULL)
+    return;
+
+  gdk_threads_enter ();
+  gdk_pixbuf_loader_close (loader, NULL);
+  g_object_unref (loader);
+  gdk_threads_leave (); 
+}
+
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes
+  (JNIEnv *env, jobject obj, jbyteArray jarr, jint len)
+{
+  GdkPixbufLoader *loader = NULL;
+  jbyte *bytes = NULL;
+
+  if (len < 1)
+    return;
+
+  bytes = (*gdk_env)->GetByteArrayElements (gdk_env, jarr, NULL);
+  g_assert (bytes != NULL);
+  loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj);
+  g_assert (loader != NULL);
+
+  gdk_threads_enter ();
+  gdk_pixbuf_loader_write (loader, bytes, len, NULL);
+  gdk_threads_leave ();
+
+  (*gdk_env)->ReleaseByteArrayElements (gdk_env, jarr, bytes, 0);
+}