Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: gnu/java/awt/peer/gtk/GdkPixbufDecoder.java


1   /* GdkPixbufDecoder.java -- Image data decoding object
2      Copyright (C) 2003, 2004, 2005  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 gnu.classpath.Configuration;
42  
43  import java.awt.image.BufferedImage;
44  import java.awt.image.ColorModel;
45  import java.awt.image.DirectColorModel;
46  import java.awt.image.ImageConsumer;
47  import java.awt.image.ImageProducer;
48  import java.awt.image.Raster;
49  import java.awt.image.RenderedImage;
50  import java.io.DataOutput;
51  import java.io.IOException;
52  import java.io.InputStream;
53  import java.net.URL;
54  import java.util.ArrayList;
55  import java.util.Hashtable;
56  import java.util.Iterator;
57  import java.util.Locale;
58  import java.util.Vector;
59  
60  import javax.imageio.IIOImage;
61  import javax.imageio.ImageReadParam;
62  import javax.imageio.ImageReader;
63  import javax.imageio.ImageTypeSpecifier;
64  import javax.imageio.ImageWriteParam;
65  import javax.imageio.ImageWriter;
66  import javax.imageio.metadata.IIOMetadata;
67  import javax.imageio.spi.IIORegistry;
68  import javax.imageio.spi.ImageReaderSpi;
69  import javax.imageio.spi.ImageWriterSpi;
70  import javax.imageio.stream.ImageInputStream;
71  import javax.imageio.stream.ImageOutputStream;
72  
73  public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
74  {
75    static 
76    {
77      if (Configuration.INIT_LOAD_LIBRARY)
78        {
79          System.loadLibrary("gtkpeer");
80        }
81      initStaticState ();
82    }
83    
84    static native void initStaticState();
85    private final int native_state = GtkGenericPeer.getUniqueInteger ();
86  
87    // initState() has been called, but pumpDone() has not yet been called.
88    private boolean needsClose = false;
89  
90    // the current set of ImageConsumers for this decoder
91    Vector curr;
92  
93    // interface to GdkPixbuf
94    native void initState ();
95    native void pumpBytes (byte[] bytes, int len) throws IOException;
96    native void pumpDone () throws IOException;
97    native void finish (boolean needsClose);
98    static native void streamImage(int[] bytes, String format, int width, int height, boolean hasAlpha, DataOutput sink);
99    
100   // gdk-pixbuf provids data in RGBA format
101   static final ColorModel cm = new DirectColorModel (32, 0xff000000, 
102                                                      0x00ff0000, 
103                                                      0x0000ff00, 
104                                                      0x000000ff);
105   public GdkPixbufDecoder (InputStream in)
106   {
107     super (in);
108   }
109 
110   public GdkPixbufDecoder (String filename)
111   {
112     super (filename);
113   }
114   
115   public GdkPixbufDecoder (URL url)
116   {
117     super (url);
118   }
119 
120   public GdkPixbufDecoder (byte[] imagedata, int imageoffset, int imagelength)
121   {
122     super (imagedata, imageoffset, imagelength);
123   }
124 
125   // called back by native side: area_prepared_cb
126   void areaPrepared (int width, int height)
127   {
128 
129     if (curr == null)
130       return;
131 
132     for (int i = 0; i < curr.size (); i++)
133       {
134         ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
135         ic.setDimensions (width, height);
136         ic.setColorModel (cm);
137         ic.setHints (ImageConsumer.RANDOMPIXELORDER);
138       }
139   }
140   
141   // called back by native side: area_updated_cb
142   void areaUpdated (int x, int y, int width, int height, 
143                     int pixels[], int scansize)
144   {
145     if (curr == null)
146       return;
147     
148     for (int i = 0; i < curr.size (); i++)
149       {
150         ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
151         ic.setPixels (x, y, width, height, cm, pixels, 0, scansize);
152       }
153   }
154   
155   // called from an async image loader of one sort or another, this method
156   // repeatedly reads bytes from the input stream and passes them through a
157   // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn
158   // decodes the image data and calls back areaPrepared and areaUpdated on
159   // this object, feeding back decoded pixel blocks, which we pass to each
160   // of the ImageConsumers in the provided Vector.
161 
162   public void produce (Vector v, InputStream is) throws IOException
163   {
164     curr = v;
165 
166     byte bytes[] = new byte[4096];
167     int len = 0;
168     initState();
169     needsClose = true;
170     while ((len = is.read (bytes)) != -1)
171       pumpBytes (bytes, len);
172     pumpDone();
173     needsClose = false;
174     
175     for (int i = 0; i < curr.size (); i++)
176       {
177         ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
178         ic.imageComplete (ImageConsumer.STATICIMAGEDONE);
179       }
180 
181     curr = null;
182   }
183 
184   public void finalize()
185   {
186     finish(needsClose);
187   }
188 
189 
190   public static class ImageFormatSpec
191   {
192     public String name;
193     public boolean writable = false;    
194     public ArrayList mimeTypes = new ArrayList();
195     public ArrayList extensions = new ArrayList();
196 
197     public ImageFormatSpec(String name, boolean writable)
198     {
199       this.name = name;
200       this.writable = writable;
201     }
202 
203     public synchronized void addMimeType(String m)
204     {
205       mimeTypes.add(m);
206     }
207 
208     public synchronized void addExtension(String e)
209     {
210       extensions.add(e);
211     }    
212   }
213 
214   static ArrayList imageFormatSpecs;
215 
216   public static ImageFormatSpec registerFormat(String name, boolean writable) 
217   {
218     ImageFormatSpec ifs = new ImageFormatSpec(name, writable);
219     synchronized(GdkPixbufDecoder.class)
220       {
221         if (imageFormatSpecs == null)
222           imageFormatSpecs = new ArrayList();
223         imageFormatSpecs.add(ifs);
224       }
225     return ifs;
226   }
227 
228   static String[] getFormatNames(boolean writable)
229   {
230     ArrayList names = new ArrayList();
231     synchronized (imageFormatSpecs) 
232       {
233         Iterator i = imageFormatSpecs.iterator();
234         while (i.hasNext())
235           {
236             ImageFormatSpec ifs = (ImageFormatSpec) i.next();
237             if (writable && !ifs.writable)
238               continue;
239             names.add(ifs.name);
240 
241             /* 
242              * In order to make the filtering code work, we need to register
243              * this type under every "format name" likely to be used as a synonym.
244              * This generally means "all the extensions people might use". 
245              */
246 
247             Iterator j = ifs.extensions.iterator();
248             while (j.hasNext())
249               names.add((String) j.next());
250           }
251       }
252     Object[] objs = names.toArray();
253     String[] strings = new String[objs.length];
254     for (int i = 0; i < objs.length; ++i)
255       strings[i] = (String) objs[i];
256     return strings;
257   }
258 
259   static String[] getFormatExtensions(boolean writable)
260   {
261     ArrayList extensions = new ArrayList();
262     synchronized (imageFormatSpecs) 
263       {
264         Iterator i = imageFormatSpecs.iterator();
265         while (i.hasNext())
266           {
267             ImageFormatSpec ifs = (ImageFormatSpec) i.next();
268             if (writable && !ifs.writable)
269               continue;
270             Iterator j = ifs.extensions.iterator();
271             while (j.hasNext())
272               extensions.add((String) j.next());
273           }
274       }
275     Object[] objs = extensions.toArray();
276     String[] strings = new String[objs.length];
277     for (int i = 0; i < objs.length; ++i)
278       strings[i] = (String) objs[i];
279     return strings;
280   }
281 
282   static String[] getFormatMimeTypes(boolean writable)
283   {
284     ArrayList mimeTypes = new ArrayList();
285     synchronized (imageFormatSpecs) 
286       {
287         Iterator i = imageFormatSpecs.iterator();
288         while (i.hasNext())
289           {
290             ImageFormatSpec ifs = (ImageFormatSpec) i.next();
291             if (writable && !ifs.writable)
292               continue;
293             Iterator j = ifs.mimeTypes.iterator();
294             while (j.hasNext())
295               mimeTypes.add((String) j.next());
296           }
297       }
298     Object[] objs = mimeTypes.toArray();
299     String[] strings = new String[objs.length];
300     for (int i = 0; i < objs.length; ++i)
301       strings[i] = (String) objs[i];
302     return strings;
303   }
304 
305   
306   static String findFormatName(Object ext, boolean needWritable)
307   {
308     if (ext == null)
309       return null;
310 
311     if (!(ext instanceof String))
312       throw new IllegalArgumentException("extension is not a string");
313 
314     String str = (String) ext;
315 
316     Iterator i = imageFormatSpecs.iterator();
317     while (i.hasNext())
318       {
319         ImageFormatSpec ifs = (ImageFormatSpec) i.next();
320 
321         if (needWritable && !ifs.writable)
322           continue;
323 
324         if (ifs.name.equals(str))
325           return str;
326 
327         Iterator j = ifs.extensions.iterator(); 
328         while (j.hasNext())
329           {
330             String extension = (String)j.next();
331             if (extension.equals(str))
332               return ifs.name;
333           }
334 
335         j = ifs.mimeTypes.iterator(); 
336         while (j.hasNext())
337           {
338             String mimeType = (String)j.next();
339             if (mimeType.equals(str))
340               return ifs.name;
341           }
342       }      
343     throw new IllegalArgumentException("unknown extension '" + str + "'");
344   }
345 
346   private static GdkPixbufReaderSpi readerSpi;
347   private static GdkPixbufWriterSpi writerSpi;
348 
349   public static synchronized GdkPixbufReaderSpi getReaderSpi()
350   {
351     if (readerSpi == null)
352       readerSpi = new GdkPixbufReaderSpi();
353     return readerSpi;
354   }
355 
356   public static synchronized GdkPixbufWriterSpi getWriterSpi()
357   {
358     if (writerSpi == null)
359       writerSpi = new GdkPixbufWriterSpi();
360     return writerSpi;
361   }
362 
363   public static void registerSpis(IIORegistry reg) 
364   {
365     reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class);
366     reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class);
367   }
368 
369   public static class GdkPixbufWriterSpi extends ImageWriterSpi
370   {
371     public GdkPixbufWriterSpi() 
372     {      
373       super("GdkPixbuf", "2.x",
374             GdkPixbufDecoder.getFormatNames(true), 
375             GdkPixbufDecoder.getFormatExtensions(true), 
376             GdkPixbufDecoder.getFormatMimeTypes(true),
377             "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
378             new Class[] { ImageOutputStream.class },
379             new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
380             false, null, null, null, null,
381             false, null, null, null, null);
382     }
383 
384     public boolean canEncodeImage(ImageTypeSpecifier ts)
385     {
386       return true;
387     }
388 
389     public ImageWriter createWriterInstance(Object ext)
390     {
391       return new GdkPixbufWriter(this, ext);
392     }
393 
394     public String getDescription(java.util.Locale loc)
395     {
396       return "GdkPixbuf Writer SPI";
397     }
398 
399   }
400 
401   public static class GdkPixbufReaderSpi extends ImageReaderSpi
402   {
403     public GdkPixbufReaderSpi() 
404     { 
405       super("GdkPixbuf", "2.x",
406             GdkPixbufDecoder.getFormatNames(false), 
407             GdkPixbufDecoder.getFormatExtensions(false), 
408             GdkPixbufDecoder.getFormatMimeTypes(false),
409             "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
410             new Class[] { ImageInputStream.class },
411             new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
412             false, null, null, null, null,
413             false, null, null, null, null);
414     }
415 
416     public boolean canDecodeInput(Object obj) 
417     { 
418       return true; 
419     }
420 
421     public ImageReader createReaderInstance(Object ext)
422     {
423       return new GdkPixbufReader(this, ext);
424     }
425 
426     public String getDescription(Locale loc)
427     {
428       return "GdkPixbuf Reader SPI";
429     }
430   }
431 
432   private static class GdkPixbufWriter
433     extends ImageWriter
434   {
435     String ext;
436     public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
437     {
438       super(ownerSpi);
439       this.ext = findFormatName(ext, true);
440     }
441 
442     public IIOMetadata convertImageMetadata (IIOMetadata inData,
443                                              ImageTypeSpecifier imageType,
444                                              ImageWriteParam param)
445     {
446       return null;
447     }
448 
449     public IIOMetadata convertStreamMetadata (IIOMetadata inData,
450                                               ImageWriteParam param)
451     {
452       return null;
453     }
454 
455     public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType, 
456                                                 ImageWriteParam param)
457     {
458       return null;
459     }
460 
461     public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
462     {
463       return null;
464     }
465 
466   public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
467     throws IOException
468     {
469       RenderedImage image = i.getRenderedImage();
470       Raster ras = image.getData();
471       int width = ras.getWidth();
472       int height = ras.getHeight();
473       ColorModel model = image.getColorModel();
474       int[] pixels = GdkGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras);
475       
476       if (pixels == null)
477         {
478           BufferedImage img = new BufferedImage(width, height, 
479                                                 (model != null && model.hasAlpha() ? 
480                                                  BufferedImage.TYPE_INT_ARGB
481                                                  : BufferedImage.TYPE_INT_RGB));
482           int[] pix = new int[4];
483           for (int y = 0; y < height; ++y)
484             for (int x = 0; x < width; ++x)
485               img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix)));
486           pixels = GdkGraphics2D.findSimpleIntegerArray (img.getColorModel(), 
487                                                          img.getRaster());
488           model = img.getColorModel();
489         }
490 
491       processImageStarted(1);
492       streamImage(pixels, this.ext, width, height, model.hasAlpha(), 
493                   (DataOutput) this.getOutput());
494       processImageComplete();
495     }    
496   }
497 
498   private static class GdkPixbufReader 
499     extends ImageReader
500     implements ImageConsumer
501   {
502     // ImageConsumer parts
503     GdkPixbufDecoder dec;
504     BufferedImage bufferedImage;
505     ColorModel defaultModel;
506     int width;
507     int height;
508     String ext;
509     
510     public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
511     {
512       super(ownerSpi);
513       this.ext = findFormatName(ext, false);
514     }
515 
516     public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext, GdkPixbufDecoder d)
517     {
518       this(ownerSpi, ext);
519       dec = d;
520     }
521 
522     public void setDimensions(int w, int h)
523     {
524       processImageStarted(1);
525       width = w;
526       height = h;
527     }
528     
529     public void setProperties(Hashtable props) {}
530 
531     public void setColorModel(ColorModel model) 
532     {
533       defaultModel = model;
534     }
535 
536     public void setHints(int flags) {}
537 
538     public void setPixels(int x, int y, int w, int h, 
539                           ColorModel model, byte[] pixels, 
540                           int offset, int scansize)
541     {
542     }      
543 
544     public void setPixels(int x, int y, int w, int h, 
545                           ColorModel model, int[] pixels, 
546                           int offset, int scansize)
547     {
548       if (model == null)
549         model = defaultModel;
550       
551       if (bufferedImage == null)
552         {
553           bufferedImage = new BufferedImage (width, height, (model != null && model.hasAlpha() ? 
554                                                              BufferedImage.TYPE_INT_ARGB
555                                                              : BufferedImage.TYPE_INT_RGB));
556         }
557 
558       int pixels2[];
559       if (model != null)
560         {
561           pixels2 = new int[pixels.length];
562           for (int yy = 0; yy < h; yy++)
563             for (int xx = 0; xx < w; xx++)
564               {
565                 int i = yy * scansize + xx;
566                 pixels2[i] = model.getRGB (pixels[i]);
567               }
568         }
569       else
570         pixels2 = pixels;
571 
572       bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
573       processImageProgress(y / (height == 0 ? 1 : height));
574     }
575 
576     public void imageComplete(int status) 
577     {
578       processImageComplete();
579     }
580 
581     public BufferedImage getBufferedImage()
582     {
583       if (bufferedImage == null && dec != null)
584         dec.startProduction (this);
585       return bufferedImage;
586     }
587 
588     // ImageReader parts
589 
590     public int getNumImages(boolean allowSearch)
591       throws IOException
592     {
593       return 1;
594     }
595 
596     public IIOMetadata getImageMetadata(int i) 
597     {
598       return null;
599     }
600 
601     public IIOMetadata getStreamMetadata()
602       throws IOException
603     {
604       return null;
605     }
606 
607     public Iterator getImageTypes(int imageIndex)
608       throws IOException
609     {
610       BufferedImage img = getBufferedImage();
611       Vector vec = new Vector();
612       vec.add(new ImageTypeSpecifier(img));
613       return vec.iterator();
614     }
615     
616     public int getHeight(int imageIndex)
617       throws IOException
618     {
619       return getBufferedImage().getHeight();
620     }
621 
622     public int getWidth(int imageIndex)
623       throws IOException
624     {
625       return getBufferedImage().getWidth();
626     }
627 
628     public void setInput(Object input,
629                          boolean seekForwardOnly,
630                          boolean ignoreMetadata)
631     {
632       super.setInput(input, seekForwardOnly, ignoreMetadata);
633       dec = new GdkPixbufDecoder((InputStream) getInput());
634     }
635 
636     public BufferedImage read(int imageIndex, ImageReadParam param)
637       throws IOException
638     {
639       return getBufferedImage ();
640     }
641   }
642 
643   // remaining helper class and static method is a convenience for the Gtk
644   // peers, for loading a BufferedImage in off a disk file without going
645   // through the whole imageio system. 
646 
647   public static BufferedImage createBufferedImage (String filename)
648   {
649     GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), 
650                                              "png", // reader auto-detects, doesn't matter
651                                              new GdkPixbufDecoder (filename));
652     return r.getBufferedImage ();
653   }
654 
655   public static BufferedImage createBufferedImage (URL u)
656   {
657     GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), 
658                                              "png", // reader auto-detects, doesn't matter
659                                              new GdkPixbufDecoder (u));
660     return r.getBufferedImage ();
661   }
662 
663   public static BufferedImage createBufferedImage (byte[] imagedata, int imageoffset,
664                                                    int imagelength)
665   {
666     GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), 
667                                              "png", // reader auto-detects, doesn't matter
668                                              new GdkPixbufDecoder (imagedata,
669                                                                    imageoffset,
670                                                                    imagelength));
671     return r.getBufferedImage ();
672   }
673   
674   public static BufferedImage createBufferedImage (ImageProducer producer)
675   {
676     GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), "png" /* ignored */, null);
677     producer.startProduction(r);
678     return r.getBufferedImage ();
679   }
680 
681 }