1 /* QtImage.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 package gnu.java.awt.peer.qt;
39 
40 import java.awt.Graphics;
41 import java.awt.Color;
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.io.ByteArrayOutputStream;
51 import java.io.BufferedInputStream;
52 import java.net.URL;
53 import java.util.Hashtable;
54 import java.util.WeakHashMap;
55 import java.util.Vector;
56 
57 /**
58  * QtImage - wraps a QImage
59  *
60  */
61 public class QtImage extends Image
62 {
63   int width = -1, height = -1;
64 
65   /**
66    * Properties.
67    */
68   Hashtable props;
69 
70   /**
71    * Loaded or not flag, for asynchronous compatibility.
72    */
73   boolean isLoaded;
74 
75   /**
76    * Pointer to the QImage
77    */
78   long nativeObject;
79 
80   /**
81    * Observer queue.
82    */
83   Vector observers;
84 
85   /**
86    * Error flag for loading.
87    */
88   boolean errorLoading;
89 
90   /**
91    * Original source, if created from an ImageProducer.
92    */
93   ImageProducer source;
94 
95   /*
96    * The 32-bit AARRGGBB format the  uses.
97    */
98   static ColorModel nativeModel = new DirectColorModel(32,
99                                                        0x00FF0000,
100                                                        0x0000FF00,
101                                                        0x000000FF,
102                                                        0xFF000000);
103   /**
104    * HashMap of Graphics objects painting on this Image.
105    */
106   WeakHashMap painters;
107 
108   /**
109    * Flags if this image is to be destroyed.
110    */
111   boolean killFlag;
112 
113   /**
114    * Clears the image to RGBA 0
115    */
clear()116   public native void clear();
117 
118   /**
119    * Returns a copy of the pixel data as a java array.
120    */
getPixels()121   private native int[] getPixels();
122 
123   /**
124    * Sets the pixel data from a java array.
125    */
setPixels(int[] pixels)126   private native void setPixels(int[] pixels);
127 
128   /**
129    * Loads an image
130    */
loadImage(String name)131   private native boolean loadImage(String name);
132 
133   /**
134    * Loads an image from data.
135    */
loadImageFromData(byte[] data)136   private native boolean loadImageFromData(byte[] data);
137 
138   /**
139    * Allocates a QImage
140    */
createImage()141   private native void createImage();
142 
143   /**
144    * Frees the above.
145    */
freeImage()146   private synchronized native void freeImage();
147 
148   /**
149    * Sets the image to scaled copy of src image. hints are rendering hints.
150    */
createScaledImage(QtImage src, int hints)151   private native void createScaledImage(QtImage src, int hints);
152 
153   /**
154    * Draws the image optionally composited.
155    */
drawPixels(QtGraphics gc, int bg_red, int bg_green, int bg_blue, int x, int y, boolean composite)156   native void drawPixels (QtGraphics gc,
157                           int bg_red, int bg_green, int bg_blue,
158                           int x, int y,
159                           boolean composite);
160   /**
161    * Draws the image, optionally scaled and composited.
162    */
drawPixelsScaled(QtGraphics gc, int bg_red, int bg_green, int bg_blue, int x, int y, int width, int height, boolean composite)163   private native void drawPixelsScaled (QtGraphics gc,
164                                         int bg_red, int bg_green, int bg_blue,
165                                         int x, int y, int width, int height,
166                                         boolean composite);
167 
168   /**
169    * Draws the image transformed.
170    */
drawPixelsTransformed(QtGraphics gc, QMatrix transform)171   private native void drawPixelsTransformed (QtGraphics gc, QMatrix transform);
172 
173   /**
174    * Draws the image scaled flipped and optionally composited.
175    */
drawPixelsScaledFlipped(QtGraphics 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)176   native void drawPixelsScaledFlipped (QtGraphics gc,
177                                        int bg_red, int bg_green,
178                                        int bg_blue,
179                                        boolean flipX, boolean flipY,
180                                        int srcX, int srcY,
181                                        int srcWidth, int srcHeight,
182                                        int dstX, int dstY,
183                                        int dstWidth, int dstHeight,
184                                        boolean composite);
185 
186   /**
187    * Creates the image from an ImageProducer. May result in an error image.
188    */
QtImage(ImageProducer producer)189   public QtImage (ImageProducer producer)
190   {
191     killFlag = false;
192     isLoaded = false;
193     observers = new Vector();
194     source = producer;
195     errorLoading = false;
196     if( producer != null )
197       source.startProduction(new QtImageConsumer(this, source));
198   }
199 
200   /**
201    * Creates the image from a URL. May result in an error image.
202    */
QtImage(URL url)203   public QtImage (URL url)
204   {
205     killFlag = false;
206     isLoaded = false;
207     observers = new Vector();
208     errorLoading = false;
209     if( url == null)
210       return;
211     ByteArrayOutputStream baos = new ByteArrayOutputStream( 5000 );
212     try
213       {
214         BufferedInputStream bis = new BufferedInputStream(url.openStream());
215 
216         byte[] buf = new byte[5000];
217         int n = 0;
218 
219         while ( (n = bis.read( buf )) != -1 )
220           baos.write(buf, 0, n);
221         bis.close();
222       }
223     catch(IOException e)
224       {
225         throw new IllegalArgumentException("Couldn't load image.");
226       }
227     if ( loadImageFromData( baos.toByteArray() ) != true )
228       throw new IllegalArgumentException("Couldn't load image.");
229 
230     isLoaded = true;
231     observers = null;
232     props = new Hashtable();
233   }
234 
235   /**
236    * Constructs a QtImage by loading a given file.
237    *
238    * @throws IllegalArgumentException if the image could not be loaded.
239    */
QtImage(String filename)240   public QtImage (String filename)
241   {
242     killFlag = false;
243     File f = new File(filename);
244     observers = null;
245     props = new Hashtable();
246     try
247       {
248         String fn = f.getCanonicalPath();
249         if (loadImage( fn ) != true)
250           {
251             errorLoading = true;
252             isLoaded = false;
253             return;
254           }
255       }
256     catch(IOException e)
257       {
258         errorLoading = true;
259         isLoaded = false;
260         return;
261       }
262     errorLoading = false;
263     isLoaded = true;
264   }
265 
266   /**
267    * Constructs a QtImage from a byte array of an image file.
268    *
269    * @throws IllegalArgumentException if the image could not be loaded.
270    */
QtImage(byte[] data)271   public QtImage (byte[] data)
272   {
273     if (loadImageFromData(data) != true)
274       throw new IllegalArgumentException("Couldn't load image.");
275 
276     killFlag = false;
277     isLoaded = true;
278     observers = null;
279     errorLoading = false;
280     props = new Hashtable();
281   }
282 
283   /**
284    * Constructs an empty QtImage.
285    */
QtImage(int width, int height)286   public QtImage (int width, int height)
287   {
288     this.width = width;
289     this.height = height;
290     props = new Hashtable();
291     isLoaded = true;
292     killFlag = false;
293     observers = null;
294     errorLoading = false;
295     createImage();
296     clear();
297   }
298 
299   /**
300    * Constructs a scaled version of the src bitmap, using Qt
301    */
QtImage(QtImage src, int width, int height, int hints)302   private QtImage (QtImage src, int width, int height, int hints)
303   {
304     this.width = width;
305     this.height = height;
306     props = new Hashtable();
307     isLoaded = true;
308     killFlag = false;
309     observers = null;
310     errorLoading = false;
311 
312     createScaledImage(src, hints);
313   }
314 
315   /**
316    * Callback from the image consumer.
317    */
setImage(int width, int height, int[] pixels, Hashtable properties)318   public void setImage(int width, int height,
319                        int[] pixels, Hashtable properties)
320   {
321     this.width = width;
322     this.height = height;
323     props = (properties != null) ? properties : new Hashtable();
324 
325     if (width <= 0 || height <= 0 || pixels == null)
326       {
327         errorLoading = true;
328         return;
329       }
330 
331     isLoaded = true;
332     deliver();
333     createImage();
334     setPixels(pixels);
335   }
336 
337   // java.awt.Image methods ////////////////////////////////////////////////
338 
getWidth(ImageObserver observer)339   public int getWidth (ImageObserver observer)
340   {
341     if (addObserver(observer))
342       return -1;
343 
344     return width;
345   }
346 
getHeight(ImageObserver observer)347   public int getHeight (ImageObserver observer)
348   {
349     if (addObserver(observer))
350       return -1;
351 
352     return height;
353   }
354 
getProperty(String name, ImageObserver observer)355   public Object getProperty (String name, ImageObserver observer)
356   {
357     if (addObserver(observer))
358       return UndefinedProperty;
359 
360     Object value = props.get (name);
361     return (value == null) ? UndefinedProperty : value;
362   }
363 
364   /**
365    * Returns the source of this image.
366    */
getSource()367   public ImageProducer getSource ()
368   {
369     if (!isLoaded)
370       return null;
371     return new MemoryImageSource(width, height, nativeModel, getPixels(),
372                                  0, width);
373   }
374 
putPainter(QtImageGraphics g)375   void putPainter(QtImageGraphics g)
376   {
377     if( painters == null )
378       painters = new WeakHashMap();
379     painters.put( g, "dummy" );
380   }
381 
removePainter(QtImageGraphics g)382   void removePainter(QtImageGraphics g)
383   {
384     painters.remove( g );
385     if( killFlag && painters.isEmpty() )
386       freeImage();
387   }
388 
389   /**
390    * Creates a Graphics context for this image.
391    */
getGraphics()392   public Graphics getGraphics ()
393   {
394     if (!isLoaded || killFlag)
395       return null;
396 
397     return new QtImageGraphics(this);
398   }
399 
400   /**
401    * Creates a Graphics context for this image.
402    */
getDirectGraphics(QtComponentPeer peer)403   Graphics getDirectGraphics(QtComponentPeer peer)
404   {
405     if (!isLoaded)
406       return null;
407 
408     return new QtImageDirectGraphics(this, peer);
409   }
410 
411   /**
412    * Returns a scaled instance of this image.
413    */
getScaledInstance(int width, int height, int hints)414   public Image getScaledInstance(int width,
415                                  int height,
416                                  int hints)
417   {
418     if (width <= 0 || height <= 0)
419       throw new IllegalArgumentException("Width and height of scaled bitmap"+
420                                          "must be >= 0");
421 
422     return new QtImage(this, width, height, hints);
423   }
424 
425   /**
426    * If the image is loaded and comes from an ImageProducer,
427    * regenerate the image from there.
428    *
429    * I have no idea if this is ever actually used. Since QtImage can't be
430    * instantiated directly, how is the user to know if it was created from
431    * an ImageProducer or not?
432    */
flush()433   public synchronized void flush ()
434   {
435     if (isLoaded && source != null)
436       {
437         observers = new Vector();
438         isLoaded = false;
439         freeImage();
440         source.startProduction(new QtImageConsumer(this, source));
441       }
442   }
443 
finalize()444   public void finalize()
445   {
446     dispose();
447   }
448 
dispose()449   public void dispose()
450   {
451     if (isLoaded)
452       {
453         if( painters == null || painters.isEmpty() )
454           freeImage();
455         else
456           killFlag = true; // can't destroy image yet.
457         // Do so when all painters are gone.
458       }
459   }
460 
461   /**
462    * Returns the image status, used by QtToolkit
463    */
checkImage(ImageObserver observer)464   public int checkImage (ImageObserver observer)
465   {
466     if (addObserver(observer))
467       {
468         if (errorLoading == true)
469           return ImageObserver.ERROR;
470         else
471           return 0;
472       }
473 
474     return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
475   }
476 
477   // Drawing methods ////////////////////////////////////////////////
478 
479   /**
480    * Draws an image with eventual scaling/transforming.
481    */
drawImage(QtGraphics g, QMatrix matrix, ImageObserver observer)482   public boolean drawImage (QtGraphics g, QMatrix matrix,
483                             ImageObserver observer)
484   {
485     if (addObserver(observer))
486       return false;
487 
488     drawPixelsTransformed (g, matrix);
489 
490     return true;
491   }
492 
493   /**
494    * Draws an image to the QtGraphics context, at (x,y) with optional
495    * compositing with a background color.
496    */
drawImage(QtGraphics g, int x, int y, Color bgcolor, ImageObserver observer)497   public boolean drawImage (QtGraphics g, int x, int y,
498                             Color bgcolor, ImageObserver observer)
499   {
500     if (addObserver(observer))
501       return false;
502 
503     if(bgcolor != null)
504       drawPixels(g, bgcolor.getRed (), bgcolor.getGreen (),
505                        bgcolor.getBlue (), x, y, true);
506     else
507       drawPixels(g, 0, 0, 0, x, y, false);
508 
509     return true;
510   }
511 
512   /**
513    * Draws an image to the QtGraphics context, at (x,y) scaled to
514    * width and height, with optional compositing with a background color.
515    */
drawImage(QtGraphics g, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)516   public boolean drawImage (QtGraphics g, int x, int y, int width, int height,
517                             Color bgcolor, ImageObserver observer)
518   {
519     if (addObserver(observer))
520       return false;
521 
522     if(bgcolor != null)
523       drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (),
524                        bgcolor.getBlue (), x, y, width, height, true);
525     else
526       drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false);
527 
528     return true;
529   }
530 
531   /**
532    * Draws an image with eventual scaling/transforming.
533    */
drawImage(QtGraphics g, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)534   public boolean drawImage (QtGraphics g, int dx1, int dy1, int dx2, int dy2,
535                             int sx1, int sy1, int sx2, int sy2,
536                             Color bgcolor, ImageObserver observer)
537   {
538     if (addObserver(observer))
539       return false;
540 
541     boolean flipX = (dx1 > dx2)^(sx1 > sx2);
542     boolean flipY = (dy1 > dy2)^(sy1 > sy2);
543     int dstWidth = Math.abs (dx2 - dx1);
544     int dstHeight = Math.abs (dy2 - dy1);
545     int srcWidth = Math.abs (sx2 - sx1);
546     int srcHeight = Math.abs (sy2 - sy1);
547     int srcX = (sx1 < sx2) ? sx1 : sx2;
548     int srcY = (sy1 < sy2) ? sy1 : sy2;
549     int dstX = (dx1 < dx2) ? dx1 : dx2;
550     int dstY = (dy1 < dy2) ? dy1 : dy2;
551 
552     // Clipping. This requires the dst to be scaled as well,
553     if (srcWidth > width)
554       {
555         dstWidth = (int)((double)dstWidth*((double)width/(double)srcWidth));
556         srcWidth = width - srcX;
557       }
558 
559     if (srcHeight > height)
560       {
561         dstHeight = (int)((double)dstHeight*((double)height/(double)srcHeight));
562         srcHeight = height - srcY;
563       }
564 
565     if (srcWidth + srcX > width)
566       {
567         dstWidth = (int)((double)dstWidth * (double)(width - srcX)/(double)srcWidth);
568         srcWidth = width - srcX;
569       }
570 
571     if (srcHeight + srcY > height)
572       {
573         dstHeight = (int)((double)dstHeight * (double)(width - srcY)/(double)srcHeight);
574         srcHeight = height - srcY;
575       }
576 
577     if ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0)
578       return true;
579 
580     if(bgcolor != null)
581       drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (),
582                                bgcolor.getBlue (),
583                                flipX, flipY,
584                                srcX, srcY,
585                                srcWidth, srcHeight,
586                                dstX,  dstY,
587                                dstWidth, dstHeight,
588                                true);
589     else
590       drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY,
591                                srcX, srcY, srcWidth, srcHeight,
592                                dstX,  dstY, dstWidth, dstHeight,
593                                false);
594     return true;
595   }
596 
copyArea(int x, int y, int width, int height, int dx, int dy)597   public native void copyArea(int x, int y, int width, int height,
598                               int dx, int dy);
599 
600   // Private methods ////////////////////////////////////////////////
601 
602   /**
603    * Delivers notifications to all queued observers.
604    */
deliver()605   private void deliver()
606   {
607     int flags = ImageObserver.HEIGHT |
608       ImageObserver.WIDTH |
609       ImageObserver.PROPERTIES |
610       ImageObserver.ALLBITS;
611 
612     if (observers != null)
613       for(int i=0; i < observers.size(); i++)
614         ((ImageObserver)observers.elementAt(i)).
615           imageUpdate(this, flags, 0, 0, width, height);
616 
617     observers = null;
618   }
619 
620   /**
621    * Adds an observer, if we need to.
622    * @return true if an observer was added.
623    */
addObserver(ImageObserver observer)624   private boolean addObserver(ImageObserver observer)
625   {
626     if (!isLoaded)
627       {
628         if(observer != null)
629           if (!observers.contains (observer))
630             observers.addElement (observer);
631         return true;
632       }
633     return false;
634   }
635 
toString()636   public String toString()
637   {
638     return "QtImage [isLoaded="+isLoaded+", width="+width+", height="+height
639       +"]";
640   }
641 }
642