Merge from mainline.
[official-gcc.git] / libjava / classpath / gnu / java / awt / peer / gtk / GdkPixbufDecoder.java
blob2e3eee4c193c7c05eb15834ba5b76eee112978b9
1 /* GdkPixbufDecoder.java -- Image data decoding object
2 Copyright (C) 2003, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 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.DataInput;
51 import java.io.DataOutput;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.net.URL;
55 import java.util.ArrayList;
56 import java.util.Hashtable;
57 import java.util.Iterator;
58 import java.util.Locale;
59 import java.util.Vector;
61 import javax.imageio.IIOImage;
62 import javax.imageio.ImageReadParam;
63 import javax.imageio.ImageReader;
64 import javax.imageio.ImageTypeSpecifier;
65 import javax.imageio.ImageWriteParam;
66 import javax.imageio.ImageWriter;
67 import javax.imageio.metadata.IIOMetadata;
68 import javax.imageio.spi.IIORegistry;
69 import javax.imageio.spi.ImageReaderSpi;
70 import javax.imageio.spi.ImageWriterSpi;
71 import javax.imageio.stream.ImageInputStream;
72 import javax.imageio.stream.ImageOutputStream;
74 public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
76 static
78 System.loadLibrary("gtkpeer");
80 initStaticState ();
83 /**
84 * Lock that should be held for all gdkpixbuf operations. We don't use
85 * the global gdk_threads_enter/leave functions since gdkpixbuf
86 * operations can be done in parallel to drawing and manipulating gtk
87 * widgets.
89 static Object pixbufLock = new Object();
91 static native void initStaticState();
92 private final int native_state = GtkGenericPeer.getUniqueInteger ();
94 // initState() has been called, but pumpDone() has not yet been called.
95 private boolean needsClose = false;
97 // the current set of ImageConsumers for this decoder
98 Vector curr;
100 // interface to GdkPixbuf
101 // These native functions should be called with the pixbufLock held.
102 native void initState ();
103 native void pumpBytes (byte[] bytes, int len) throws IOException;
104 native void pumpDone () throws IOException;
105 native void finish (boolean needsClose);
106 static native void streamImage(int[] bytes, String format, int width, int height, boolean hasAlpha, DataOutput sink);
108 // gdk-pixbuf provids data in RGBA format
109 static final ColorModel cm = new DirectColorModel (32, 0xff000000,
110 0x00ff0000,
111 0x0000ff00,
112 0x000000ff);
113 public GdkPixbufDecoder (DataInput datainput)
115 super (datainput);
118 public GdkPixbufDecoder (InputStream in)
120 super (in);
123 public GdkPixbufDecoder (String filename)
125 super (filename);
128 public GdkPixbufDecoder (URL url)
130 super (url);
133 public GdkPixbufDecoder (byte[] imagedata, int imageoffset, int imagelength)
135 super (imagedata, imageoffset, imagelength);
138 // called back by native side: area_prepared_cb
139 void areaPrepared (int width, int height)
142 if (curr == null)
143 return;
145 for (int i = 0; i < curr.size (); i++)
147 ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
148 ic.setDimensions (width, height);
149 ic.setColorModel (cm);
150 ic.setHints (ImageConsumer.RANDOMPIXELORDER);
154 // called back by native side: area_updated_cb
155 void areaUpdated (int x, int y, int width, int height,
156 int pixels[], int scansize)
158 if (curr == null)
159 return;
161 for (int i = 0; i < curr.size (); i++)
163 ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
164 ic.setPixels (x, y, width, height, cm, pixels, 0, scansize);
168 // called from an async image loader of one sort or another, this method
169 // repeatedly reads bytes from the input stream and passes them through a
170 // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn
171 // decodes the image data and calls back areaPrepared and areaUpdated on
172 // this object, feeding back decoded pixel blocks, which we pass to each
173 // of the ImageConsumers in the provided Vector.
175 public void produce (Vector v, InputStream is) throws IOException
177 curr = v;
179 byte bytes[] = new byte[4096];
180 int len = 0;
181 synchronized(pixbufLock)
183 initState();
185 needsClose = true;
187 // Note: We don't want the pixbufLock while reading from the InputStream.
188 while ((len = is.read (bytes)) != -1)
190 synchronized(pixbufLock)
192 pumpBytes (bytes, len);
196 synchronized(pixbufLock)
198 pumpDone();
201 needsClose = false;
203 for (int i = 0; i < curr.size (); i++)
205 ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
206 ic.imageComplete (ImageConsumer.STATICIMAGEDONE);
209 curr = null;
212 public void finalize()
214 synchronized(pixbufLock)
216 finish(needsClose);
221 public static class ImageFormatSpec
223 public String name;
224 public boolean writable = false;
225 public ArrayList mimeTypes = new ArrayList();
226 public ArrayList extensions = new ArrayList();
228 public ImageFormatSpec(String name, boolean writable)
230 this.name = name;
231 this.writable = writable;
234 public synchronized void addMimeType(String m)
236 mimeTypes.add(m);
239 public synchronized void addExtension(String e)
241 extensions.add(e);
245 static ArrayList imageFormatSpecs;
247 public static ImageFormatSpec registerFormat(String name, boolean writable)
249 ImageFormatSpec ifs = new ImageFormatSpec(name, writable);
250 synchronized(GdkPixbufDecoder.class)
252 if (imageFormatSpecs == null)
253 imageFormatSpecs = new ArrayList();
254 imageFormatSpecs.add(ifs);
256 return ifs;
259 static String[] getFormatNames(boolean writable)
261 ArrayList names = new ArrayList();
262 synchronized (imageFormatSpecs)
264 Iterator i = imageFormatSpecs.iterator();
265 while (i.hasNext())
267 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
268 if (writable && !ifs.writable)
269 continue;
270 names.add(ifs.name);
273 * In order to make the filtering code work, we need to register
274 * this type under every "format name" likely to be used as a synonym.
275 * This generally means "all the extensions people might use".
278 Iterator j = ifs.extensions.iterator();
279 while (j.hasNext())
280 names.add((String) j.next());
283 Object[] objs = names.toArray();
284 String[] strings = new String[objs.length];
285 for (int i = 0; i < objs.length; ++i)
286 strings[i] = (String) objs[i];
287 return strings;
290 static String[] getFormatExtensions(boolean writable)
292 ArrayList extensions = new ArrayList();
293 synchronized (imageFormatSpecs)
295 Iterator i = imageFormatSpecs.iterator();
296 while (i.hasNext())
298 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
299 if (writable && !ifs.writable)
300 continue;
301 Iterator j = ifs.extensions.iterator();
302 while (j.hasNext())
303 extensions.add((String) j.next());
306 Object[] objs = extensions.toArray();
307 String[] strings = new String[objs.length];
308 for (int i = 0; i < objs.length; ++i)
309 strings[i] = (String) objs[i];
310 return strings;
313 static String[] getFormatMimeTypes(boolean writable)
315 ArrayList mimeTypes = new ArrayList();
316 synchronized (imageFormatSpecs)
318 Iterator i = imageFormatSpecs.iterator();
319 while (i.hasNext())
321 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
322 if (writable && !ifs.writable)
323 continue;
324 Iterator j = ifs.mimeTypes.iterator();
325 while (j.hasNext())
326 mimeTypes.add((String) j.next());
329 Object[] objs = mimeTypes.toArray();
330 String[] strings = new String[objs.length];
331 for (int i = 0; i < objs.length; ++i)
332 strings[i] = (String) objs[i];
333 return strings;
337 static String findFormatName(Object ext, boolean needWritable)
339 if (ext == null)
340 return null;
342 if (!(ext instanceof String))
343 throw new IllegalArgumentException("extension is not a string");
345 String str = (String) ext;
347 Iterator i = imageFormatSpecs.iterator();
348 while (i.hasNext())
350 ImageFormatSpec ifs = (ImageFormatSpec) i.next();
352 if (needWritable && !ifs.writable)
353 continue;
355 if (ifs.name.equals(str))
356 return str;
358 Iterator j = ifs.extensions.iterator();
359 while (j.hasNext())
361 String extension = (String)j.next();
362 if (extension.equals(str))
363 return ifs.name;
366 j = ifs.mimeTypes.iterator();
367 while (j.hasNext())
369 String mimeType = (String)j.next();
370 if (mimeType.equals(str))
371 return ifs.name;
374 throw new IllegalArgumentException("unknown extension '" + str + "'");
377 private static GdkPixbufReaderSpi readerSpi;
378 private static GdkPixbufWriterSpi writerSpi;
380 public static synchronized GdkPixbufReaderSpi getReaderSpi()
382 if (readerSpi == null)
383 readerSpi = new GdkPixbufReaderSpi();
384 return readerSpi;
387 public static synchronized GdkPixbufWriterSpi getWriterSpi()
389 if (writerSpi == null)
390 writerSpi = new GdkPixbufWriterSpi();
391 return writerSpi;
394 public static void registerSpis(IIORegistry reg)
396 reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class);
397 reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class);
400 public static class GdkPixbufWriterSpi extends ImageWriterSpi
402 public GdkPixbufWriterSpi()
404 super("GdkPixbuf", "2.x",
405 GdkPixbufDecoder.getFormatNames(true),
406 GdkPixbufDecoder.getFormatExtensions(true),
407 GdkPixbufDecoder.getFormatMimeTypes(true),
408 "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
409 new Class[] { ImageOutputStream.class },
410 new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
411 false, null, null, null, null,
412 false, null, null, null, null);
415 public boolean canEncodeImage(ImageTypeSpecifier ts)
417 return true;
420 public ImageWriter createWriterInstance(Object ext)
422 return new GdkPixbufWriter(this, ext);
425 public String getDescription(java.util.Locale loc)
427 return "GdkPixbuf Writer SPI";
432 public static class GdkPixbufReaderSpi extends ImageReaderSpi
434 public GdkPixbufReaderSpi()
436 super("GdkPixbuf", "2.x",
437 GdkPixbufDecoder.getFormatNames(false),
438 GdkPixbufDecoder.getFormatExtensions(false),
439 GdkPixbufDecoder.getFormatMimeTypes(false),
440 "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
441 new Class[] { ImageInputStream.class },
442 new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
443 false, null, null, null, null,
444 false, null, null, null, null);
447 public boolean canDecodeInput(Object obj)
449 return true;
452 public ImageReader createReaderInstance(Object ext)
454 return new GdkPixbufReader(this, ext);
457 public String getDescription(Locale loc)
459 return "GdkPixbuf Reader SPI";
463 private static class GdkPixbufWriter
464 extends ImageWriter
466 String ext;
467 public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
469 super(ownerSpi);
470 this.ext = findFormatName(ext, true);
473 public IIOMetadata convertImageMetadata (IIOMetadata inData,
474 ImageTypeSpecifier imageType,
475 ImageWriteParam param)
477 return null;
480 public IIOMetadata convertStreamMetadata (IIOMetadata inData,
481 ImageWriteParam param)
483 return null;
486 public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType,
487 ImageWriteParam param)
489 return null;
492 public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
494 return null;
497 public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
498 throws IOException
500 RenderedImage image = i.getRenderedImage();
501 Raster ras = image.getData();
502 int width = ras.getWidth();
503 int height = ras.getHeight();
504 ColorModel model = image.getColorModel();
505 int[] pixels = GdkGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras);
507 if (pixels == null)
509 BufferedImage img = new BufferedImage(width, height,
510 (model != null && model.hasAlpha() ?
511 BufferedImage.TYPE_INT_ARGB
512 : BufferedImage.TYPE_INT_RGB));
513 int[] pix = new int[4];
514 for (int y = 0; y < height; ++y)
515 for (int x = 0; x < width; ++x)
516 img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix)));
517 pixels = GdkGraphics2D.findSimpleIntegerArray (img.getColorModel(),
518 img.getRaster());
519 model = img.getColorModel();
522 processImageStarted(1);
523 synchronized(pixbufLock)
525 streamImage(pixels, this.ext, width, height, model.hasAlpha(),
526 (DataOutput) this.getOutput());
528 processImageComplete();
532 private static class GdkPixbufReader
533 extends ImageReader
534 implements ImageConsumer
536 // ImageConsumer parts
537 GdkPixbufDecoder dec;
538 BufferedImage bufferedImage;
539 ColorModel defaultModel;
540 int width;
541 int height;
542 String ext;
544 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
546 super(ownerSpi);
547 this.ext = findFormatName(ext, false);
550 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext, GdkPixbufDecoder d)
552 this(ownerSpi, ext);
553 dec = d;
556 public void setDimensions(int w, int h)
558 processImageStarted(1);
559 width = w;
560 height = h;
563 public void setProperties(Hashtable props) {}
565 public void setColorModel(ColorModel model)
567 defaultModel = model;
570 public void setHints(int flags) {}
572 public void setPixels(int x, int y, int w, int h,
573 ColorModel model, byte[] pixels,
574 int offset, int scansize)
578 public void setPixels(int x, int y, int w, int h,
579 ColorModel model, int[] pixels,
580 int offset, int scansize)
582 if (model == null)
583 model = defaultModel;
585 if (bufferedImage == null)
587 bufferedImage = new BufferedImage (width, height, (model != null && model.hasAlpha() ?
588 BufferedImage.TYPE_INT_ARGB
589 : BufferedImage.TYPE_INT_RGB));
592 int pixels2[];
593 if (model != null)
595 pixels2 = new int[pixels.length];
596 for (int yy = 0; yy < h; yy++)
597 for (int xx = 0; xx < w; xx++)
599 int i = yy * scansize + xx;
600 pixels2[i] = model.getRGB (pixels[i]);
603 else
604 pixels2 = pixels;
606 bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
607 processImageProgress(y / (height == 0 ? 1 : height));
610 public void imageComplete(int status)
612 processImageComplete();
615 public BufferedImage getBufferedImage()
617 if (bufferedImage == null && dec != null)
618 dec.startProduction (this);
619 return bufferedImage;
622 // ImageReader parts
624 public int getNumImages(boolean allowSearch)
625 throws IOException
627 return 1;
630 public IIOMetadata getImageMetadata(int i)
632 return null;
635 public IIOMetadata getStreamMetadata()
636 throws IOException
638 return null;
641 public Iterator getImageTypes(int imageIndex)
642 throws IOException
644 BufferedImage img = getBufferedImage();
645 Vector vec = new Vector();
646 vec.add(new ImageTypeSpecifier(img));
647 return vec.iterator();
650 public int getHeight(int imageIndex)
651 throws IOException
653 return getBufferedImage().getHeight();
656 public int getWidth(int imageIndex)
657 throws IOException
659 return getBufferedImage().getWidth();
662 public void setInput(Object input,
663 boolean seekForwardOnly,
664 boolean ignoreMetadata)
666 super.setInput(input, seekForwardOnly, ignoreMetadata);
667 Object get = getInput();
668 if (get instanceof InputStream)
669 dec = new GdkPixbufDecoder((InputStream) get);
670 else if (get instanceof DataInput)
671 dec = new GdkPixbufDecoder((DataInput) get);
672 else
673 throw new IllegalArgumentException("input object not supported: "
674 + get);
677 public BufferedImage read(int imageIndex, ImageReadParam param)
678 throws IOException
680 return getBufferedImage ();
684 // remaining helper class and static method is a convenience for the Gtk
685 // peers, for loading a BufferedImage in off a disk file without going
686 // through the whole imageio system.
688 public static BufferedImage createBufferedImage (String filename)
690 GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(),
691 "png", // reader auto-detects, doesn't matter
692 new GdkPixbufDecoder (filename));
693 return r.getBufferedImage ();
696 public static BufferedImage createBufferedImage (URL u)
698 GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(),
699 "png", // reader auto-detects, doesn't matter
700 new GdkPixbufDecoder (u));
701 return r.getBufferedImage ();
704 public static BufferedImage createBufferedImage (byte[] imagedata, int imageoffset,
705 int imagelength)
707 GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(),
708 "png", // reader auto-detects, doesn't matter
709 new GdkPixbufDecoder (imagedata,
710 imageoffset,
711 imagelength));
712 return r.getBufferedImage ();
715 public static BufferedImage createBufferedImage (ImageProducer producer)
717 GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), "png" /* ignored */, null);
718 producer.startProduction(r);
719 return r.getBufferedImage ();