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., 51 Franklin Street, Fifth Floor, 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 ();
87 // initState() has been called, but pumpDone() has not yet been called.
88 private boolean needsClose
= false;
90 // the current set of ImageConsumers for this decoder
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
);
100 // gdk-pixbuf provids data in RGBA format
101 static final ColorModel cm
= new DirectColorModel (32, 0xff000000,
105 public GdkPixbufDecoder (InputStream in
)
110 public GdkPixbufDecoder (String filename
)
115 public GdkPixbufDecoder (URL url
)
120 public GdkPixbufDecoder (byte[] imagedata
, int imageoffset
, int imagelength
)
122 super (imagedata
, imageoffset
, imagelength
);
125 // called back by native side: area_prepared_cb
126 void areaPrepared (int width
, int height
)
132 for (int i
= 0; i
< curr
.size (); i
++)
134 ImageConsumer ic
= (ImageConsumer
) curr
.elementAt (i
);
135 ic
.setDimensions (width
, height
);
136 ic
.setColorModel (cm
);
137 ic
.setHints (ImageConsumer
.RANDOMPIXELORDER
);
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
)
148 for (int i
= 0; i
< curr
.size (); i
++)
150 ImageConsumer ic
= (ImageConsumer
) curr
.elementAt (i
);
151 ic
.setPixels (x
, y
, width
, height
, cm
, pixels
, 0, scansize
);
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.
162 public void produce (Vector v
, InputStream is
) throws IOException
166 byte bytes
[] = new byte[4096];
170 while ((len
= is
.read (bytes
)) != -1)
171 pumpBytes (bytes
, len
);
175 for (int i
= 0; i
< curr
.size (); i
++)
177 ImageConsumer ic
= (ImageConsumer
) curr
.elementAt (i
);
178 ic
.imageComplete (ImageConsumer
.STATICIMAGEDONE
);
184 public void finalize()
190 public static class ImageFormatSpec
193 public boolean writable
= false;
194 public ArrayList mimeTypes
= new ArrayList();
195 public ArrayList extensions
= new ArrayList();
197 public ImageFormatSpec(String name
, boolean writable
)
200 this.writable
= writable
;
203 public synchronized void addMimeType(String m
)
208 public synchronized void addExtension(String e
)
214 static ArrayList imageFormatSpecs
;
216 public static ImageFormatSpec
registerFormat(String name
, boolean writable
)
218 ImageFormatSpec ifs
= new ImageFormatSpec(name
, writable
);
219 synchronized(GdkPixbufDecoder
.class)
221 if (imageFormatSpecs
== null)
222 imageFormatSpecs
= new ArrayList();
223 imageFormatSpecs
.add(ifs
);
228 static String
[] getFormatNames(boolean writable
)
230 ArrayList names
= new ArrayList();
231 synchronized (imageFormatSpecs
)
233 Iterator i
= imageFormatSpecs
.iterator();
236 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
237 if (writable
&& !ifs
.writable
)
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".
247 Iterator j
= ifs
.extensions
.iterator();
249 names
.add((String
) j
.next());
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
];
259 static String
[] getFormatExtensions(boolean writable
)
261 ArrayList extensions
= new ArrayList();
262 synchronized (imageFormatSpecs
)
264 Iterator i
= imageFormatSpecs
.iterator();
267 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
268 if (writable
&& !ifs
.writable
)
270 Iterator j
= ifs
.extensions
.iterator();
272 extensions
.add((String
) j
.next());
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
];
282 static String
[] getFormatMimeTypes(boolean writable
)
284 ArrayList mimeTypes
= new ArrayList();
285 synchronized (imageFormatSpecs
)
287 Iterator i
= imageFormatSpecs
.iterator();
290 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
291 if (writable
&& !ifs
.writable
)
293 Iterator j
= ifs
.mimeTypes
.iterator();
295 mimeTypes
.add((String
) j
.next());
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
];
306 static String
findFormatName(Object ext
, boolean needWritable
)
311 if (!(ext
instanceof String
))
312 throw new IllegalArgumentException("extension is not a string");
314 String str
= (String
) ext
;
316 Iterator i
= imageFormatSpecs
.iterator();
319 ImageFormatSpec ifs
= (ImageFormatSpec
) i
.next();
321 if (needWritable
&& !ifs
.writable
)
324 if (ifs
.name
.equals(str
))
327 Iterator j
= ifs
.extensions
.iterator();
330 String extension
= (String
)j
.next();
331 if (extension
.equals(str
))
335 j
= ifs
.mimeTypes
.iterator();
338 String mimeType
= (String
)j
.next();
339 if (mimeType
.equals(str
))
343 throw new IllegalArgumentException("unknown extension '" + str
+ "'");
346 private static GdkPixbufReaderSpi readerSpi
;
347 private static GdkPixbufWriterSpi writerSpi
;
349 public static synchronized GdkPixbufReaderSpi
getReaderSpi()
351 if (readerSpi
== null)
352 readerSpi
= new GdkPixbufReaderSpi();
356 public static synchronized GdkPixbufWriterSpi
getWriterSpi()
358 if (writerSpi
== null)
359 writerSpi
= new GdkPixbufWriterSpi();
363 public static void registerSpis(IIORegistry reg
)
365 reg
.registerServiceProvider(getReaderSpi(), ImageReaderSpi
.class);
366 reg
.registerServiceProvider(getWriterSpi(), ImageWriterSpi
.class);
369 public static class GdkPixbufWriterSpi
extends ImageWriterSpi
371 public GdkPixbufWriterSpi()
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);
384 public boolean canEncodeImage(ImageTypeSpecifier ts
)
389 public ImageWriter
createWriterInstance(Object ext
)
391 return new GdkPixbufWriter(this, ext
);
394 public String
getDescription(java
.util
.Locale loc
)
396 return "GdkPixbuf Writer SPI";
401 public static class GdkPixbufReaderSpi
extends ImageReaderSpi
403 public GdkPixbufReaderSpi()
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);
416 public boolean canDecodeInput(Object obj
)
421 public ImageReader
createReaderInstance(Object ext
)
423 return new GdkPixbufReader(this, ext
);
426 public String
getDescription(Locale loc
)
428 return "GdkPixbuf Reader SPI";
432 private static class GdkPixbufWriter
436 public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi
, Object ext
)
439 this.ext
= findFormatName(ext
, true);
442 public IIOMetadata
convertImageMetadata (IIOMetadata inData
,
443 ImageTypeSpecifier imageType
,
444 ImageWriteParam param
)
449 public IIOMetadata
convertStreamMetadata (IIOMetadata inData
,
450 ImageWriteParam param
)
455 public IIOMetadata
getDefaultImageMetadata (ImageTypeSpecifier imageType
,
456 ImageWriteParam param
)
461 public IIOMetadata
getDefaultStreamMetadata (ImageWriteParam param
)
466 public void write (IIOMetadata streamMetadata
, IIOImage i
, ImageWriteParam param
)
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
);
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(),
488 model
= img
.getColorModel();
491 processImageStarted(1);
492 streamImage(pixels
, this.ext
, width
, height
, model
.hasAlpha(),
493 (DataOutput
) this.getOutput());
494 processImageComplete();
498 private static class GdkPixbufReader
500 implements ImageConsumer
502 // ImageConsumer parts
503 GdkPixbufDecoder dec
;
504 BufferedImage bufferedImage
;
505 ColorModel defaultModel
;
510 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi
, Object ext
)
513 this.ext
= findFormatName(ext
, false);
516 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi
, Object ext
, GdkPixbufDecoder d
)
522 public void setDimensions(int w
, int h
)
524 processImageStarted(1);
529 public void setProperties(Hashtable props
) {}
531 public void setColorModel(ColorModel model
)
533 defaultModel
= model
;
536 public void setHints(int flags
) {}
538 public void setPixels(int x
, int y
, int w
, int h
,
539 ColorModel model
, byte[] pixels
,
540 int offset
, int scansize
)
544 public void setPixels(int x
, int y
, int w
, int h
,
545 ColorModel model
, int[] pixels
,
546 int offset
, int scansize
)
549 model
= defaultModel
;
551 if (bufferedImage
== null)
553 bufferedImage
= new BufferedImage (width
, height
, (model
!= null && model
.hasAlpha() ?
554 BufferedImage
.TYPE_INT_ARGB
555 : BufferedImage
.TYPE_INT_RGB
));
561 pixels2
= new int[pixels
.length
];
562 for (int yy
= 0; yy
< h
; yy
++)
563 for (int xx
= 0; xx
< w
; xx
++)
565 int i
= yy
* scansize
+ xx
;
566 pixels2
[i
] = model
.getRGB (pixels
[i
]);
572 bufferedImage
.setRGB (x
, y
, w
, h
, pixels2
, offset
, scansize
);
573 processImageProgress(y
/ (height
== 0 ?
1 : height
));
576 public void imageComplete(int status
)
578 processImageComplete();
581 public BufferedImage
getBufferedImage()
583 if (bufferedImage
== null && dec
!= null)
584 dec
.startProduction (this);
585 return bufferedImage
;
590 public int getNumImages(boolean allowSearch
)
596 public IIOMetadata
getImageMetadata(int i
)
601 public IIOMetadata
getStreamMetadata()
607 public Iterator
getImageTypes(int imageIndex
)
610 BufferedImage img
= getBufferedImage();
611 Vector vec
= new Vector();
612 vec
.add(new ImageTypeSpecifier(img
));
613 return vec
.iterator();
616 public int getHeight(int imageIndex
)
619 return getBufferedImage().getHeight();
622 public int getWidth(int imageIndex
)
625 return getBufferedImage().getWidth();
628 public void setInput(Object input
,
629 boolean seekForwardOnly
,
630 boolean ignoreMetadata
)
632 super.setInput(input
, seekForwardOnly
, ignoreMetadata
);
633 dec
= new GdkPixbufDecoder((InputStream
) getInput());
636 public BufferedImage
read(int imageIndex
, ImageReadParam param
)
639 return getBufferedImage ();
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.
647 public static BufferedImage
createBufferedImage (String filename
)
649 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(),
650 "png", // reader auto-detects, doesn't matter
651 new GdkPixbufDecoder (filename
));
652 return r
.getBufferedImage ();
655 public static BufferedImage
createBufferedImage (URL u
)
657 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(),
658 "png", // reader auto-detects, doesn't matter
659 new GdkPixbufDecoder (u
));
660 return r
.getBufferedImage ();
663 public static BufferedImage
createBufferedImage (byte[] imagedata
, int imageoffset
,
666 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(),
667 "png", // reader auto-detects, doesn't matter
668 new GdkPixbufDecoder (imagedata
,
671 return r
.getBufferedImage ();
674 public static BufferedImage
createBufferedImage (ImageProducer producer
)
676 GdkPixbufReader r
= new GdkPixbufReader (getReaderSpi(), "png" /* ignored */, null);
677 producer
.startProduction(r
);
678 return r
.getBufferedImage ();