/[classpath]/classpath/gnu/java/awt/peer/gtk/GtkImage.java
ViewVC logotype

Diff of /classpath/gnu/java/awt/peer/gtk/GtkImage.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.14 by mkoch, Wed Dec 1 16:22:44 2004 UTC revision 1.15 by smarothy, Thu Jun 2 13:18:10 2005 UTC
# Line 1  Line 1 
1  /* GtkImage.java  /* GtkImage.java
2     Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.     Copyright (C) 2005 Free Software Foundation, Inc.
3    
4  This file is part of GNU Classpath.  This file is part of GNU Classpath.
5    
# Line 39  exception statement from your version. * Line 39  exception statement from your version. *
39  package gnu.java.awt.peer.gtk;  package gnu.java.awt.peer.gtk;
40    
41  import java.awt.Graphics;  import java.awt.Graphics;
42    import java.awt.Color;
43  import java.awt.Image;  import java.awt.Image;
44  import java.awt.image.ColorModel;  import java.awt.image.ColorModel;
45    import java.awt.image.DirectColorModel;
46    import java.awt.image.MemoryImageSource;
47  import java.awt.image.ImageConsumer;  import java.awt.image.ImageConsumer;
48  import java.awt.image.ImageObserver;  import java.awt.image.ImageObserver;
49  import java.awt.image.ImageProducer;  import java.awt.image.ImageProducer;
50    import java.io.File;
51    import java.io.IOException;
52  import java.util.Hashtable;  import java.util.Hashtable;
53  import java.util.Vector;  import java.util.Vector;
54    import gnu.classpath.RawData;
55    
56  public class GtkImage extends Image implements ImageConsumer  /**
57     * GtkImage - wraps a GdkPixbuf or GdkPixmap.
58     *
59     * The constructor GtkImage(int, int) creates an 'off-screen' GdkPixmap,
60     * this can be drawn to (it's a GdkDrawable), and correspondingly, you can
61     * create a GdkGraphics object for it.
62     *
63     * This corresponds to the Image implementation returned by
64     * Component.createImage(int, int).
65     *
66     * A GdkPixbuf is 'on-screen' and the gdk cannot draw to it,
67     * this is used for the other constructors (and other createImage methods), and
68     * corresponds to the Image implementations returned by the Toolkit.createImage
69     * methods, and is basically immutable.
70     *
71     * @author Sven de Marothy
72     */
73    public class GtkImage extends Image
74  {  {
75    int width = -1, height = -1;    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 ();  
76    
77      /**
78       * Properties.
79       */
80      Hashtable props;
81    
82      /**
83       * Loaded or not flag, for asynchronous compatibility.
84       */
85      boolean isLoaded;
86    
87      /**
88       * Pointer to the GdkPixbuf
89       */
90      RawData pixmap;
91    
92      /**
93       * Observer queue.
94       */
95      Vector observers;
96    
97      /**
98       * If offScreen is set, a GdkBitmap is wrapped and not a Pixbuf.
99       */
100      boolean offScreen;
101    
102      /**
103       * Original source, if created from an ImageProducer.
104       */
105    ImageProducer source;    ImageProducer source;
   ImageObserver observer;  
   Graphics g;  
106    
107    /* Variables in which we stored cached data, if possible.    /*
108       * The 32-bit AABBGGRR format the GDK uses.
109       An image is cached if the following properties are true:     */
110       1. The ColorModel passed into setColorModel is the same ColorModel    static ColorModel nativeModel = new DirectColorModel(32,
111          passed to all invocations of setPixels.                                                         0x000000FF,
112       2. The image contains a single frame.                                                         0x0000FF00,
113                                                           0x00FF0000,
114    */                                                         0xFF000000);
115    int[] pixelCache;  
116    ColorModel model;    /**
117       * Returns a copy of the pixel data as a java array.
118    public     */
119    GtkImage (ImageProducer producer, Graphics g)    private native int[] getPixels();
120    
121      /**
122       * Sets the pixel data from a java array.
123       */
124      private native void setPixels(int[] pixels);
125    
126      /**
127       * Loads an image using gdk-pixbuf.
128       */
129      private native boolean loadPixbuf(String name);
130    
131      /**
132       * Allocates a Gtk Pixbuf or pixmap
133       */
134      private native void createPixmap();
135    
136      /**
137       * Frees the above.
138       */
139      private native void freePixmap();
140    
141      /**
142       * Sets the pixmap to scaled copy of src image. hints are rendering hints.
143       */
144      private native void createScaledPixmap(GtkImage src, int hints);
145    
146      /**
147       * Draws the image, optionally scaled and composited.
148       */
149      private native void drawPixelsScaled (GdkGraphics gc,
150                                            int bg_red, int bg_green, int bg_blue,
151                                            int x, int y, int width, int height,
152                                            boolean composite);
153    
154      /**
155       * Draws the image, optionally scaled flipped and composited.
156       */
157      private native void drawPixelsScaledFlipped (GdkGraphics gc,
158                                                   int bg_red, int bg_green,
159                                                   int bg_blue,
160                                                   boolean flipX, boolean flipY,
161                                                   int srcX, int srcY,
162                                                   int srcWidth, int srcHeight,
163                                                   int dstX, int dstY,
164                                                   int dstWidth, int dstHeight,
165                                                   boolean composite);
166    
167      /**
168       * Constructs a GtkImage from an ImageProducer. Asynchronity is handled in
169       * the following manner:
170       * A GtkImageConsumer gets the image data, and calls setImage() when
171       * completely finished. The GtkImage is not considered loaded until the
172       * GtkImageConsumer is completely finished. We go for all "all or nothing".
173       */
174      public GtkImage (ImageProducer producer)
175    {    {
176        isLoaded = false;
177        observers = new Vector();
178      source = producer;      source = producer;
179      this.g = g;      source.startProduction(new GtkImageConsumer(this, source));
180        offScreen = false;
181      }
182    
183      if (source != null)    /**
184        source.addConsumer (this);     * Constructs a GtkImage by loading a given file.
185       *
186       * @throws IllegalArgumentException if the image could not be loaded.
187       */
188      public GtkImage (String filename)
189      {
190        File f = new File(filename);
191        try
192          {
193            if (loadPixbuf(f.getCanonicalPath()) != true)
194              throw new IllegalArgumentException("Couldn't load image: "+filename);
195          }
196        catch(IOException e)
197          {
198              throw new IllegalArgumentException("Couldn't load image: "+filename);
199          }
200    
201        isLoaded = true;
202        observers = null;
203        offScreen = false;
204        props = new Hashtable();
205    }    }
206    
207    public void setObserver (ImageObserver observer)    /**
208       * Constructs an empty GtkImage.
209       */
210      public GtkImage (int width, int height)
211    {    {
212      this.observer = observer;      this.width = width;
213        this.height = height;
214        props = new Hashtable();
215        isLoaded = true;
216        observers = null;
217        offScreen = true;
218        createPixmap();
219    }    }
220    
221    public synchronized int[]    /**
222    getPixelCache ()     * Constructs a scaled version of the src bitmap, using the GDK.
223       */
224      private GtkImage (GtkImage src, int width, int height, int hints)
225    {    {
226      return pixelCache;      this.width = width;
227        this.height = height;
228        props = new Hashtable();
229        isLoaded = true;
230        observers = null;
231        offScreen = false;
232    
233        // Use the GDK scaling method.
234        createScaledPixmap(src, hints);
235    }    }
236    
237    public synchronized ColorModel    /**
238    getColorModel ()     * Callback from the image consumer.
239       */
240      public void setImage(int width, int height,
241                           int[] pixels, Hashtable properties)
242    {    {
243      return model;      this.width = width;
244        this.height = height;
245        props = (properties != null) ? properties : new Hashtable();
246        isLoaded = true;
247        deliver();
248        createPixmap();
249        setPixels(pixels);
250    }    }
251    
252    public synchronized int    // java.awt.Image methods ////////////////////////////////////////////////
253    getWidth (ImageObserver observer)  
254      public synchronized int getWidth (ImageObserver observer)
255    {    {
256      if (width == -1)      if (addObserver(observer))
257        widthObservers.addElement (observer);        return -1;
258        
259      return width;      return width;
260    }    }
261        
262    public synchronized int    public synchronized int getHeight (ImageObserver observer)
   getHeight (ImageObserver observer)  
263    {    {
264      if (height == -1)      if (addObserver(observer))
265        heightObservers.addElement (observer);        return -1;
266            
267      return height;      return height;
268    }    }
     
   public ImageProducer  
   getSource ()  
   {  
     return source;  
   }  
269    
270    public Graphics    public synchronized Object getProperty (String name, ImageObserver observer)
   getGraphics ()  
271    {    {
272      return g;      if (addObserver(observer))
273    }        return UndefinedProperty;
     
   public synchronized Object  
   getProperty (String name, ImageObserver observer)  
   {  
     if (props == null)  
       {  
         propertyObservers.addElement (observer);  
         return null;  
       }  
274            
275      Object value = props.get (name);      Object value = props.get (name);
276      return (value == null) ? UndefinedProperty : value;      return (value == null) ? UndefinedProperty : value;
277    }    }
278    
279    public synchronized void    /**
280    flush ()     * Returns the source of this image.
281    {     */
282      isLoaded = false;    public ImageProducer getSource ()
283      isCacheable = true;    {
284      width = height = -1;      return new MemoryImageSource(width, height, nativeModel, getPixels(),
285      props = null;                                   0, width);
286      pixelCache = null;    }
287      model = null;  
288      /**
289      if (source != null)     * Creates a GdkGraphics context for this pixmap.
290        {     */
291          source.removeConsumer (this);    public Graphics getGraphics ()
292          source.addConsumer (this);    {
293        }      if (!isLoaded)
294    }        return null;
295        if (offScreen)
296    public boolean        return new GdkGraphics(this);
297    isLoaded ()      else
298    {        throw new IllegalAccessError("This method only works for off-screen"
299      return isLoaded;                                     +" Images.");
300    }    }
301      
302    /* ImageConsumer methods */    /**
303       * Returns a scaled instance of this pixmap.
304    public synchronized void     */
305    setDimensions (int width, int height)    public Image getScaledInstance(int width,
306                                     int height,
307                                     int hints)
308    {    {
309      pixelCache = new int[width*height];      if (width <= 0 || height <= 0)
310          throw new IllegalArgumentException("Width and height of scaled bitmap"+
311      this.width = width;                                           "must be >= 0");
     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);  
       }  
