1 /* GdkPixbufDecoder.java -- Image data decoding object
2 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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., 59 Temple Place, Suite 330, Boston, MA
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
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. */
39 package gnu
.java
.awt
.peer
.gtk
;
41 import gnu
.classpath
.Configuration
;
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
;
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
;
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
;
73 public class GdkPixbufDecoder
extends gnu
.java
.awt
.image
.ImageDecoder
77 if (Configuration
.INIT_LOAD_LIBRARY
)
79 System
.loadLibrary("gtkpeer");
84 static native void initStaticState();
85 private final int native_state
= GtkGenericPeer
.getUniqueInteger ();
86 private boolean initialized
= false;
88 // the current set of ImageConsumers for this decoder
91 // interface to GdkPixbuf
92 native void initState ();
93 native void pumpBytes (byte[] bytes
, int len
);
94 native void finish ();
95 static native void streamImage(int[] bytes
, String format
, int width
, int height
, boolean hasAlpha
, DataOutput sink
);
97 // gdk-pixbuf provids data in RGBA format
98 static final ColorModel cm
= new DirectColorModel (32, 0xff000000,
102 public GdkPixbufDecoder (InputStream in
)
107 public GdkPixbufDecoder (String filename
)
112 public GdkPixbufDecoder (URL url
)
117 public GdkPixbufDecoder (byte[] imagedata
, int imageoffset
, int imagelength
)
119 super (imagedata
, imageoffset
, imagelength
);
122 // called back by native side
123 void areaPrepared (int width
, int height
)
129 for (int i
= 0; i
< curr
.size (); i
++)
131 ImageConsumer ic
= (ImageConsumer
) curr
.elementAt (i
);
132 ic
.setDimensions (width
, height
);
133 ic
.setColorModel (cm
);
134 ic
.setHints (ImageConsumer
.RANDOMPIXELORDER
);
138 // called back by native side
139 void areaUpdated (int x
, int y
, int width
, int height
,
140 int pixels
[], int scansize
)
145 for (int i
= 0; i
< curr
.size (); i
++)
147 ImageConsumer ic
= (ImageConsumer
) curr
.elementAt (i
);
148 ic
.setPixels (x
, y
, width
, height
, cm
, pixels
, 0, scansize
);
152 // called from an async image loader of one sort or another, this method
153 // repeatedly reads bytes from the input stream and passes them through a
154 // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn
155 // decodes the image data and calls back areaPrepared and areaUpdated on
156 // this object, feeding back decoded pixel blocks, which we pass to each
157 // of the ImageConsumers in the provided Vector.
159 public void produce (Vector v
, InputStream is
) throws IOException
163 byte bytes
[] = new byte[4096];
166 while ((len
= is
.read (bytes
)) != -1)
167 pumpBytes (bytes
, len
);
169 for (int i
= 0; i
< curr
.size (); i
++)
171 ImageConsumer ic
= (ImageConsumer
) curr
.elementAt (i
);
172 ic
.imageComplete (ImageConsumer
.STATICIMAGEDONE
);
178 public void finalize()
184 public static class ImageFormatSpec
187 public boolean writable
= false;
188 public ArrayList mimeTypes
= new ArrayList();
189 public ArrayList extensions
= new ArrayList();
191 public ImageFormatSpec(String name
, boolean writable
)
194 this.writable
= writable
;
197 public synchronized void addMimeType(String m
)
202 public synchronized void addExtension(String e
)
208 static ArrayList imageFormatSpecs
;
210 public static ImageFormatSpec
registerFormat(String name
, boolean writable
)
212 ImageFormatSpec ifs
= new ImageFormatSpec(name
, writable
);
213 synchronized(GdkPixbufDecoder
.class)
215 if (imageFormatSpecs
== null)
216 imageFormatSpecs
= new ArrayList();
217 imageFormatSpecs
.add(ifs
);
222 static String
[] getFormatNames(boolean writable
)
224 ArrayList names
= new ArrayList();
225 synchronized (imageFormatSpecs
)
227 Iterator i
= imageFormatSpecs
.iterator();
230 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
231 if (writable
&& !ifs
.writable
)
236 * In order to make the filtering code work, we need to register
237 * this type under every "format name" likely to be used as a synonym.
238 * This generally means "all the extensions people might use".
241 Iterator j
= ifs
.extensions
.iterator();
243 names
.add((String
) j
.next());
246 Object
[] objs
= names
.toArray();
247 String
[] strings
= new String
[objs
.length
];
248 for (int i
= 0; i
< objs
.length
; ++i
)
249 strings
[i
] = (String
) objs
[i
];
253 static String
[] getFormatExtensions(boolean writable
)
255 ArrayList extensions
= new ArrayList();
256 synchronized (imageFormatSpecs
)
258 Iterator i
= imageFormatSpecs
.iterator();
261 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
262 if (writable
&& !ifs
.writable
)
264 Iterator j
= ifs
.extensions
.iterator();
266 extensions
.add((String
) j
.next());
269 Object
[] objs
= extensions
.toArray();
270 String
[] strings
= new String
[objs
.length
];
271 for (int i
= 0; i
< objs
.length
; ++i
)
272 strings
[i
] = (String
) objs
[i
];
276 static String
[] getFormatMimeTypes(boolean writable
)
278 ArrayList mimeTypes
= new ArrayList();
279 synchronized (imageFormatSpecs
)
281 Iterator i
= imageFormatSpecs
.iterator();
284 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
285 if (writable
&& !ifs
.writable
)
287 Iterator j
= ifs
.mimeTypes
.iterator();
289 mimeTypes
.add((String
) j
.next());
292 Object
[] objs
= mimeTypes
.toArray();
293 String
[] strings
= new String
[objs
.length
];
294 for (int i
= 0; i
< objs
.length
; ++i
)
295 strings
[i
] = (String
) objs
[i
];
300 static String
findFormatName(Object ext
, boolean needWritable
)
303 throw new IllegalArgumentException("extension is null");
305 if (!(ext
instanceof String
))
306 throw new IllegalArgumentException("extension is not a string");
308 String str
= (String
) ext
;
310 Iterator i
= imageFormatSpecs
.iterator();
313 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
315 if (needWritable
&& !ifs
.writable
)
318 if (ifs
.name
.equals(str
))
321 Iterator j
= ifs
.extensions
.iterator();
324 String extension
= (String
)j
.next();
325 if (extension
.equals(str
))
329 j
= ifs
.mimeTypes
.iterator();
332 String mimeType
= (String
)j
.next();
333 if (mimeType
.equals(str
))
337 throw new IllegalArgumentException("unknown extension '" + str
+ "'");
340 private static GdkPixbufReaderSpi readerSpi
;
341 private static GdkPixbufWriterSpi writerSpi
;
343 public static synchronized GdkPixbufReaderSpi
getReaderSpi()
345 if (readerSpi
== null)
346 readerSpi
= new GdkPixbufReaderSpi();
350 public static synchronized GdkPixbufWriterSpi
getWriterSpi()
352 if (writerSpi
== null)
353 writerSpi
= new GdkPixbufWriterSpi();
357 public static void registerSpis(IIORegistry reg
)
359 reg
.registerServiceProvider(getReaderSpi(), ImageReaderSpi
.class);
360 reg
.registerServiceProvider(getWriterSpi(), ImageWriterSpi
.class);
363 public static class GdkPixbufWriterSpi
extends ImageWriterSpi
365 public GdkPixbufWriterSpi()
367 super("GdkPixbuf", "2.x",
368 GdkPixbufDecoder
.getFormatNames(true),
369 GdkPixbufDecoder
.getFormatExtensions(true),
370 GdkPixbufDecoder
.getFormatMimeTypes(true),
371 "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
372 new Class
[] { ImageOutputStream
.class },
373 new String
[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
374 false, null, null, null, null,
375 false, null, null, null, null);
378 public boolean canEncodeImage(ImageTypeSpecifier ts
)
383 public ImageWriter
createWriterInstance(Object ext
)
385 return new GdkPixbufWriter(this, ext
);
388 public String
getDescription(java
.util
.Locale loc
)
390 return "GdkPixbuf Writer SPI";
395 public static class GdkPixbufReaderSpi
extends ImageReaderSpi
397 public GdkPixbufReaderSpi()
399 super("GdkPixbuf", "2.x",
400 GdkPixbufDecoder
.getFormatNames(false),
401 GdkPixbufDecoder
.getFormatExtensions(false),
402 GdkPixbufDecoder
.getFormatMimeTypes(false),
403 "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
404 new Class
[] { ImageInputStream
.class },
405 new String
[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
406 false, null, null, null, null,
407 false, null, null, null, null);
410 public boolean canDecodeInput(Object obj
)
415 public ImageReader
createReaderInstance(Object ext
)
417 return new GdkPixbufReader(this, ext
);
420 public String
getDescription(Locale loc
)
422 return "GdkPixbuf Reader SPI";
426 private static class GdkPixbufWriter
430 public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi
, Object ext
)
433 this.ext
= findFormatName(ext
, true);
436 public IIOMetadata
convertImageMetadata (IIOMetadata inData
,
437 ImageTypeSpecifier imageType
,
438 ImageWriteParam param
)
443 public IIOMetadata
convertStreamMetadata (IIOMetadata inData
,
444 ImageWriteParam param
)
449 public IIOMetadata
getDefaultImageMetadata (ImageTypeSpecifier imageType
,
450 ImageWriteParam param
)
455 public IIOMetadata
getDefaultStreamMetadata (ImageWriteParam param
)
460 public void write (IIOMetadata streamMetadata
, IIOImage i
, ImageWriteParam param
)
463 RenderedImage image
= i
.getRenderedImage();
464 Raster ras
= image
.getData();
465 int width
= ras
.getWidth();
466 int height
= ras
.getHeight();
467 ColorModel model
= image
.getColorModel();
468 int[] pixels
= GdkGraphics2D
.findSimpleIntegerArray (image
.getColorModel(), ras
);
472 BufferedImage img
= new BufferedImage(width
, height
,
473 (model
!= null && model
.hasAlpha() ?
474 BufferedImage
.TYPE_INT_ARGB
475 : BufferedImage
.TYPE_INT_RGB
));
476 int[] pix
= new int[4];
477 for (int y
= 0; y
< height
; ++y
)
478 for (int x
= 0; x
< width
; ++x
)
479 img
.setRGB(x
, y
, model
.getRGB(ras
.getPixel(x
, y
, pix
)));
480 pixels
= GdkGraphics2D
.findSimpleIntegerArray (img
.getColorModel(),
482 model
= img
.getColorModel();
485 processImageStarted(1);
486 streamImage(pixels
, this.ext
, width
, height
, model
.hasAlpha(),
487 (DataOutput
) this.getOutput());
488 processImageComplete();
492 private static class GdkPixbufReader
494 implements ImageConsumer
496 // ImageConsumer parts
497 GdkPixbufDecoder dec
;
498 BufferedImage bufferedImage
;
499 ColorModel defaultModel
;
504 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi
, Object ext
)
507 this.ext
= findFormatName(ext
, false);
510 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi
, Object ext
, GdkPixbufDecoder d
)
516 public void setDimensions(int w
, int h
)
518 processImageStarted(1);
523 public void setProperties(Hashtable props
) {}
525 public void setColorModel(ColorModel model
)
527 defaultModel
= model
;
530 public void setHints(int flags
) {}
532 public void setPixels(int x
, int y
, int w
, int h
,
533 ColorModel model
, byte[] pixels
,
534 int offset
, int scansize
)
538 public void setPixels(int x
, int y
, int w
, int h
,
539 ColorModel model
, int[] pixels
,
540 int offset
, int scansize
)
543 model
= defaultModel
;
545 if (bufferedImage
== null)
547 bufferedImage
= new BufferedImage (width
, height
, (model
!= null && model
.hasAlpha() ?
548 BufferedImage
.TYPE_INT_ARGB
549 : BufferedImage
.TYPE_INT_RGB
));
555 pixels2
= new int[pixels
.length
];
556 for (int yy
= 0; yy
< h
; yy
++)
557 for (int xx
= 0; xx
< w
; xx
++)
559 int i
= yy
* scansize
+ xx
;
560 pixels2
[i
] = model
.getRGB (pixels
[i
]);
566 bufferedImage
.setRGB (x
, y
, w
, h
, pixels2
, offset
, scansize
);
567 processImageProgress(y
/ (height
== 0 ?
1 : height
));
570 public void imageComplete(int status
)
572 processImageComplete();
575 public BufferedImage
getBufferedImage()
577 if (bufferedImage
== null && dec
!= null)
578 dec
.startProduction (this);
579 return bufferedImage
;
584 public int getNumImages(boolean allowSearch
)
590 public IIOMetadata
getImageMetadata(int i
)
595 public IIOMetadata
getStreamMetadata()
601 public Iterator
getImageTypes(int imageIndex
)
604 BufferedImage img
= getBufferedImage();
605 Vector vec
= new Vector();
606 vec
.add(new ImageTypeSpecifier(img
));
607 return vec
.iterator();
610 public int getHeight(int imageIndex
)
613 return getBufferedImage().getHeight();
616 public int getWidth(int imageIndex
)
619 return getBufferedImage().getWidth();
622 public void setInput(Object input
,
623 boolean seekForwardOnly
,
624 boolean ignoreMetadata
)
626 super.setInput(input
, seekForwardOnly
, ignoreMetadata
);
627 dec
= new GdkPixbufDecoder((InputStream
) getInput());
630 public BufferedImage
read(int imageIndex
, ImageReadParam param
)
633 return getBufferedImage ();
637 // remaining helper class and static method is a convenience for the Gtk
638 // peers, for loading a BufferedImage in off a disk file without going
639 // through the whole imageio system.
641 public static BufferedImage
createBufferedImage (String filename
)
643 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(),
644 "png", // reader auto-detects, doesn't matter
645 new GdkPixbufDecoder (filename
));
646 return r
.getBufferedImage ();
649 public static BufferedImage
createBufferedImage (URL u
)
651 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(),
652 "png", // reader auto-detects, doesn't matter
653 new GdkPixbufDecoder (u
));
654 return r
.getBufferedImage ();
657 public static BufferedImage
createBufferedImage (byte[] imagedata
, int imageoffset
,
660 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(),
661 "png", // reader auto-detects, doesn't matter
662 new GdkPixbufDecoder (imagedata
,
665 return r
.getBufferedImage ();
668 public static BufferedImage
createBufferedImage (ImageProducer producer
)
670 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(), "png" /* ignored */, null);
671 producer
.startProduction(r
);
672 return r
.getBufferedImage ();