--- /home/cpdev/src/classpath/gnu/java/awt/peer/gtk/GtkImage.java	2005-07-12 05:32:41.000000000 +0000
+++ gnu/java/awt/peer/gtk/GtkImage.java	2005-06-30 05:34:04.000000000 +0000
@@ -1,5 +1,5 @@
 /* GtkImage.java
-   Copyright (C) 2005 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -39,456 +39,306 @@
 package gnu.java.awt.peer.gtk;
 
 import java.awt.Graphics;
-import java.awt.Color;
 import java.awt.Image;
 import java.awt.image.ColorModel;
-import java.awt.image.DirectColorModel;
-import java.awt.image.MemoryImageSource;
 import java.awt.image.ImageConsumer;
 import java.awt.image.ImageObserver;
 import java.awt.image.ImageProducer;
-import java.io.File;
-import java.io.IOException;
 import java.util.Hashtable;
 import java.util.Vector;
-import gnu.classpath.RawData;
 
-/**
- * GtkImage - wraps a GdkPixbuf or GdkPixmap.
- *
- * The constructor GtkImage(int, int) creates an 'off-screen' GdkPixmap,
- * this can be drawn to (it's a GdkDrawable), and correspondingly, you can
- * create a GdkGraphics object for it. 
- *
- * This corresponds to the Image implementation returned by 
- * Component.createImage(int, int). 
- *
- * A GdkPixbuf is 'on-screen' and the gdk cannot draw to it,
- * this is used for the other constructors (and other createImage methods), and
- * corresponds to the Image implementations returned by the Toolkit.createImage
- * methods, and is basically immutable. 
- *
- * @author Sven de Marothy
- */
-public class GtkImage extends Image
+public class GtkImage extends Image implements ImageConsumer
 {
   int width = -1, height = -1;
+  Hashtable props = null;
+  boolean isLoaded = false;
+  boolean isCacheable = true;
+  boolean loading = false;
+
+  Vector widthObservers = new Vector ();
+  Vector heightObservers = new Vector ();
+  Vector propertyObservers = new Vector ();
 
-  /**
-   * Properties.
-   */
-  Hashtable props;
-
-  /**
-   * Loaded or not flag, for asynchronous compatibility.
-   */
-  boolean isLoaded;
-
-  /**
-   * Pointer to the GdkPixbuf
-   */
-  RawData pixmap;
-
-  /**
-   * Observer queue.
-   */
-  Vector observers;
-
-  /**
-   * If offScreen is set, a GdkBitmap is wrapped and not a Pixbuf.
-   */
-  boolean offScreen;
-
-  /**
-   * Error flag for loading.
-   */
-  boolean errorLoading;
-
-  /**
-   * Original source, if created from an ImageProducer.
-   */
   ImageProducer source;
+  ImageObserver observer;
+  Graphics g;
 
-  /*
-   * The 32-bit AABBGGRR format the GDK uses.
-   */
-  static ColorModel nativeModel = new DirectColorModel(32, 
-						       0x000000FF,
-						       0x0000FF00,
-						       0x00FF0000,
-						       0xFF000000);
-
-  /**
-   * Returns a copy of the pixel data as a java array.
-   */
-  private native int[] getPixels();
-
-  /**
-   * Sets the pixel data from a java array.
-   */
-  private native void setPixels(int[] pixels);
-
-  /**
-   * Loads an image using gdk-pixbuf.
-   */
-  private native boolean loadPixbuf(String name);
-
-  /**
-   * Allocates a Gtk Pixbuf or pixmap
-   */
-  private native void createPixmap();
-
-  /**
-   * Frees the above.
-   */
-  private native void freePixmap();
-
-  /**
-   * Sets the pixmap to scaled copy of src image. hints are rendering hints.
-   */
-  private native void createScaledPixmap(GtkImage src, int hints);
-
-  /**
-   * Draws the image, optionally scaled and composited.
-   */
-  private native void drawPixelsScaled (GdkGraphics gc, 
-					int bg_red, int bg_green, int bg_blue, 
-					int x, int y, int width, int height, 
-					boolean composite);
-
-  /**
-   * Draws the image, optionally scaled flipped and composited.
-   */
-  private native void drawPixelsScaledFlipped (GdkGraphics gc, 
-					       int bg_red, int bg_green, 
-					       int bg_blue, 
-					       boolean flipX, boolean flipY,
-					       int srcX, int srcY,
-					       int srcWidth, int srcHeight,
-					       int dstX, int dstY,
-					       int dstWidth, int dstHeight,
-					       boolean composite);
-
-  /**
-   * Constructs a GtkImage from an ImageProducer. Asynchronity is handled in
-   * the following manner: 
-   * A GtkImageConsumer gets the image data, and calls setImage() when 
-   * completely finished. The GtkImage is not considered loaded until the
-   * GtkImageConsumer is completely finished. We go for all "all or nothing".
-   */
-  public GtkImage (ImageProducer producer)
-  {
-    isLoaded = false;
-    observers = new Vector();
-    source = producer;
-    errorLoading = false;
-    source.startProduction(new GtkImageConsumer(this, source));
-    offScreen = false;
-  }
+  /* Variables in which we stored cached data, if possible.
+
+     An image is cached if the following properties are true:
+     1. The ColorModel passed into setColorModel is the same ColorModel
+        passed to all invocations of setPixels.
+     2. The image contains a single frame.
+
+  */
+  int[] pixelCache;
+  ColorModel model;
 
-  /**
-   * Constructs a GtkImage by loading a given file.
-   *
-   * @throws IllegalArgumentException if the image could not be loaded.
-   */
-  public GtkImage (String filename)
+  public 
+  GtkImage (ImageProducer producer, Graphics g)
   {
-    File f = new File(filename);
-    try
-      {
-	if (loadPixbuf(f.getCanonicalPath()) != true)
-	  throw new IllegalArgumentException("Couldn't load image: "+filename);
-      } 
-    catch(IOException e)
-      {
-	  throw new IllegalArgumentException("Couldn't load image: "+filename);
-      }
+    source = producer;
+    this.g = g;
 
-    isLoaded = true;
-    observers = null;
-    offScreen = false;
-    props = new Hashtable();
+    if (source != null)
+      source.addConsumer (this);
   }
 
-  /**
-   * Constructs an empty GtkImage.
-   */
-  public GtkImage (int width, int height)
+  public void setObserver (ImageObserver observer)
   {
-    this.width = width;
-    this.height = height;
-    props = new Hashtable();
-    isLoaded = true;
-    observers = null;
-    offScreen = true;
-    createPixmap();
+    this.observer = observer;
   }
 
-  /**
-   * Constructs a scaled version of the src bitmap, using the GDK.
-   */
-  private GtkImage (GtkImage src, int width, int height, int hints)
+  public synchronized int[]
+  getPixelCache ()
   {
-    this.width = width;
-    this.height = height;
-    props = new Hashtable();
-    isLoaded = true;
-    observers = null;
-    offScreen = false;
-
-    // Use the GDK scaling method.
-    createScaledPixmap(src, hints);
+    return pixelCache;
   }
 
-  /**
-   * Callback from the image consumer.
-   */
-  public void setImage(int width, int height, 
-		       int[] pixels, Hashtable properties)
+  public synchronized ColorModel
+  getColorModel ()
   {
-    this.width = width;
-    this.height = height;
-    props = (properties != null) ? properties : new Hashtable();
-
-    if (width <= 0 || height <= 0 || pixels == null)
-      {
-	errorLoading = true;
-	return;
-      }
-
-    isLoaded = true;
-    deliver();
-    createPixmap();
-    setPixels(pixels);
+    return model;
   }
 
-  // java.awt.Image methods ////////////////////////////////////////////////
-
-  public synchronized int getWidth (ImageObserver observer)
+  public synchronized int 
+  getWidth (ImageObserver observer)
   {
-    if (addObserver(observer))
-      return -1;
-
+    if (width == -1)
+      widthObservers.addElement (observer);
+    
     return width;
   }
   
-  public synchronized int getHeight (ImageObserver observer)
+  public synchronized int 
+  getHeight (ImageObserver observer)
   {
-    if (addObserver(observer))
-      return -1;
+    if (height == -1)
+      heightObservers.addElement (observer);
     
     return height;
   }
+  
+  public ImageProducer 
+  getSource ()
+  {
+    return source;
+  }
 
-  public synchronized Object getProperty (String name, ImageObserver observer)
+  public Graphics 
+  getGraphics ()
   {
-    if (addObserver(observer))
-      return UndefinedProperty;
+    return g;
+  }
+  
+  public synchronized Object 
+  getProperty (String name, ImageObserver observer)
+  {
+    if (props == null)
+      {
+	propertyObservers.addElement (observer);
+	return null;
+      }
     
     Object value = props.get (name);
     return (value == null) ? UndefinedProperty : value;
   }
 
-  /**
-   * Returns the source of this image.
-   */
-  public ImageProducer getSource ()
-  {
-    if (!isLoaded)
-      return null;
-    return new MemoryImageSource(width, height, nativeModel, getPixels(), 
-				 0, width);
-  }
-
-  /**
-   * Creates a GdkGraphics context for this pixmap.
-   */
-  public Graphics getGraphics ()
-  {
-    if (!isLoaded) 
-      return null;
-    if (offScreen)
-      return new GdkGraphics(this);
-    else
-      throw new IllegalAccessError("This method only works for off-screen"
-				   +" Images.");
-  }
-  
-  /**
-   * Returns a scaled instance of this pixmap.
-   */
-  public Image getScaledInstance(int width,
-				 int height,
-				 int hints)
+  public synchronized void 
+  flush ()
   {
-    if (width <= 0 || height <= 0)
-      throw new IllegalArgumentException("Width and height of scaled bitmap"+
-					 "must be >= 0");
+    isLoaded = false;
+    isCacheable = true;
+    width = height = -1;
+    props = null;
+    pixelCache = null;
+    model = null;
 
-    return new GtkImage(this, width, height, hints);
+    if (source != null)
+      {
+	source.removeConsumer (this);
+	source.addConsumer (this);
+      }
   }
 
-  /**
-   * If the image is loaded and comes from an ImageProducer, 
-   * regenerate the image from there.
-   *
-   * I have no idea if this is ever actually used. Since GtkImage can't be
-   * instantiated directly, how is the user to know if it was created from
-   * an ImageProducer or not?
-   */
-  public synchronized void flush ()
+  public boolean
+  isLoaded ()
   {
-    if (isLoaded && source != null)
-      {
-	observers = new Vector();
-	isLoaded = false;
-	freePixmap();
-	source.startProduction(new GtkImageConsumer(this, source));
-      }
+    return isLoaded;
   }
 
-  public void finalize()
+  /* ImageConsumer methods */
+
+  public synchronized void 
+  setDimensions (int width, int height)
   {
-    if (isLoaded)
-      freePixmap();
+    pixelCache = new int[width*height];
+
+    this.width = width;
+    this.height = height;
+    
+    for (int i = 0; i < widthObservers.size (); i++)
+      {
+	ImageObserver io = (ImageObserver) widthObservers.elementAt (i);
+	if (io != null)
+	  io.imageUpdate (this, ImageObserver.WIDTH, -1, -1, width, height);
+      }
+
+    for (int i = 0; i < heightObservers.size (); i++)
+      {
+	ImageObserver io = (ImageObserver) heightObservers.elementAt (i);
+	if (io != null)
+	  io.imageUpdate (this, ImageObserver.HEIGHT, -1, -1, width, height);
+      }
+
+    if (observer != null)
+      observer.imageUpdate (this,
+			    (ImageObserver.WIDTH
+			     | ImageObserver.HEIGHT),
+			    -1, -1, width, height);
   }
 
-  /**
-   * Returns the image status, used by GtkToolkit
-   */
-  public int checkImage (ImageObserver observer)
+  public synchronized void 
+  setProperties (Hashtable props)
   {
-    if (addObserver(observer))
+    this.props = props;
+    
+    for (int i = 0; i < propertyObservers.size (); i++)
       {
-	if (errorLoading == true)
-	  return ImageObserver.ERROR;
-	else
-	  return 0;
+	ImageObserver io = (ImageObserver) propertyObservers.elementAt (i);
+	if (io != null)
+	  io.imageUpdate (this, ImageObserver.PROPERTIES, -1, -1, width, height);
       }
+  }
 
-    return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
+  public synchronized void 
+  setColorModel (ColorModel model)
+  {
+    if (this.model == null || this.model.equals(model))
+      this.model = model;
+    else
+      isCacheable = false;
   }
 
-  // Drawing methods ////////////////////////////////////////////////
+  public synchronized void 
+  setHints (int flags)
+  {
+  }
 
-  /**
-   * Draws an image with eventual scaling/transforming.
-   */
-  public boolean drawImage (GdkGraphics g, int dx1, int dy1, int dx2, int dy2, 
-			    int sx1, int sy1, int sx2, int sy2, 
-			    Color bgcolor, ImageObserver observer)
+  public synchronized void 
+  setPixels (int x, int y, int width, int height, ColorModel cm, byte[] pixels,
+	     int offset, int scansize)
   {
-    if (addObserver(observer))
-      return false;
+    setPixels (x, y, width, height, cm, convertPixels (pixels), offset,
+               scansize);
 
-    boolean flipX = (dx1 > dx2)^(sx1 > sx2);
-    boolean flipY = (dy1 > dy2)^(sy1 > sy2);
-    int dstWidth = Math.abs (dx2 - dx1);
-    int dstHeight = Math.abs (dy2 - dy1);
-    int srcWidth = Math.abs (sx2 - sx1);
-    int srcHeight = Math.abs (sy2 - sy1);
-    int srcX = (sx1 < sx2) ? sx1 : sx2;
-    int srcY = (sy1 < sy2) ? sy1 : sy2;
-    int dstX = (dx1 < dx2) ? dx1 : dx2;
-    int dstY = (dy1 < dy2) ? dy1 : dy2;
+    if (observer != null)
+      observer.imageUpdate (this,
+			    ImageObserver.SOMEBITS,
+			    x, y, width, height);
+  }
 
-    // Clipping. This requires the dst to be scaled as well, 
-    if (srcWidth > width)
+  public synchronized void 
+  setPixels (int x, int y, int width, int height, ColorModel cm, int[] pixels,
+	     int offset, int scansize)
+  {
+    loading = true;
+
+    if (!isCacheable)
+      return;
+
+    if (!cm.equals(model) || pixelCache == null)
       {
-	dstWidth = (int)((double)dstWidth*((double)width/(double)srcWidth));
-	srcWidth = width - srcX;
+	isCacheable = false;
+	return;
       }
 
-    if (srcHeight > height) 
+    if (scansize == width && height == 1)
       {
-	dstHeight = (int)((double)dstHeight*((double)height/(double)srcHeight));
-	srcHeight = height - srcY;
+        // Copy contents of pixels array into pixel cache.
+	System.arraycopy (pixels, offset,
+			  pixelCache, y * this.width + x,
+			  pixels.length - offset);
       }
-
-    if (srcWidth + srcX > width)
+    else			// skip over scansize-width for each row
       {
-	dstWidth = (int)((double)dstWidth * (double)(width - srcX)/(double)srcWidth);
-	srcWidth = width - srcX;
+	for (int i = 0; i < height; i++)
+	  System.arraycopy (pixels, offset + (i * scansize),
+			    pixelCache, (y + i) * this.width + x,
+			    width);
       }
+  }
 
-    if (srcHeight + srcY > height)
+  public synchronized void 
+  imageComplete (int status)
+  {
+    if (status == ImageConsumer.STATICIMAGEDONE && isCacheable)
+      isLoaded = true;
+
+    if (status == ImageConsumer.SINGLEFRAME)
+      isCacheable = false;
+
+    if (observer != null)
       {
-	dstHeight = (int)((double)dstHeight * (double)(width - srcY)/(double)srcHeight);
-	srcHeight = height - srcY;
+	if (status == ImageConsumer.IMAGEERROR)
+	  observer.imageUpdate (null,
+				ImageObserver.ERROR,
+				-1, -1, -1, -1);
+	else
+	  observer.imageUpdate (null,
+				ImageObserver.ALLBITS,
+				-1, -1, -1, -1);
       }
 
-    if ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0)
-      return true;
+    if (source != null && status != ImageConsumer.SINGLEFRAME)
+      source.removeConsumer (this);
+  }
 