312    
313      if (observer != null)      return new GtkImage(this, width, height, hints);
       observer.imageUpdate (this,  
                             (ImageObserver.WIDTH  
                              | ImageObserver.HEIGHT),  
                             -1, -1, width, height);  
314    }    }
315    
316    public synchronized void    /**
317    setProperties (Hashtable props)     * If the image is loaded and comes from an ImageProducer,
318       * regenerate the image from there.
319       *
320       * I have no idea if this is ever actually used. Since GtkImage can't be
321       * instantiated directly, how is the user to know if it was created from
322       * an ImageProducer or not?
323       */
324      public synchronized void flush ()
325    {    {
326      this.props = props;      if (isLoaded && source != null)
       
     for (int i = 0; i < propertyObservers.size (); i++)  
327        {        {
328          ImageObserver io = (ImageObserver) propertyObservers.elementAt (i);          observers = new Vector();
329          if (io != null)          isLoaded = false;
330            io.imageUpdate (this, ImageObserver.PROPERTIES, -1, -1, width, height);          freePixmap();
331            source.startProduction(new GtkImageConsumer(this, source));
332        }        }
333    }    }
334    
335    public synchronized void    public void finalize()
   setColorModel (ColorModel model)  
   {  
     if (this.model == null || this.model.equals(model))  
       this.model = model;  
     else  
       isCacheable = false;  
   }  
   
   public synchronized void  
   setHints (int flags)  
