Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / gnu / java / awt / peer / gtk / GdkPixbufDecoder.java
blobee6c38f3c3ac50994bcf412ff43fe3c3b4826f1f
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)
9 any later version.
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
19 02111-1307 USA.
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.
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;
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;
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
75 static
77 if (Configuration.INIT_LOAD_LIBRARY)
79 System.loadLibrary("gtkpeer");
81 initStaticState ();
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
89 Vector curr;
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,
99 0x00ff0000,
100 0x0000ff00,
101 0x000000ff);
102 public GdkPixbufDecoder (InputStream in)
104 super (in);
107 public GdkPixbufDecoder (String filename)
109 super (filename);
112 public GdkPixbufDecoder (URL url)
114 super (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)
126 if (curr == null)
127 return;
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)
142 if (curr == null)
143 return;
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
161 curr = v;
163 byte bytes[] = new byte[4096];
164 int len = 0;
165 initState();
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);
175 curr = null;
178 public void finalize()
180 finish();
184 public static class ImageFormatSpec
186 public String name;
187 public boolean writable = false;
188 public ArrayList mimeTypes = new ArrayList();
189 public ArrayList extensions = new ArrayList();
191 public ImageFormatSpec(String name, boolean writable)
193 this.name = name;
194 this.writable = writable;
197 public synchronized void addMimeType(String m)
199 mimeTypes.add(m);
202 public synchronized void addExtension(String e)
204 extensions.add(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);
219 return ifs;
222 static String[] getFormatNames(boolean writable)
224 ArrayList names = new ArrayList();
225 synchronized (imageFormatSpecs)
227 Iterator i = imageFormatSpecs.iterator();
228 while (i.hasNext())
230 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
231 if (writable && !ifs.writable)
232 continue;
233 names.add(ifs.name);
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();
242 while (j.hasNext())
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];
250 return strings;
253 static String[] getFormatExtensions(boolean writable)
255 ArrayList extensions = new ArrayList();
256 synchronized (imageFormatSpecs)
258 Iterator i = imageFormatSpecs.iterator();
259 while (i.hasNext())
261 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
262 if (writable && !ifs.writable)
263 continue;
264 Iterator j = ifs.extensions.iterator();
265 while (j.hasNext())
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];
273 return strings;
276 static String[] getFormatMimeTypes(boolean writable)
278 ArrayList mimeTypes = new ArrayList();
279 synchronized (imageFormatSpecs)
281 Iterator i = imageFormatSpecs.iterator();
282 while (i.hasNext())
284 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
285 if (writable && !ifs.writable)
286 continue;
287 Iterator j = ifs.mimeTypes.iterator();
288 while (j.hasNext())
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];
296 return strings;
300 static String findFormatName(Object ext, boolean needWritable)
302 if (ext == null)
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();
311 while (i.hasNext())
313 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
315 if (needWritable && !ifs.writable)
316 continue;
318 if (ifs.name.equals(str))
319 return str;
321 Iterator j = ifs.extensions.iterator();
322 while (j.hasNext())
324 String extension = (String)j.next();
325 if (extension.equals(str))
326 return ifs.name;
329 j = ifs.mimeTypes.iterator();
330 while (j.hasNext())
332 String mimeType = (String)j.next();
333 if (mimeType.equals(str))
334 return ifs.name;
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();
347 return readerSpi;
350 public static synchronized GdkPixbufWriterSpi getWriterSpi()
352 if (writerSpi == null)
353 writerSpi = new GdkPixbufWriterSpi();
354 return writerSpi;
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)
380 return true;
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)
412 return true;
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
427 extends ImageWriter
429 String ext;
430 public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
432 super(ownerSpi);
433 this.ext = findFormatName(ext, true);
436 public IIOMetadata convertImageMetadata (IIOMetadata inData,
437 ImageTypeSpecifier imageType,
438 ImageWriteParam param)
440 return null;
443 public IIOMetadata convertStreamMetadata (IIOMetadata inData,
444 ImageWriteParam param)
446 return null;
449 public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType,
450 ImageWriteParam param)
452 return null;
455 public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
457 return null;
460 public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
461 throws IOException
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);
470 if (pixels == null)
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(),
481 img.getRaster());
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
493 extends ImageReader
494 implements ImageConsumer
496 // ImageConsumer parts
497 GdkPixbufDecoder dec;
498 BufferedImage bufferedImage;
499 ColorModel defaultModel;
500 int width;
501 int height;
502 String ext;
504 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
506 super(ownerSpi);
507 this.ext = findFormatName(ext, false);
510 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext, GdkPixbufDecoder d)
512 this(ownerSpi, ext);
513 dec = d;
516 public void setDimensions(int w, int h)
518 processImageStarted(1);
519 width = w;
520 height = h;
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)
542 if (model == null)
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));
552 int pixels2[];
553 if (model != null)
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]);
563 else
564 pixels2 = pixels;
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;
582 // ImageReader parts
584 public int getNumImages(boolean allowSearch)
585 throws IOException
587 return 1;
590 public IIOMetadata getImageMetadata(int i)
592 return null;
595 public IIOMetadata getStreamMetadata()
596 throws IOException
598 return null;
601 public Iterator getImageTypes(int imageIndex)
602 throws IOException
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)
611 throws IOException
613 return getBufferedImage().getHeight();
616 public int getWidth(int imageIndex)
617 throws IOException
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)
631 throws IOException
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,
658 int imagelength)
660 GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(),
661 "png", // reader auto-detects, doesn't matter
662 new GdkPixbufDecoder (imagedata,
663 imageoffset,
664 imagelength));
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 ();