-    if(bgcolor != null)
-      drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (), 
-			       bgcolor.getBlue (), 
-			       flipX, flipY,
-			       srcX, srcY,
-			       srcWidth, srcHeight,
-			       dstX,  dstY,
-			       dstWidth, dstHeight,
-			       true);
-    else
-      drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY,
-			       srcX, srcY, srcWidth, srcHeight,
-			       dstX,  dstY, dstWidth, dstHeight,
-			       false);
-    return true;
-  }
-
-  /**
-   * Draws an image to the GdkGraphics context, at (x,y) scaled to 
-   * width and height, with optional compositing with a background color.
-   */
-  public boolean drawImage (GdkGraphics g, int x, int y, int width, int height,
-			    Color bgcolor, ImageObserver observer)
-  {
-    if (addObserver(observer))
-      return false;
-
-    if(bgcolor != null)
-      drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (), 
-		       bgcolor.getBlue (), x, y, width, height, true);
+  public synchronized void
+  startProduction (GtkImagePainter painter)
+  {
+    if (isLoaded)
+      {
+	painter.setDimensions (width, height);
+	painter.setPixels (0, 0, width, height, model, pixelCache, 0, width);
+      }
     else
-      drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false);
-
-    return true;
+      {
+	if (source != null)
+	  {
+	    source.startProduction (painter);
+	    source.removeConsumer (painter);
+	  }
+      }
   }
 
