OSDN Git Service

libjava/classpath/ChangeLog.gcj:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / peer / gtk / GtkImage.java
1 /* GtkImage.java
2    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.java.awt.peer.gtk;
40
41 import java.awt.Graphics;
42 import java.awt.Image;
43 import java.awt.image.ColorModel;
44 import java.awt.image.DirectColorModel;
45 import java.awt.image.MemoryImageSource;
46 import java.awt.image.ImageObserver;
47 import java.awt.image.ImageProducer;
48 import java.io.File;
49 import java.io.IOException;
50 import java.util.Hashtable;
51 import java.util.Vector;
52 import java.io.ByteArrayOutputStream;
53 import java.io.BufferedInputStream;
54 import java.net.URL;
55 import gnu.classpath.Pointer;
56
57 /**
58  * GtkImage - wraps a GdkPixbuf.
59  *
60  * A GdkPixbuf is 'on-screen' and the gdk cannot draw to it,
61  * this is used for the other constructors (and other createImage methods), and
62  * corresponds to the Image implementations returned by the Toolkit.createImage
63  * methods, and is basically immutable. 
64  *
65  * @author Sven de Marothy
66  */
67 public class GtkImage extends Image
68 {
69   int width = -1, height = -1;
70
71   /**
72    * Properties.
73    */
74   Hashtable<?,?> props;
75
76   /**
77    * Loaded or not flag, for asynchronous compatibility.
78    */
79   boolean isLoaded;
80
81   /**
82    * Pointer to the GdkPixbuf - 
83    * don't change the name without changing the native code.
84    */
85   Pointer pixbuf;
86
87   /**
88    * Observer queue.
89    */
90   Vector<ImageObserver> observers;
91
92   /**
93    * Error flag for loading.
94    */
95   boolean errorLoading;
96
97   /**
98    * Original source, if created from an ImageProducer.
99    */
100   ImageProducer source;
101
102   /*
103    * The 32-bit AABBGGRR format the GDK uses.
104    */
105   static ColorModel nativeModel = new DirectColorModel(32, 
106                                                        0x000000FF,
107                                                        0x0000FF00,
108                                                        0x00FF0000,
109                                                        0xFF000000);
110
111   /**
112    * The singleton GtkImage that is returned on errors by GtkToolkit.
113    */
114   private static GtkImage errorImage;
115
116   /**
117    * Lock that should be held for all gdkpixbuf operations. We don't use
118    * the global gdk_threads_enter/leave functions in most places since
119    * most gdkpixbuf operations can be done in parallel to drawing and 
120    * manipulating gtk widgets.
121    */
122   static Object pixbufLock = new Object();
123
124   /**
125    * Allocate a PixBuf from a given ARGB32 buffer pointer.
126    */
127   private native void initFromBuffer( long bufferPointer );
128
129   /**
130    * Returns a copy of the pixel data as a java array.
131    * Should be called with the pixbufLock held.
132    */
133   native int[] getPixels();
134
135   /**
136    * Sets the pixel data from a java array.
137    * Should be called with the pixbufLock held.
138    */
139   private native void setPixels(int[] pixels);
140
141   /**
142    * Loads an image using gdk-pixbuf from a file.
143    * Should be called with the pixbufLock held.
144    */
145   private native boolean loadPixbuf(String name);
146
147   /**
148    * Loads an image using gdk-pixbuf from data.
149    * Should be called with the pixbufLock held.
150    */
151   private native boolean loadImageFromData(byte[] data);
152
153   /**
154    * Allocates a Gtk Pixbuf
155    * Should be called with the pixbufLock held.
156    */
157   private native void createPixbuf();
158
159   /**
160    * Frees the above.
161    * Should be called with the pixbufLock held.
162    */
163   private native void freePixbuf();
164
165   /**
166    * Sets the pixbuf to scaled copy of src image. hints are rendering hints.
167    * Should be called with the pixbufLock held.
168    */
169   private native void createScaledPixbuf(GtkImage src, int hints);
170
171   /**
172    * Constructs a GtkImage from an ImageProducer. Asynchronity is handled in
173    * the following manner: 
174    * A GtkImageConsumer gets the image data, and calls setImage() when 
175    * completely finished. The GtkImage is not considered loaded until the
176    * GtkImageConsumer is completely finished. We go for all "all or nothing".
177    */
178   public GtkImage (ImageProducer producer)
179   {
180     isLoaded = false;
181     observers = new Vector<ImageObserver>();
182     source = producer;
183     errorLoading = false;
184     source.startProduction(new GtkImageConsumer(this, source));
185   }
186
187   /**
188    * Constructs a blank GtkImage.  This is called when
189    * GtkToolkit.createImage (String) is called with an empty string
190    * argument ("").  A blank image is loaded immediately upon
191    * construction and has width -1 and height -1.
192    */
193   public GtkImage ()
194   {
195     isLoaded = true;
196     observers = null;
197     props = new Hashtable<String,Object>();
198     errorLoading = false;
199   }
200
201   /**
202    * Constructs a GtkImage by loading a given file.
203    *
204    * @throws IllegalArgumentException if the image could not be loaded.
205    */
206   public GtkImage (String filename)
207   {
208     File f = new File(filename);
209     try
210       {
211         String path = f.getCanonicalPath();
212         synchronized(pixbufLock)
213           {
214             if (loadPixbuf(f.getCanonicalPath()) != true)
215               throw new IllegalArgumentException("Couldn't load image: "
216                                                  + filename);
217           }
218       } 
219     catch(IOException e)
220       {
221         IllegalArgumentException iae;
222         iae = new IllegalArgumentException("Couldn't load image: "
223                                            + filename);
224         iae.initCause(e);
225         throw iae;
226       }
227
228     isLoaded = true;
229     observers = null;
230     props = new Hashtable<String,Object>();
231   }
232
233   /**
234    * Constructs a GtkImage from a byte array of an image file.
235    *
236    * @throws IllegalArgumentException if the image could not be
237    * loaded.
238    */
239   public GtkImage (byte[] data)
240   {
241     synchronized(pixbufLock)
242       {
243         if (loadImageFromData (data) != true)
244           throw new IllegalArgumentException ("Couldn't load image.");
245       }
246
247     isLoaded = true;
248     observers = null;
249     props = new Hashtable<String,Object>();
250     errorLoading = false;
251   }
252
253   /**
254    * Constructs a GtkImage from a URL. May result in an error image.
255    */
256   public GtkImage (URL url)
257   {
258     isLoaded = false;
259     observers = new Vector<ImageObserver>();
260     errorLoading = false;
261     if( url == null)
262       return;
263     ByteArrayOutputStream baos = new ByteArrayOutputStream (5000);
264     try
265       {
266         BufferedInputStream bis = new BufferedInputStream (url.openStream());
267
268         byte[] buf = new byte[5000];
269         int n = 0;
270
271         while ((n = bis.read(buf)) != -1)
272           baos.write(buf, 0, n); 
273         bis.close();
274       }
275     catch(IOException e)
276       {
277         throw new IllegalArgumentException ("Couldn't load image.");
278       }
279     byte[] array = baos.toByteArray();
280     synchronized(pixbufLock)
281       {
282         if (loadImageFromData(array) != true)
283           throw new IllegalArgumentException ("Couldn't load image.");
284       }
285
286     isLoaded = true;
287     observers = null;
288     props = new Hashtable<String,Object>();
289   }
290
291   /**
292    * Constructs a scaled version of the src bitmap, using the GDK.
293    */
294   private GtkImage (GtkImage src, int width, int height, int hints)
295   {
296     this.width = width;
297     this.height = height;
298     props = new Hashtable<String,Object>();
299     isLoaded = true;
300     observers = null;
301
302     // Use the GDK scaling method.
303     synchronized(pixbufLock)
304       {
305         createScaledPixbuf(src, hints);
306       }
307   }
308
309   /**
310    * Package private constructor to create a GtkImage from a given
311    * PixBuf pointer.
312    */
313   GtkImage (Pointer pixbuf)
314   {
315     this.pixbuf = pixbuf;
316     synchronized(pixbufLock)
317       {
318         createFromPixbuf();
319       }
320     isLoaded = true;
321     observers = null;
322     props = new Hashtable<String,Object>();
323   }
324
325   /**
326    * Wraps a buffer with a GtkImage.
327    *
328    * @param bufferPointer a pointer to an ARGB32 buffer
329    */
330   GtkImage(int width, int height, long bufferPointer)
331   {
332     this.width = width;
333     this.height = height;
334     props = new Hashtable<String,Object>();
335     isLoaded = true;
336     observers = null;
337     initFromBuffer( bufferPointer );
338   }
339
340   /**
341    * Returns an empty GtkImage with the errorLoading flag set.
342    * Called from GtkToolKit when some error occured, but an image needs
343    * to be returned anyway.
344    */
345   static synchronized GtkImage getErrorImage()
346   {
347     if (errorImage == null)
348       {
349         errorImage = new GtkImage();
350         errorImage.errorLoading = true;
351       }
352     return errorImage;
353   }
354
355   /**
356    * Native helper function for constructor that takes a pixbuf Pointer.
357    * Should be called with the pixbufLock held.
358    */
359   private native void createFromPixbuf();
360
361   /**
362    * Callback from the image consumer.
363    */
364   public void setImage(int width, int height, 
365                        int[] pixels, Hashtable<?,?> properties)
366   {
367     this.width = width;
368     this.height = height;
369     props = (properties != null) ? properties : new Hashtable<String,Object>();
370
371     if (width <= 0 || height <= 0 || pixels == null)
372       {
373         errorLoading = true;
374         return;
375       }
376
377     synchronized(pixbufLock)
378       {
379         createPixbuf();
380         setPixels(pixels);
381       }
382     isLoaded = true;
383     deliver();
384   }
385
386   // java.awt.Image methods ////////////////////////////////////////////////
387
388   public synchronized int getWidth (ImageObserver observer)
389   {
390     if (addObserver(observer))
391       return -1;
392
393     return width;
394   }
395   
396   public synchronized int getHeight (ImageObserver observer)
397   {
398     if (addObserver(observer))
399       return -1;
400     
401     return height;
402   }
403
404   public synchronized Object getProperty (String name, ImageObserver observer)
405   {
406     if (addObserver(observer))
407       return UndefinedProperty;
408     
409     Object value = props.get (name);
410     return (value == null) ? UndefinedProperty : value;
411   }
412
413   /**
414    * Returns the source of this image.
415    */
416   public ImageProducer getSource ()
417   {
418     if (!isLoaded)
419       return null;
420
421     int[] pixels;
422     synchronized (pixbufLock)
423       {
424         if (!errorLoading)
425           pixels = getPixels();
426         else
427           return null;
428       }
429     return new MemoryImageSource(width, height, nativeModel, pixels, 
430                                  0, width);
431   }
432
433   /**
434    * Does nothing. Should not be called.
435    */
436   public Graphics getGraphics ()
437   {
438     throw new IllegalAccessError("This method only works for off-screen"
439                                  +" Images.");
440   }
441   
442   /**
443    * Returns a scaled instance of this pixbuf.
444    */
445   public Image getScaledInstance(int width,
446                                  int height,
447                                  int hints)
448   {
449     if (width <= 0 || height <= 0)
450       throw new IllegalArgumentException("Width and height of scaled bitmap"
451                                          + "must be >= 0");
452
453     return new GtkImage(this, width, height, hints);
454   }
455
456   /**
457    * If the image is loaded and comes from an ImageProducer, 
458    * regenerate the image from there.
459    *
460    * I have no idea if this is ever actually used. Since GtkImage can't be
461    * instantiated directly, how is the user to know if it was created from
462    * an ImageProducer or not?
463    */
464   public synchronized void flush ()
465   {
466     if (isLoaded && source != null)
467       {
468         observers = new Vector<ImageObserver>();
469         isLoaded = false;
470         synchronized(pixbufLock)
471           {
472             freePixbuf();
473           }
474         source.startProduction(new GtkImageConsumer(this, source));
475       }
476   }
477
478   public void finalize()
479   {
480     if (isLoaded)
481       {
482         synchronized(pixbufLock)
483           {
484             freePixbuf();
485           }
486       }
487   }
488
489   /**
490    * Returns the image status, used by GtkToolkit
491    */
492   public int checkImage (ImageObserver observer)
493   {
494     if (addObserver(observer))
495       {
496         if (errorLoading == true)
497           return ImageObserver.ERROR;
498         else
499           return 0;
500       }
501
502     return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
503   }
504
505
506   // Private methods ////////////////////////////////////////////////
507
508   /**
509    * Delivers notifications to all queued observers.
510    */
511   private void deliver()
512   {
513     int flags = ImageObserver.HEIGHT | 
514       ImageObserver.WIDTH |
515       ImageObserver.PROPERTIES |
516       ImageObserver.ALLBITS;
517
518     if (observers != null)
519       for(int i=0; i < observers.size(); i++)
520         ((ImageObserver)observers.elementAt(i)).imageUpdate(this, flags, 0, 0,
521                                                             width, height);
522
523     observers = null;
524   }
525   
526   /**
527    * Adds an observer, if we need to.
528    * @return true if an observer was added.
529    */
530   private boolean addObserver(ImageObserver observer)
531   {
532     if (!isLoaded)
533       {
534         if(observer != null)
535           if (!observers.contains (observer))
536             observers.addElement (observer);
537         return true;
538       }
539     return false;
540   }
541 }