336    {    {
337        if (isLoaded)
338          freePixmap();
339    }    }
340    
341    public synchronized void    /**
342    setPixels (int x, int y, int width, int height, ColorModel cm, byte[] pixels,     * Returns the image status, used by GtkToolkit
343               int offset, int scansize)     */
344      public int checkImage (ImageObserver observer)
345    {    {
346      setPixels (x, y, width, height, cm, convertPixels (pixels), offset,      if (addObserver(observer))
347                 scansize);        return 0;
348    
349      if (observer != null)      return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
       observer.imageUpdate (this,  
                             ImageObserver.SOMEBITS,  
                             x, y, width, height);  
350    }    }
351    
352    public synchronized void    // Drawing methods ////////////////////////////////////////////////
   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)  
       {  
         isCacheable = false;  
         return;  
       }  
353    
354      if (scansize == width && height == 1)    /**
355        {     * Draws an image with eventual scaling/transforming.
356          // Copy contents of pixels array into pixel cache.     */
357          System.arraycopy (pixels, offset,    public boolean drawImage (GdkGraphics g, int dx1, int dy1, int dx2, int dy2,
358                            pixelCache, y * this.width + x,                              int sx1, int sy1, int sx2, int sy2,
359                            pixels.length - offset);                              Color bgcolor, ImageObserver observer)
       }  
     else                        // skip over scansize-width for each row  
       {  
         for (int i = 0; i < height; i++)  
           System.arraycopy (pixels, offset + (i * scansize),  
                             pixelCache, (y + i) * this.width + x,  
                             width);  
       }  
   }  
   
   public synchronized void  
   imageComplete (int status)  
