libjava/classpath/ChangeLog.gcj:
[official-gcc.git] / libjava / classpath / gnu / java / awt / peer / gtk / GdkPixbufDecoder.java
blob6f6ea560db70d81ef7970a9edf1ae5bae0e5a241
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 java.awt.image.BufferedImage;
42 import java.awt.image.ColorModel;
43 import java.awt.image.DirectColorModel;
44 import java.awt.image.ImageConsumer;
45 import java.awt.image.Raster;
46 import java.awt.image.RenderedImage;
47 import java.io.DataInput;
48 import java.io.DataOutput;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.net.URL;
52 import java.util.ArrayList;
53 import java.util.Hashtable;
54 import java.util.Iterator;
55 import java.util.Locale;
56 import java.util.Vector;
58 import javax.imageio.IIOImage;
59 import javax.imageio.ImageReadParam;
60 import javax.imageio.ImageReader;
61 import javax.imageio.ImageTypeSpecifier;
62 import javax.imageio.ImageWriteParam;
63 import javax.imageio.ImageWriter;
64 import javax.imageio.metadata.IIOMetadata;
65 import javax.imageio.spi.IIORegistry;
66 import javax.imageio.spi.ImageReaderSpi;
67 import javax.imageio.spi.ImageWriterSpi;
68 import javax.imageio.stream.ImageInputStream;
69 import javax.imageio.stream.ImageOutputStream;
71 public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
73 static
75 System.loadLibrary("gtkpeer");
77 initStaticState ();
80 /**
81 * Lock that should be held for all gdkpixbuf operations. We don't use
82 * the global gdk_threads_enter/leave functions since gdkpixbuf
83 * operations can be done in parallel to drawing and manipulating gtk
84 * widgets.
86 static Object pixbufLock = new Object();
88 static native void initStaticState();
89 private final int native_state = GtkGenericPeer.getUniqueInteger ();
91 // initState() has been called, but pumpDone() has not yet been called.
92 private boolean needsClose = false;
94 // the current set of ImageConsumers for this decoder
95 Vector curr;
97 // interface to GdkPixbuf
98 // These native functions should be called with the pixbufLock held.
99 native void initState ();
100 native void pumpBytes (byte[] bytes, int len) throws IOException;
101 native void pumpDone () throws IOException;
102 native void finish (boolean needsClose);
105 * Converts given image to bytes.
106 * Will call the GdkPixbufWriter for each chunk.
108 static native void streamImage(int[] bytes, String format,
109 int width, int height,
110 boolean hasAlpha, GdkPixbufWriter writer);
112 // gdk-pixbuf provids data in RGBA format
113 static final ColorModel cm = new DirectColorModel (32, 0xff000000,
114 0x00ff0000,
115 0x0000ff00,
116 0x000000ff);
117 public GdkPixbufDecoder (DataInput datainput)
119 super (datainput);
122 public GdkPixbufDecoder (InputStream in)
124 super (in);
127 public GdkPixbufDecoder (String filename)
129 super (filename);
132 public GdkPixbufDecoder (URL url)
134 super (url);
137 public GdkPixbufDecoder (byte[] imagedata, int imageoffset, int imagelength)
139 super (imagedata, imageoffset, imagelength);
142 // called back by native side: area_prepared_cb
143 void areaPrepared (int width, int height)
146 if (curr == null)
147 return;
149 for (int i = 0; i < curr.size (); i++)
151 ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
152 ic.setDimensions (width, height);
153 ic.setColorModel (cm);
154 ic.setHints (ImageConsumer.RANDOMPIXELORDER);
158 // called back by native side: area_updated_cb
159 void areaUpdated (int x, int y, int width, int height,
160 int pixels[], int scansize)
162 if (curr == null)
163 return;
165 for (int i = 0; i < curr.size (); i++)
167 ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
168 ic.setPixels (x, y, width, height, cm, pixels, 0, scansize);
172 // called from an async image loader of one sort or another, this method
173 // repeatedly reads bytes from the input stream and passes them through a
174 // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn
175 // decodes the image data and calls back areaPrepared and areaUpdated on
176 // this object, feeding back decoded pixel blocks, which we pass to each
177 // of the ImageConsumers in the provided Vector.
179 public void produce (Vector v, InputStream is) throws IOException
181 curr = v;
183 byte bytes[] = new byte[4096];
184 int len = 0;
185 synchronized(pixbufLock)
187 initState();
189 needsClose = true;
191 // Note: We don't want the pixbufLock while reading from the InputStream.
192 while ((len = is.read (bytes)) != -1)
194 synchronized(pixbufLock)
196 pumpBytes (bytes, len);
200 synchronized(pixbufLock)
202 pumpDone();
205 needsClose = false;
207 for (int i = 0; i < curr.size (); i++)
209 ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
210 ic.imageComplete (ImageConsumer.STATICIMAGEDONE);
213 curr = null;
216 public void finalize()
218 synchronized(pixbufLock)
220 finish(needsClose);
225 public static class ImageFormatSpec
227 public String name;
228 public boolean writable = false;
229 public ArrayList<String> mimeTypes = new ArrayList<String>();
230 public ArrayList<String> extensions = new ArrayList<String>();
232 public ImageFormatSpec(String name, boolean writable)
234 this.name = name;
235 this.writable = writable;
238 public synchronized void addMimeType(String m)
240 mimeTypes.add(m);
243 public synchronized void addExtension(String e)
245 extensions.add(e);
249 static ArrayList<ImageFormatSpec> imageFormatSpecs;
251 public static ImageFormatSpec registerFormat(String name, boolean writable)
253 ImageFormatSpec ifs = new ImageFormatSpec(name, writable);
254 synchronized(GdkPixbufDecoder.class)
256 if (imageFormatSpecs == null)
257 imageFormatSpecs = new ArrayList<ImageFormatSpec>();
258 imageFormatSpecs.add(ifs);
260 return ifs;
263 static String[] getFormatNames(boolean writable)
265 ArrayList<String> names = new ArrayList<String>();
266 synchronized (imageFormatSpecs)
268 Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
269 while (i.hasNext())
271 ImageFormatSpec ifs = i.next();
272 if (writable && !ifs.writable)
273 continue;
274 names.add(ifs.name);
277 * In order to make the filtering code work, we need to register
278 * this type under every "format name" likely to be used as a synonym.
279 * This generally means "all the extensions people might use".
282 Iterator<String> j = ifs.extensions.iterator();
283 while (j.hasNext())
284 names.add(j.next());
287 return names.toArray(new String[names.size()]);
290 static String[] getFormatExtensions(boolean writable)
292 ArrayList<String> extensions = new ArrayList<String>();
293 synchronized (imageFormatSpecs)
295 Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
296 while (i.hasNext())
298 ImageFormatSpec ifs = i.next();
299 if (writable && !ifs.writable)
300 continue;
301 Iterator<String> j = ifs.extensions.iterator();
302 while (j.hasNext())
303 extensions.add(j.next());
306 return extensions.toArray(new String[extensions.size()]);
309 static String[] getFormatMimeTypes(boolean writable)
311 ArrayList<String> mimeTypes = new ArrayList<String>();
312 synchronized (imageFormatSpecs)
314 Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
315 while (i.hasNext())
317 ImageFormatSpec ifs = i.next();
318 if (writable && !ifs.writable)
319 continue;
320 Iterator<String> j = ifs.mimeTypes.iterator();
321 while (j.hasNext())
322 mimeTypes.add(j.next());
325 return mimeTypes.toArray(new String[mimeTypes.size()]);
329 static String findFormatName(Object ext, boolean needWritable)
331 if (ext == null)
332 return null;
334 if (!(ext instanceof String))
335 throw new IllegalArgumentException("extension is not a string");
337 String str = (String) ext;
339 Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
340 while (i.hasNext())
342 ImageFormatSpec ifs = i.next();
344 if (needWritable && !ifs.writable)
345 continue;
347 if (ifs.name.equals(str))
348 return str;
350 Iterator<String> j = ifs.extensions.iterator();
351 while (j.hasNext())
353 String extension = j.next();
354 if (extension.equals(str))
355 return ifs.name;
358 j = ifs.mimeTypes.iterator();
359 while (j.hasNext())
361 String mimeType = j.next();
362 if (mimeType.equals(str))
363 return ifs.name;
366 throw new IllegalArgumentException("unknown extension '" + str + "'");
369 private static GdkPixbufReaderSpi readerSpi;
370 private static GdkPixbufWriterSpi writerSpi;
372 public static synchronized GdkPixbufReaderSpi getReaderSpi()
374 if (readerSpi == null)
375 readerSpi = new GdkPixbufReaderSpi();
376 return readerSpi;
379 public static synchronized GdkPixbufWriterSpi getWriterSpi()
381 if (writerSpi == null)
382 writerSpi = new GdkPixbufWriterSpi();
383 return writerSpi;
386 public static void registerSpis(IIORegistry reg)
388 reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class);
389 reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class);
392 public static class GdkPixbufWriterSpi extends ImageWriterSpi
394 public GdkPixbufWriterSpi()
396 super("GdkPixbuf", "2.x",
397 GdkPixbufDecoder.getFormatNames(true),
398 GdkPixbufDecoder.getFormatExtensions(true),
399 GdkPixbufDecoder.getFormatMimeTypes(true),
400 "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
401 new Class[] { ImageOutputStream.class },
402 new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
403 false, null, null, null, null,
404 false, null, null, null, null);
407 public boolean canEncodeImage(ImageTypeSpecifier ts)
409 return true;
412 public ImageWriter createWriterInstance(Object ext)
414 return new GdkPixbufWriter(this, ext);
417 public String getDescription(java.util.Locale loc)
419 return "GdkPixbuf Writer SPI";
424 public static class GdkPixbufReaderSpi extends ImageReaderSpi
426 public GdkPixbufReaderSpi()
428 super("GdkPixbuf", "2.x",
429 GdkPixbufDecoder.getFormatNames(false),
430 GdkPixbufDecoder.getFormatExtensions(false),
431 GdkPixbufDecoder.getFormatMimeTypes(false),
432 "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
433 new Class[] { ImageInputStream.class },
434 new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
435 false, null, null, null, null,
436 false, null, null, null, null);
439 public boolean canDecodeInput(Object obj)
441 return true;
444 public ImageReader createReaderInstance(Object ext)
446 return new GdkPixbufReader(this, ext);
449 public String getDescription(Locale loc)
451 return "GdkPixbuf Reader SPI";
455 private static class GdkPixbufWriter
456 extends ImageWriter implements Runnable
458 String ext;
459 public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
461 super(ownerSpi);
462 this.ext = findFormatName(ext, true);
465 public IIOMetadata convertImageMetadata (IIOMetadata inData,
466 ImageTypeSpecifier imageType,
467 ImageWriteParam param)
469 return null;
472 public IIOMetadata convertStreamMetadata (IIOMetadata inData,
473 ImageWriteParam param)
475 return null;
478 public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType,
479 ImageWriteParam param)
481 return null;
484 public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
486 return null;
489 public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
490 throws IOException
492 RenderedImage image = i.getRenderedImage();
493 Raster ras = image.getData();
494 int width = ras.getWidth();
495 int height = ras.getHeight();
496 ColorModel model = image.getColorModel();
497 int[] pixels = CairoGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras);
499 if (pixels == null)
501 BufferedImage img;
502 if(model != null && model.hasAlpha())
503 img = CairoSurface.getBufferedImage(width, height);
504 img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
505 int[] pix = new int[4];
506 for (int y = 0; y < height; ++y)
507 for (int x = 0; x < width; ++x)
508 img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix)));
509 pixels = CairoGraphics2D.findSimpleIntegerArray (img.getColorModel(),
510 img.getRaster());
511 model = img.getColorModel();
514 Thread workerThread = new Thread(this, "GdkPixbufWriter");
515 workerThread.start();
516 processImageStarted(1);
517 synchronized(pixbufLock)
519 streamImage(pixels, this.ext, width, height, model.hasAlpha(),
520 this);
522 synchronized(data)
524 data.add(DATADONE);
525 data.notifyAll();
528 while (workerThread.isAlive())
532 workerThread.join();
534 catch (InterruptedException ioe)
536 // Ignored.
540 if (exception != null)
541 throw exception;
543 processImageComplete();
547 * Object marking end of data from native streamImage code.
549 private static final Object DATADONE = new Object();
552 * Holds the data gotten from the native streamImage code.
553 * A worker thread will pull data out.
554 * Needs to be synchronized for access.
555 * The special object DATADONE is added when all data has been delivered.
557 private ArrayList<Object> data = new ArrayList<Object>();
560 * Holds any IOException thrown by the run method that needs
561 * to be rethrown by the write method.
563 private IOException exception;
565 /** Callback for streamImage native code. **/
566 private void write(byte[] bs)
568 synchronized(data)
570 data.add(bs);
571 data.notifyAll();
575 public void run()
577 boolean done = false;
578 while (!done)
580 synchronized(data)
582 while (data.isEmpty())
586 data.wait();
588 catch (InterruptedException ie)
590 /* ignore */
594 Object o = data.remove(0);
595 if (o == DATADONE)
596 done = true;
597 else
599 DataOutput out = (DataOutput) getOutput();
602 out.write((byte[]) o);
604 catch (IOException ioe)
606 // We are only interested in the first exception.
607 if (exception == null)
608 exception = ioe;
616 private static class GdkPixbufReader
617 extends ImageReader
618 implements ImageConsumer
620 // ImageConsumer parts
621 GdkPixbufDecoder dec;
622 BufferedImage bufferedImage;
623 ColorModel defaultModel;
624 int width;
625 int height;
626 String ext;
628 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
630 super(ownerSpi);
631 this.ext = findFormatName(ext, false);
634 public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext,
635 GdkPixbufDecoder d)
637 this(ownerSpi, ext);
638 dec = d;
641 public void setDimensions(int w, int h)
643 processImageStarted(1);
644 width = w;
645 height = h;
648 public void setProperties(Hashtable props) {}
650 public void setColorModel(ColorModel model)
652 defaultModel = model;
655 public void setHints(int flags) {}
657 public void setPixels(int x, int y, int w, int h,
658 ColorModel model, byte[] pixels,
659 int offset, int scansize)
663 public void setPixels(int x, int y, int w, int h,
664 ColorModel model, int[] pixels,
665 int offset, int scansize)
667 if (model == null)
668 model = defaultModel;
670 if (bufferedImage == null)
672 if(model != null && model.hasAlpha())
673 bufferedImage = new BufferedImage (width, height,
674 BufferedImage.TYPE_INT_ARGB);
675 else
676 bufferedImage = new BufferedImage (width, height,
677 BufferedImage.TYPE_INT_RGB);
680 int pixels2[];
681 if (model != null)
683 pixels2 = new int[pixels.length];
684 for (int yy = 0; yy < h; yy++)
685 for (int xx = 0; xx < w; xx++)
687 int i = yy * scansize + xx;
688 pixels2[i] = model.getRGB (pixels[i]);
691 else
692 pixels2 = pixels;
694 bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
695 processImageProgress(y / (height == 0 ? 1 : height));
698 public void imageComplete(int status)
700 processImageComplete();
703 public BufferedImage getBufferedImage()
705 if (bufferedImage == null && dec != null)
706 dec.startProduction (this);
707 return bufferedImage;
710 // ImageReader parts
712 public int getNumImages(boolean allowSearch)
713 throws IOException
715 return 1;
718 public IIOMetadata getImageMetadata(int i)
720 return null;
723 public IIOMetadata getStreamMetadata()
724 throws IOException
726 return null;
729 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
730 throws IOException
732 BufferedImage img = getBufferedImage();
733 Vector<ImageTypeSpecifier> vec = new Vector<ImageTypeSpecifier>();
734 vec.add(new ImageTypeSpecifier(img));
735 return vec.iterator();
738 public int getHeight(int imageIndex)
739 throws IOException
741 return getBufferedImage().getHeight();
744 public int getWidth(int imageIndex)
745 throws IOException
747 return getBufferedImage().getWidth();
750 public void setInput(Object input,
751 boolean seekForwardOnly,
752 boolean ignoreMetadata)
754 super.setInput(input, seekForwardOnly, ignoreMetadata);
755 Object get = getInput();
756 if (get instanceof InputStream)
757 dec = new GdkPixbufDecoder((InputStream) get);
758 else if (get instanceof DataInput)
759 dec = new GdkPixbufDecoder((DataInput) get);
760 else
761 throw new IllegalArgumentException("input object not supported: "
762 + get);
765 public BufferedImage read(int imageIndex, ImageReadParam param)
766 throws IOException
768 return getBufferedImage ();