-  // Private methods ////////////////////////////////////////////////
+  private int[]
+  convertPixels (byte[] pixels)
+  {
+    int ret[] = new int[pixels.length];
+
+    for (int i = 0; i < pixels.length; i++)
+      ret[i] = pixels[i];
+    
+    return ret;
+  }
 
-  /**
-   * Delivers notifications to all queued observers.
-   */
-  private void deliver()
+  synchronized int 
+  checkImage ()
   {
-    int flags = ImageObserver.HEIGHT | 
-      ImageObserver.WIDTH |
-      ImageObserver.PROPERTIES |
-      ImageObserver.ALLBITS;
+    int bits = 0;
 
-    if (observers != null)
-      for(int i=0; i < observers.size(); i++)
-	((ImageObserver)observers.elementAt(i)).
-	  imageUpdate(this, flags, 0, 0, width, height);
+    if (width != -1)
+      bits |= ImageObserver.WIDTH;
+    if (height != -1)
+      bits |= ImageObserver.HEIGHT;
+    if (props != null)
+      bits |= ImageObserver.PROPERTIES;
+    if (loading)
+      bits |= ImageObserver.SOMEBITS;
+    if (isLoaded)
+      bits |= ImageObserver.ALLBITS;
 
-    observers = null;
-  }
-  
-  /**
-   * Adds an observer, if we need to.
-   * @return true if an observer was added.
-   */
-  private boolean addObserver(ImageObserver observer)
-  {
-    if (!isLoaded)
-      {
-	if(observer != null)
-	  if (!observers.contains (observer))
-	    observers.addElement (observer);
-	return true;
-      }
-    return false;
+    return bits;
   }
 }