360    {    {
361      if (status == ImageConsumer.STATICIMAGEDONE && isCacheable)      if (addObserver(observer))
362        isLoaded = true;        return false;
363    
364      if (status == ImageConsumer.SINGLEFRAME)      boolean flipX = (dx1 > dx2)^(sx1 > sx2);
365        isCacheable = false;      boolean flipY = (dy1 > dy2)^(sy1 > sy2);
366        int dstWidth = Math.abs (dx2 - dx1);
367        int dstHeight = Math.abs (dy2 - dy1);
368        int srcWidth = Math.abs (sx2 - sx1);
369        int srcHeight = Math.abs (sy2 - sy1);
370        int srcX = (sx1 < sx2) ? sx1 : sx2;
371        int srcY = (sy1 < sy2) ? sy1 : sy2;
372        int dstX = (dx1 < dx2) ? dx1 : dx2;
373        int dstY = (dy1 < dy2) ? dy1 : dy2;
374    
375      if (observer != null)      // Clipping. This requires the dst to be scaled as well,
376        if (srcWidth + srcX > width)
377        {        {
378          if (status == ImageConsumer.IMAGEERROR)          dstWidth = (int)((double)dstWidth * (double)(width - srcX)/(double)srcWidth);
379            observer.imageUpdate (null,          srcWidth = width - srcX;
380                                  ImageObserver.ERROR,      }
                                 -1, -1, -1, -1);  
         else  
           observer.imageUpdate (null,  
                                 ImageObserver.ALLBITS,  
                                 -1, -1, -1, -1);  
       }  
   
     if (source != null && status != ImageConsumer.SINGLEFRAME)  
       source.removeConsumer (this);  
   }  
381    
382    public synchronized void      if (srcHeight + srcY > height)
   startProduction (GtkImagePainter painter)  
   {  
     if (isLoaded)  
383        {        {
384          painter.setDimensions (width, height);          dstHeight = (int)((double)dstHeight * (double)(width - srcY)/(double)srcHeight);
385          painter.setPixels (0, 0, width, height, model, pixelCache, 0, width);          srcHeight = height - srcY;
386        }        }
387        
388        if(bgcolor != null)
389          drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (),
390                                   bgcolor.getBlue (),
391                                   flipX, flipY,
392                                   srcX, srcY,
393                                   srcWidth, srcHeight,
394                                   dstX,  dstY,
395                                   dstWidth, dstHeight,
396                                   true);
397      else      else
398        {        drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY,
399          if (source != null)                                 srcX, srcY, srcWidth, srcHeight,
400            {                                 dstX,  dstY, dstWidth, dstHeight,
401              source.startProduction (painter);                                 false);
402              source.removeConsumer (painter);      return true;
403            }    }
404        }  
405    }    /**
406       * Draws an image to the GdkGraphics context, at (x,y) scaled to
407    private int[]     * width and height, with optional compositing with a background color.
408    convertPixels (byte[] pixels)     */
409    {    public boolean drawImage (GdkGraphics g, int x, int y, int width, int height,
410      int ret[] = new int[pixels.length];                              Color bgcolor, ImageObserver observer)
411      {
412        if (addObserver(observer))
413          return false;
414    
415        if(bgcolor != null)
416          drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (),
417                           bgcolor.getBlue (), x, y, width, height, true);
418        else
419          drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false);
420    
421      for (int i = 0; i < pixels.length; i++)      return true;
       ret[i] = pixels[i];  
       
     return ret;  
422    }    }
423    
424    synchronized int    // Private methods ////////////////////////////////////////////////
425    checkImage ()  
426      /**
427       * Delivers notifications to all queued observers.
428       */
429      private void deliver()
430    {    {
431      int bits = 0;      int flags = ImageObserver.HEIGHT |
432          ImageObserver.WIDTH |
433          ImageObserver.PROPERTIES |
434          ImageObserver.ALLBITS;
435    
436      if (width != -1)      for(int i=0; i < observers.size(); i++)
437        bits |= ImageObserver.WIDTH;          ((ImageObserver)observers.elementAt(i)).
438      if (height != -1)            imageUpdate(this, flags, 0, 0, width, height);
       bits |= ImageObserver.HEIGHT;  
     if (props != null)  
       bits |= ImageObserver.PROPERTIES;  
     if (loading)  
       bits |= ImageObserver.SOMEBITS;  
     if (isLoaded)  
       bits |= ImageObserver.ALLBITS;  
439    
440      return bits;      observers = null;
441      }
442      
443      /**
444       * Adds an observer, if we need to.
445       * @return true if an observer was added.
446       */
447      private boolean addObserver(ImageObserver observer)
448      {
449        if (!isLoaded)
450          {
451            if(observer != null)
452              if (!observers.contains (observer))
453                observers.addElement (observer);
454            return true;
455          }
456        return false;
457    }    }
458  }  }

Legend:
Removed from v.1.14  
changed lines
  Added in v.1.15

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26