This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gui][patch] implement imageio SPI for gdk-pixbuf


hi,

this patch, which I've just committed to java-gui-branch, provides a javax.imageio SPI for the gdk-pixbuf library, and corrects a variety of small bugs in the existing imageio implementation. it also adds the standard static entry points to imageio. so your basic actions like

     ImageIO.write(ImageIO.read(new File("foo.jpg"),
                   "png", new File("bar.png")));

should now work. it can also use URLs and Input/OutputStreams. buffering is pretty crude and purely in-memory, so you will pay about 2x size of image worth of memory, temporarily, during a write.

the available input and output types are registered dynamically by querying modules in gdk-pixbuf. currently, looking at the modules which came with my copy of gdk-pixbuf, this means you get:

 - loading capability for ani, bpm, gif, ico, jpeg, png, pnm, ras, tga,
   tiff, wbmp, xbm and xpm

- saving capability for jpeg and png

of course the SPI is not available if you're not using the GTK toolkit; it is registered dynamically depending on the choice of toolkit. also, multi-frame i/o and all metadata operations are currently unsupported.

-graydon


2005-01-04 Graydon Hoare <graydon@redhat.com>


	* gnu/java/awt/ClasspathToolkit.java
	(registerImageIOSpis): New method.
	* gnu/java/awt/image/ImageDecoder.java
	(imageDecoder): New constructor using InputStream
	(startProduction): Handle existing InputStream.
	* gnu/java/awt/peer/gtk/GdkGraphics2D.java
	(findSimpleIntegerArray): Make public and static.
	(updateBufferedImage): Set each pixel, in a loop.
	* gnu/java/awt/peer/gtk/GdkPixbufDecoder.java:
	Implement ImageIO SPI classes.
	(createBufferedImage): Rewrite in terms of SPI classes.
	* gnu/java/awt/peer/gtk/GtkToolkit.java
	(registerImageIOSpis): New method.
	* java/lang/reflect/natMethod.cc
	(_Jv_CallAnyMethodA): Borrow a patch from aph, applied to trunk,
	which lets JNI call interface methods properly.
	* javax/imageio/ImageIO.java
	(WriterFormatFilter.filter): Fix copy-and-paste typos.
	(WriterMIMETypeFilter.filter): Likewise.
	(ImageReaderIterator): Pass extension argument through to SPI.
	(getReadersByFilter): Likewise.
	(getWritersByFilter): Likewise.
	(getImageReadersByFormatName): Likewise.
	(getImageReadersByMIMEType): Likewise.
	(getImageReadersBySuffix): Likewise.
	(getImageWritersByFormatName): Likewise.
	(getImageWritersByMIMEType): Likewise.
	(getImageWritersBySuffix): Likewise.
	(read): Implement.
	(write): Implement.
	* javax/imageio/ImageReader.java
	(progressListeners): Initialize.
	(setInput): Implement.
	* javax/imageio/ImageWriter.java
	(progressListeners): Initialize.
	(warningListeners): Likewise.
	(warningLocales): Likewise.
	(setOutput): Test "isInstance" rather than class equality.
	* javax/imageio/spi/IIORegistry.java
	(static): Add reader and writer SPIs.
	(IIORegistry): Call ClasspathToolkit.registerImageIOSpis.
	* jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c
	(query_formats): New function.
	(save_to_stream): Likewise.
	(Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage): Likewise.
--- gnu/java/awt/ClasspathToolkit.java	4 Jan 2005 18:45:20 -0000	1.1.18.6
+++ gnu/java/awt/ClasspathToolkit.java	4 Jan 2005 20:47:59 -0000
@@ -62,6 +62,7 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.awt.peer.RobotPeer;
+import javax.imageio.spi.IIORegistry;
 
 
 /**
@@ -356,6 +357,14 @@
   public abstract RobotPeer createRobot (GraphicsDevice screen)
     throws AWTException;
 
+  /** 
+   * Used to register ImageIO SPIs provided by the toolkit.
+   */
+
+  public void registerImageIOSpis(IIORegistry reg)
+  {
+  }
+
   public abstract boolean nativeQueueEmpty();
   public abstract void wakeNativeQueue();  
   public abstract void iterateNativeQueue(EventQueue locked);
--- gnu/java/awt/image/ImageDecoder.java	24 Jun 2004 05:30:00 -0000	1.3.8.2
+++ gnu/java/awt/image/ImageDecoder.java	4 Jan 2005 20:47:59 -0000
@@ -74,6 +74,11 @@
     this.url = url;
   }
 
+  public ImageDecoder (InputStream is)
+  {
+    this.input = is;
+  }
+
   public ImageDecoder (byte[] imagedata, int imageoffset, int imagelength)
   {
     data = imagedata;
@@ -108,17 +113,30 @@
 	// ImageDecoder constructors so that exceptions cause
 	// imageComplete to be called with an appropriate error
 	// status.
-	if (url != null)
-	  input = url.openStream();
-	else
-	  {
-	    if (filename != null)
-	      input = new FileInputStream (filename);
-	    else
-	      input = new ByteArrayInputStream (data, offset, length);
-	  }
-
-	produce (list, input);
+        if (input == null)
+          {
+            try 
+              {
+                if (url != null)
+                  input = url.openStream();
+                else
+                  {
+                    if (filename != null)
+                      input = new FileInputStream (filename);
+                    else
+                      input = new ByteArrayInputStream (data, offset, length);
+                  }
+                produce (list, input);
+              } 
+            finally 
+              {
+                input = null;
+              }
+          }
+        else
+          {
+            produce (list, input);
+          }
       }
     catch (Exception e)
       {
--- gnu/java/awt/peer/gtk/GdkGraphics2D.java	26 Dec 2004 13:07:29 -0000	1.7.2.25
+++ gnu/java/awt/peer/gtk/GdkGraphics2D.java	4 Jan 2005 20:47:59 -0000
@@ -451,7 +451,7 @@
     return defaultHints;
   }
 
-  private final int[] findSimpleIntegerArray(ColorModel cm, Raster raster)
+  public static final int[] findSimpleIntegerArray (ColorModel cm, Raster raster)
   {
     if (cm == null || raster == null)
       return null;
@@ -494,10 +494,11 @@
   {
     if (bimage != null && pixelConversionRequired)
       {
-        bimage.getRaster().setPixels(0, 0, 
-                                     bimage.getRaster().getWidth (),
-                                     bimage.getRaster().getHeight (), 
-                                     pixelBuffer);
+        int height = bimage.getHeight();
+        int width = bimage.getWidth();
+        for (int y = 0; y < height; ++y)
+          for (int x = 0; x < width; ++x)
+            bimage.setRGB(x, y, pixelBuffer[y*width+height]);
       }
   }
 
--- gnu/java/awt/peer/gtk/GdkPixbufDecoder.java	7 Dec 2004 00:30:11 -0000	1.3.10.4
+++ gnu/java/awt/peer/gtk/GdkPixbufDecoder.java	4 Jan 2005 20:47:59 -0000
@@ -42,17 +42,38 @@
 
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
+import java.awt.image.DataBufferInt;
 import java.awt.image.DirectColorModel;
 import java.awt.image.ImageConsumer;
 import java.awt.image.ImageProducer;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.io.DataOutput;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
 import java.util.Vector;
 
+import javax.imageio.ImageReader;
+import javax.imageio.ImageWriter;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.IIOImage;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.spi.IIORegistry;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+
 public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
 {
   static 
@@ -65,20 +86,27 @@
   }
   native static void initStaticState ();
   private final int native_state = GtkGenericPeer.getUniqueInteger ();
+  private boolean initialized = false;
 
   // the current set of ImageConsumers for this decoder
   Vector curr;
 
   // interface to GdkPixbuf
   native void initState ();
-  native void pumpBytes (byte bytes[], int len);
+  native void pumpBytes (byte[] bytes, int len);
   native void finish ();
+  static native void streamImage(int[] bytes, String format, int width, int height, boolean hasAlpha, DataOutput sink);
   
   // gdk-pixbuf provids data in RGBA format
   static final ColorModel cm = new DirectColorModel (32, 0xff000000, 
                                                      0x00ff0000, 
                                                      0x0000ff00, 
                                                      0x000000ff);
+  public GdkPixbufDecoder (InputStream in)
+  {
+    super (in);
+  }
+
   public GdkPixbufDecoder (String filename)
   {
     super (filename);
@@ -148,27 +176,345 @@
       }
 
     curr = null;
+    finish();    
   }
 
-  // remaining helper class and static method is a convenience for the Gtk
-  // peers, for loading a BufferedImage in off a disk file. one would think
-  // this ought to be fairly straightforward, but it does not appear
-  // anywhere else I can find.
 
-  private static class BufferedImageBuilder implements ImageConsumer
+  public static class ImageFormatSpec
+  {
+    public String name;
+    public boolean writable = false;    
+    public ArrayList mimeTypes = new ArrayList();
+    public ArrayList extensions = new ArrayList();
+
+    public ImageFormatSpec(String name, boolean writable)
+    {
+      this.name = name;
+      this.writable = writable;
+    }
+
+    public synchronized void addMimeType(String m)
+    {
+      mimeTypes.add(m);
+    }
+
+    public synchronized void addExtension(String e)
+    {
+      extensions.add(e);
+    }    
+  }
+
+  static ArrayList imageFormatSpecs;
+
+  public static ImageFormatSpec registerFormat(String name, boolean writable) 
+  {
+    ImageFormatSpec ifs = new ImageFormatSpec(name, writable);
+    synchronized(GdkPixbufDecoder.class)
+      {
+        if (imageFormatSpecs == null)
+          imageFormatSpecs = new ArrayList();
+        imageFormatSpecs.add(ifs);
+      }
+    return ifs;
+  }
+
+  static String[] getFormatNames(boolean writable)
+  {
+    ArrayList names = new ArrayList();
+    synchronized (imageFormatSpecs) 
+      {
+        Iterator i = imageFormatSpecs.iterator();
+        while (i.hasNext())
+          {
+            ImageFormatSpec ifs = (ImageFormatSpec) i.next();
+            if (writable && !ifs.writable)
+              continue;
+            names.add(ifs.name);
+
+            /* 
+             * In order to make the filtering code work, we need to register
+             * this type under every "format name" likely to be used as a synonym.
+             * This generally means "all the extensions people might use". 
+             */
+
+            Iterator j = ifs.extensions.iterator();
+            while (j.hasNext())
+              names.add((String) j.next());
+          }
+      }
+    Object[] objs = names.toArray();
+    String[] strings = new String[objs.length];
+    for (int i = 0; i < objs.length; ++i)
+      strings[i] = (String) objs[i];
+    return strings;
+  }
+
+  static String[] getFormatExtensions(boolean writable)
+  {
+    ArrayList extensions = new ArrayList();
+    synchronized (imageFormatSpecs) 
+      {
+        Iterator i = imageFormatSpecs.iterator();
+        while (i.hasNext())
+          {
+            ImageFormatSpec ifs = (ImageFormatSpec) i.next();
+            if (writable && !ifs.writable)
+              continue;
+            Iterator j = ifs.extensions.iterator();
+            while (j.hasNext())
+              extensions.add((String) j.next());
+          }
+      }
+    Object[] objs = extensions.toArray();
+    String[] strings = new String[objs.length];
+    for (int i = 0; i < objs.length; ++i)
+      strings[i] = (String) objs[i];
+    return strings;
+  }
+
+  static String[] getFormatMimeTypes(boolean writable)
+  {
+    ArrayList mimeTypes = new ArrayList();
+    synchronized (imageFormatSpecs) 
+      {
+        Iterator i = imageFormatSpecs.iterator();
+        while (i.hasNext())
+          {
+            ImageFormatSpec ifs = (ImageFormatSpec) i.next();
+            if (writable && !ifs.writable)
+              continue;
+            Iterator j = ifs.mimeTypes.iterator();
+            while (j.hasNext())
+              mimeTypes.add((String) j.next());
+          }
+      }
+    Object[] objs = mimeTypes.toArray();
+    String[] strings = new String[objs.length];
+    for (int i = 0; i < objs.length; ++i)
+      strings[i] = (String) objs[i];
+    return strings;
+  }
+
+  
+  static String findFormatName(Object ext, boolean needWritable)
+  {
+    if (ext == null)
+      throw new IllegalArgumentException("extension is null");
+
+    if (!(ext instanceof String))
+      throw new IllegalArgumentException("extension is not a string");
+
+    String str = (String) ext;
+
+    Iterator i = imageFormatSpecs.iterator();
+    while (i.hasNext())
+      {
+        ImageFormatSpec ifs = (ImageFormatSpec) i.next();
+
+        if (needWritable && !ifs.writable)
+          continue;
+
+        if (ifs.name.equals(str))
+          return str;
+
+        Iterator j = ifs.extensions.iterator(); 
+        while (j.hasNext())
+          {
+            String extension = (String)j.next();
+            if (extension.equals(str))
+              return ifs.name;
+          }
+
+        j = ifs.mimeTypes.iterator(); 
+        while (j.hasNext())
+          {
+            String mimeType = (String)j.next();
+            if (mimeType.equals(str))
+              return ifs.name;
+          }
+      }      
+    throw new IllegalArgumentException("unknown extension '" + str + "'");
+  }
+
+  private static GdkPixbufReaderSpi readerSpi;
+  private static GdkPixbufWriterSpi writerSpi;
+
+  public static synchronized GdkPixbufReaderSpi getReaderSpi()
+  {
+    if (readerSpi == null)
+      readerSpi = new GdkPixbufReaderSpi();
+    return readerSpi;
+  }
+
+  public static synchronized GdkPixbufWriterSpi getWriterSpi()
+  {
+    if (writerSpi == null)
+      writerSpi = new GdkPixbufWriterSpi();
+    return writerSpi;
+  }
+
+  public static void registerSpis(IIORegistry reg) 
+  {
+    reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class);
+    reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class);
+  }
+
+  public static class GdkPixbufWriterSpi extends ImageWriterSpi
+  {
+    public GdkPixbufWriterSpi() 
+    {      
+      super("GdkPixbuf", "2.x",
+            GdkPixbufDecoder.getFormatNames(true), 
+            GdkPixbufDecoder.getFormatExtensions(true), 
+            GdkPixbufDecoder.getFormatMimeTypes(true),
+            "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
+            new Class[] { ImageOutputStream.class },
+            new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
+            false, null, null, null, null,
+            false, null, null, null, null);
+    }
+
+    public boolean canEncodeImage(ImageTypeSpecifier ts)
+    {
+      return true;
+    }
+
+    public ImageWriter createWriterInstance(Object ext)
+    {
+      return new GdkPixbufWriter(this, ext);
+    }
+
+    public String getDescription(java.util.Locale loc)
+    {
+      return "GdkPixbuf Writer SPI";
+    }
+
+  }
+
+  public static class GdkPixbufReaderSpi extends ImageReaderSpi
+  {
+    public GdkPixbufReaderSpi() 
+    { 
+      super("GdkPixbuf", "2.x",
+            GdkPixbufDecoder.getFormatNames(false), 
+            GdkPixbufDecoder.getFormatExtensions(false), 
+            GdkPixbufDecoder.getFormatMimeTypes(false),
+            "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
+            new Class[] { ImageInputStream.class },
+            new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
+            false, null, null, null, null,
+            false, null, null, null, null);
+    }
+
+    public boolean canDecodeInput(Object obj) 
+    { 
+      return true; 
+    }
+
+    public ImageReader createReaderInstance(Object ext)
+    {
+      return new GdkPixbufReader(this, ext);
+    }
+
+    public String getDescription(Locale loc)
+    {
+      return "GdkPixbuf Reader SPI";
+    }
+  }
+
+  private static class GdkPixbufWriter
+    extends ImageWriter
+  {
+    String ext;
+    public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
+    {
+      super(ownerSpi);
+      this.ext = findFormatName(ext, true);
+    }
+
+    public IIOMetadata convertImageMetadata (IIOMetadata inData,
+                                             ImageTypeSpecifier imageType,
+                                             ImageWriteParam param)
+    {
+      return null;
+    }
+
+    public IIOMetadata convertStreamMetadata (IIOMetadata inData,
+                                              ImageWriteParam param)
+    {
+      return null;
+    }
+
+    public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType, 
+                                                ImageWriteParam param)
+    {
+      return null;
+    }
+
+    public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
+    {
+      return null;
+    }
+
+  public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
+    throws IOException
+    {
+      RenderedImage image = i.getRenderedImage();
+      Raster ras = image.getData();
+      int width = ras.getWidth();
+      int height = ras.getHeight();
+      ColorModel model = image.getColorModel();
+      int[] pixels = GdkGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras);
+      
+      if (pixels == null)
+        {
+          BufferedImage img = new BufferedImage(width, height, 
+                                                (model != null && model.hasAlpha() ? 
+                                                 BufferedImage.TYPE_INT_ARGB
+                                                 : BufferedImage.TYPE_INT_RGB));
+          int[] pix = new int[4];
+          for (int y = 0; y < height; ++y)
+            for (int x = 0; x < width; ++x)
+              img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix)));
+          pixels = GdkGraphics2D.findSimpleIntegerArray (img.getColorModel(), 
+                                                         img.getRaster());
+          model = img.getColorModel();
+        }
+
+      processImageStarted(1);
+      streamImage(pixels, this.ext, width, height, model.hasAlpha(), 
+                  (DataOutput) this.getOutput());
+      processImageComplete();
+    }    
+  }
+
+  private static class GdkPixbufReader 
+    extends ImageReader
+    implements ImageConsumer
   {
+    // ImageConsumer parts
+    GdkPixbufDecoder dec;
     BufferedImage bufferedImage;
     ColorModel defaultModel;
     int width;
     int height;
+    String ext;
+    
+    public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
+    {
+      super(ownerSpi);
+      this.ext = findFormatName(ext, false);
+    }
 
-    public BufferedImage getBufferedImage()
+    public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext, GdkPixbufDecoder d)
     {
-      return bufferedImage;
+      this(ownerSpi, ext);
+      dec = d;
     }
 
     public void setDimensions(int w, int h)
     {
+      processImageStarted(1);
       width = w;
       height = h;
     }
@@ -196,9 +542,12 @@
         model = defaultModel;
       
       if (bufferedImage == null)
-        bufferedImage = new BufferedImage (width, height, (model != null && model.hasAlpha() ? 
-                                                           BufferedImage.TYPE_INT_ARGB
-                                                           : BufferedImage.TYPE_INT_RGB));
+        {
+          bufferedImage = new BufferedImage (width, height, (model != null && model.hasAlpha() ? 
+                                                             BufferedImage.TYPE_INT_ARGB
+                                                             : BufferedImage.TYPE_INT_RGB));
+        }
+
       int pixels2[];
       if (model != null)
         {
@@ -214,43 +563,112 @@
         pixels2 = pixels;
 
       bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
+      processImageProgress(y / (height == 0 ? 1 : height));
+    }
+
+    public void imageComplete(int status) 
+    {
+      processImageComplete();
+    }
+
+    public BufferedImage getBufferedImage()
+    {
+      if (bufferedImage == null && dec != null)
+        dec.startProduction (this);
+      return bufferedImage;
+    }
+
+    // ImageReader parts
+
+    public int getNumImages(boolean allowSearch)
+      throws IOException
+    {
+      return 1;
+    }
+
+    public IIOMetadata getImageMetadata(int i) 
+    {
+      return null;
+    }
+
+    public IIOMetadata getStreamMetadata()
+      throws IOException
+    {
+      return null;
+    }
+
+    public Iterator getImageTypes(int imageIndex)
+      throws IOException
+    {
+      BufferedImage img = getBufferedImage();
+      Vector vec = new Vector();
+      vec.add(new ImageTypeSpecifier(img));
+      return vec.iterator();
+    }
+    
+    public int getHeight(int imageIndex)
+      throws IOException
+    {
+      return getBufferedImage().getHeight();
+    }
+
+    public int getWidth(int imageIndex)
+      throws IOException
+    {
+      return getBufferedImage().getWidth();
     }
 
-    public void imageComplete(int status) {}
+    public void setInput(Object input,
+                         boolean seekForwardOnly,
+                         boolean ignoreMetadata)
+    {
+      super.setInput(input, seekForwardOnly, ignoreMetadata);
+      dec = new GdkPixbufDecoder((InputStream) getInput());
+    }
+
+    public BufferedImage read(int imageIndex, ImageReadParam param)
+      throws IOException
+    {
+      return getBufferedImage ();
+    }
   }
 
+  // remaining helper class and static method is a convenience for the Gtk
+  // peers, for loading a BufferedImage in off a disk file without going
+  // through the whole imageio system. 
+
   public static BufferedImage createBufferedImage (String filename)
   {
-    BufferedImageBuilder bb = new BufferedImageBuilder ();
-    GdkPixbufDecoder dec = new GdkPixbufDecoder (filename);
-    dec.startProduction (bb);
-    return bb.getBufferedImage ();
+    GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), 
+                                             "png", // reader auto-detects, doesn't matter
+                                             new GdkPixbufDecoder (filename));
+    return r.getBufferedImage ();
   }
 
   public static BufferedImage createBufferedImage (URL u)
   {
-    BufferedImageBuilder bb = new BufferedImageBuilder ();
-    GdkPixbufDecoder dec = new GdkPixbufDecoder (u);
-    dec.startProduction (bb);
-    return bb.getBufferedImage ();
+    GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), 
+                                             "png", // reader auto-detects, doesn't matter
+                                             new GdkPixbufDecoder (u));
+    return r.getBufferedImage ();
   }
 
   public static BufferedImage createBufferedImage (byte[] imagedata, int imageoffset,
                                                    int imagelength)
   {
-    BufferedImageBuilder bb = new BufferedImageBuilder ();
-    GdkPixbufDecoder dec = new GdkPixbufDecoder (imagedata, imageoffset, imagelength);
-    dec.startProduction (bb);
-    return bb.getBufferedImage ();
+    GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), 
+                                             "png", // reader auto-detects, doesn't matter
+                                             new GdkPixbufDecoder (imagedata,
+                                                                   imageoffset,
+                                                                   imagelength));
+    return r.getBufferedImage ();
   }
   
   public static BufferedImage createBufferedImage (ImageProducer producer)
   {
-    BufferedImageBuilder bb = new BufferedImageBuilder ();
-    producer.startProduction(bb);
-    return bb.getBufferedImage ();
+    GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), "png" /* ignored */, null);
+    producer.startProduction(r);
+    return r.getBufferedImage ();
   }
-  
-
 
 }
--- gnu/java/awt/peer/gtk/GtkToolkit.java	4 Jan 2005 18:45:21 -0000	1.8.2.16
+++ gnu/java/awt/peer/gtk/GtkToolkit.java	4 Jan 2005 20:47:59 -0000
@@ -649,6 +649,11 @@
     return new GdkRobotPeer (screen);
   }
 
+  public void registerImageIOSpis(IIORegistry reg)
+  {
+    GdkPixbufDecoder.registerSpis(reg);
+  }
+
   public native boolean nativeQueueEmpty();
   public native void wakeNativeQueue();  
   public native void iterateNativeQueue(EventQueue locked);
--- java/lang/reflect/natMethod.cc	24 Jun 2004 05:30:29 -0000	1.36.10.1
+++ java/lang/reflect/natMethod.cc	4 Jan 2005 20:47:59 -0000
@@ -30,6 +30,7 @@
 #include <java/lang/Double.h>
 #include <java/lang/IllegalAccessException.h>
 #include <java/lang/IllegalArgumentException.h>
+#include <java/lang/IncompatibleClassChangeError.h>
 #include <java/lang/NullPointerException.h>
 #include <java/lang/ArrayIndexOutOfBoundsException.h>
 #include <java/lang/VirtualMachineError.h>
@@ -483,7 +484,26 @@
     {
       _Jv_VTable *vtable = *(_Jv_VTable **) obj;
       if (iface == NULL)
-	ncode = vtable->get_method (meth->index);
+	{
+	  if (is_jni_call && Modifier::isAbstract (meth->accflags))
+	    {
+	      // With JNI we don't know if this is an interface call
+	      // or a call to an abstract method.  Look up the method
+	      // by name, the slow way.
+	      _Jv_Method *concrete_meth
+		= _Jv_LookupDeclaredMethod (vtable->clas,
+					    meth->name,
+					    meth->signature);
+	      if (concrete_meth == NULL
+		  || concrete_meth->ncode == NULL
+		  || Modifier::isAbstract(concrete_meth->accflags))
+		throw new java::lang::IncompatibleClassChangeError
+		  (_Jv_GetMethodString (vtable->clas, meth->name));
+	      ncode = concrete_meth->ncode;
+	    }
+	  else
+	    ncode = vtable->get_method (meth->index);
+	}
       else
 	ncode = _Jv_LookupInterfaceMethodIdx (vtable->clas, iface, meth->index);
     }
--- javax/imageio/ImageIO.java	7 Dec 2004 16:54:48 -0000	1.2.4.1
+++ javax/imageio/ImageIO.java	4 Jan 2005 20:48:00 -0000
@@ -38,8 +38,15 @@
 
 package javax.imageio;
 
+import java.awt.image.RenderedImage;
+import java.awt.image.BufferedImage;
+import java.net.URL;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -48,6 +55,10 @@
 import javax.imageio.spi.ImageReaderSpi;
 import javax.imageio.spi.ImageWriterSpi;
 import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+import javax.imageio.stream.MemoryCacheImageOutputStream;
 
 public final class ImageIO
 {
@@ -142,14 +153,14 @@
 
     public boolean filter(Object provider)
     {
-      if (provider instanceof ImageReaderSpi)
+      if (provider instanceof ImageWriterSpi)
 	{
-	  ImageReaderSpi spi = (ImageReaderSpi) provider;
+	  ImageWriterSpi spi = (ImageWriterSpi) provider;
 	  String[] formatNames = spi.getFormatNames();
 	  
 	  for (int i = formatNames.length - 1; i >= 0; --i)
-	    if (formatName.equals(formatNames[i]))
-	      return true;
+            if (formatName.equals(formatNames[i]))
+              return true;
 	}
 
       return false;
@@ -167,7 +178,7 @@
 
     public boolean filter(Object provider)
     {
-      if (provider instanceof ImageReaderSpi)
+      if (provider instanceof ImageWriterSpi)
         {
           ImageWriterSpi spi = (ImageWriterSpi) provider;
           String[] mimetypes = spi.getMIMETypes();
@@ -192,7 +203,7 @@
 
     public boolean filter(Object provider)
     {
-      if (provider instanceof ImageReaderSpi)
+      if (provider instanceof ImageWriterSpi)
         {
           ImageWriterSpi spi = (ImageWriterSpi) provider;
           String[] suffixes = spi.getFileSuffixes();
@@ -209,10 +220,12 @@
   private static final class ImageReaderIterator implements Iterator
   {
     Iterator it;
+    Object readerExtension;
     
-    public ImageReaderIterator(Iterator it)
+    public ImageReaderIterator(Iterator it, Object readerExtension)
     {
       this.it = it;
+      this.readerExtension = readerExtension;
     }
 
     public boolean hasNext()
@@ -224,7 +237,7 @@
     {
       try
         {
-          return ((ImageReaderSpi) it.next()).createReaderInstance();
+          return ((ImageReaderSpi) it.next()).createReaderInstance(readerExtension);
         }
       catch (IOException e)
         {
@@ -241,10 +254,12 @@
   private static final class ImageWriterIterator implements Iterator
   {
     Iterator it;
+    Object writerExtension;
     
-    public ImageWriterIterator(Iterator it)
+    public ImageWriterIterator(Iterator it, Object writerExtension)
     {
       this.it = it;
+      this.writerExtension = writerExtension;
     }
 
     public boolean hasNext()
@@ -256,7 +271,7 @@
     {
       try
         {
-          return ((ImageWriterSpi) it.next()).createWriterInstance();
+          return ((ImageWriterSpi) it.next()).createWriterInstance(writerExtension);
         }
       catch (IOException e)
         {
@@ -274,12 +289,13 @@
   private static boolean useCache = true;
 
   private static Iterator getReadersByFilter(Class type,
-                                             ServiceRegistry.Filter filter)
+                                             ServiceRegistry.Filter filter,
+                                             Object readerExtension)
   {
     try
       {
         Iterator it = getRegistry().getServiceProviders(type, filter, true);
-        return new ImageReaderIterator(it);
+        return new ImageReaderIterator(it, readerExtension);
       }
     catch (IllegalArgumentException e)
       {
@@ -288,12 +304,13 @@
   }
   
   private static Iterator getWritersByFilter(Class type,
-					     ServiceRegistry.Filter filter)
+					     ServiceRegistry.Filter filter,
+                                             Object writerExtension)
   {
     try
       {
         Iterator it = getRegistry().getServiceProviders(type, filter, true);
-        return new ImageWriterIterator(it);
+        return new ImageWriterIterator(it, writerExtension);
       }
     catch (IllegalArgumentException e)
       {
@@ -312,7 +329,8 @@
       throw new IllegalArgumentException("formatName may not be null");
 
     return getReadersByFilter(ImageReaderSpi.class,
-                              new ReaderFormatFilter(formatName));
+                              new ReaderFormatFilter(formatName),
+                              formatName);
   }
 
   public static Iterator getImageReadersByMIMEType(String MIMEType)
@@ -321,7 +339,8 @@
       throw new IllegalArgumentException("MIMEType may not be null");
 
     return getReadersByFilter(ImageReaderSpi.class,
-                              new ReaderMIMETypeFilter(MIMEType));
+                              new ReaderMIMETypeFilter(MIMEType),
+                              MIMEType);
   }
 
   public static Iterator getImageReadersBySuffix(String fileSuffix)
@@ -330,7 +349,8 @@
       throw new IllegalArgumentException("formatName may not be null");
     
     return getReadersByFilter(ImageReaderSpi.class,
-                              new ReaderSuffixFilter(fileSuffix));
+                              new ReaderSuffixFilter(fileSuffix),
+                              fileSuffix);
   }
 
   public static Iterator getImageWritersByFormatName(String formatName)
@@ -339,7 +359,8 @@
       throw new IllegalArgumentException("formatName may not be null");
     
     return getWritersByFilter(ImageWriterSpi.class,
-                              new WriterFormatFilter(formatName));
+                              new WriterFormatFilter(formatName),
+                              formatName);
   }
 
   public static Iterator getImageWritersByMIMEType(String MIMEType)
@@ -348,7 +369,8 @@
       throw new IllegalArgumentException("MIMEType may not be null");
     
     return getWritersByFilter(ImageWriterSpi.class,
-                              new WriterMIMETypeFilter(MIMEType));
+                              new WriterMIMETypeFilter(MIMEType),
+                              MIMEType);
   }
 
   public static Iterator getImageWritersBySuffix(String fileSuffix)
@@ -357,7 +379,8 @@
       throw new IllegalArgumentException("fileSuffix may not be null");
     
     return getWritersByFilter(ImageWriterSpi.class,
-                              new WriterSuffixFilter(fileSuffix));
+                              new WriterSuffixFilter(fileSuffix),
+                              fileSuffix);
   }
 
   public static String[] getReaderFormatNames()
@@ -496,4 +519,87 @@
   {
     ImageIO.useCache = useCache;
   }
+
+  /* 
+   * "Standard" simplified entry points.
+   */
+
+  public static boolean write(RenderedImage im,
+                              String formatName,
+                              File output)
+    throws IOException
+  {
+    return write(im, formatName, new FileOutputStream(output));
+  }
+
+  public static boolean write(RenderedImage im,
+                              String formatName,
+                              OutputStream output)
+    throws IOException
+  {
+    return write(im, formatName, new MemoryCacheImageOutputStream(output));
+  }
+  
+  
+  public static boolean write(RenderedImage im,
+                              String formatName,
+                              ImageOutputStream output)
+    throws IOException
+  {
+    Iterator writers = getImageWritersByFormatName(formatName);
+    IIOImage img = new IIOImage(im, null, null);
+    while (writers.hasNext())
+      {
+        ImageWriter w = (ImageWriter) writers.next();
+        try 
+          {
+            w.setOutput(output);
+          }
+        catch (IllegalArgumentException e)
+          {
+            continue;
+          }
+        
+        w.write(null, img, null);
+        output.close();
+        return true;
+      }
+    return false;
+  }
+
+  public static BufferedImage read(ImageInputStream stream)
+    throws IOException
+  {
+    Iterator providers = getRegistry().getServiceProviders(ImageReaderSpi.class, true);
+    while (providers.hasNext())
+      {
+        ImageReaderSpi spi = (ImageReaderSpi) providers.next();
+        if (spi.canDecodeInput(stream))
+          {
+            ImageReader reader = spi.createReaderInstance();
+            reader.setInput(stream);
+            return reader.read(0, null);
+          }
+      }
+    return null;
+  }
+        
+  public static BufferedImage read(URL input)
+    throws IOException
+  {
+    return read(input.openStream());
+  }
+
+  public static BufferedImage read(InputStream input)
+    throws IOException
+  {
+    return read(new MemoryCacheImageInputStream(input));
+  }
+
+  public static BufferedImage read(File input)
+    throws IOException
+  {
+    return read(new FileInputStream(input));
+  }
+
 }
--- javax/imageio/ImageReader.java	7 Dec 2004 16:54:48 -0000	1.1.16.2
+++ javax/imageio/ImageReader.java	4 Jan 2005 20:48:00 -0000
@@ -51,6 +51,7 @@
 import javax.imageio.event.IIOReadWarningListener;
 import javax.imageio.metadata.IIOMetadata;
 import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
 
 public abstract class ImageReader
 {
@@ -62,7 +63,7 @@
   protected Locale locale;
   protected int minIndex;
   protected ImageReaderSpi originatingProvider;
-  protected List progressListeners;
+  protected List progressListeners = new ArrayList();
   protected boolean seekForwardOnly;
   protected List updateListeners = new ArrayList();
   protected List warningListeners = new ArrayList();
@@ -156,6 +157,42 @@
   public abstract Iterator getImageTypes(int imageIndex)
     throws IOException;
 
+  public void setInput(Object input,
+                       boolean seekForwardOnly,
+                       boolean ignoreMetadata)
+  {
+    Class[] okClasses = originatingProvider.getInputTypes();
+    if (okClasses == null)
+      {
+        if (!(input instanceof ImageInputStream))
+          throw new IllegalArgumentException();
+      }
+    else
+      {
+        boolean classOk = false;
+        for (int i = 0; i < okClasses.length; ++i)
+          if (okClasses[i].isInstance(input))
+            classOk = true;
+        if (!classOk)
+          throw new IllegalArgumentException();
+      }
+
+    this.input = input;
+    this.seekForwardOnly = seekForwardOnly;
+    this.ignoreMetadata = ignoreMetadata;
+    this.minIndex = 0;
+  }
+
+  public void setInput(Object in, boolean seekForwardOnly)
+  {
+    setInput(in, seekForwardOnly, false);
+  }
+
+  public void setInput(Object in)
+  {
+    setInput(in, false, false);
+  }
+
   public Object getInput()
   {
     return input;
--- javax/imageio/ImageWriter.java	7 Dec 2004 16:54:48 -0000	1.1.16.2
+++ javax/imageio/ImageWriter.java	4 Jan 2005 20:48:00 -0000
@@ -40,6 +40,7 @@
 
 import java.awt.Dimension;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -59,9 +60,9 @@
   protected Locale locale;
   protected ImageWriterSpi originatingProvider;
   protected Object output;
-  protected List progressListeners;
-  protected List warningListeners;
-  protected List warningLocales;
+  protected List progressListeners = new ArrayList();
+  protected List warningListeners = new ArrayList();
+  protected List warningLocales = new ArrayList();
 
   protected ImageWriter(ImageWriterSpi originatingProvider)
   {
@@ -371,11 +372,11 @@
 
 	if (originatingProvider != null)
 	  types = originatingProvider.getOutputTypes();
-	
+        
 	if (types != null)
 	  for (int i = types.length - 1; i >= 0; --i)
-	    if (types[i].equals(output.getClass()))
-	      found = true;
+            if (types[i].isInstance(output))
+              found = true;
 
 	if (! found)
 	  throw new IllegalArgumentException("output type not available");
--- javax/imageio/spi/IIORegistry.java	7 Dec 2004 16:54:50 -0000	1.1.12.1
+++ javax/imageio/spi/IIORegistry.java	4 Jan 2005 20:48:00 -0000
@@ -39,7 +39,9 @@
 package javax.imageio.spi;
 
 import gnu.classpath.ServiceFactory;
+import gnu.java.awt.ClasspathToolkit;
 
+import java.awt.Toolkit;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -52,8 +54,8 @@
 
   static
   {
-    //defaultCategories.add(ImageReaderSpi.class);
-    //defaultCategories.add(ImageWriterSpi.class);
+    defaultCategories.add(ImageReaderSpi.class);
+    defaultCategories.add(ImageWriterSpi.class);
     defaultCategories.add(ImageTranscoderSpi.class);
     defaultCategories.add(ImageInputStreamSpi.class);
     defaultCategories.add(ImageOutputStreamSpi.class);
@@ -78,6 +80,8 @@
     super(defaultCategories.iterator());
 
     // XXX: Register built-in Spis here.
+
+    ((ClasspathToolkit)Toolkit.getDefaultToolkit()).registerImageIOSpis(this);
     
     registerApplicationClasspathSpis();
   }
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c	7 Nov 2004 00:04:27 -0000	1.2.10.6
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c	4 Jan 2005 20:48:00 -0000
@@ -60,6 +60,8 @@
 
 static jmethodID areaPreparedID;
 static jmethodID areaUpdatedID;
+static jmethodID dataOutputWriteID;
+static jmethodID registerFormatID;
 
 static void
 area_prepared (GdkPixbufLoader *loader, 
@@ -193,10 +195,72 @@
   NSA_SET_PB_PTR (env, obj, loader);
 }
 
+static void
+query_formats (JNIEnv *env, jclass clazz)
+{
+  jobject jformat;
+  GSList *formats, *f;
+  GdkPixbufFormat *format;
+  char **ch, *name;
+
+  jclass formatClass;
+  jmethodID addExtensionID;
+  jmethodID addMimeTypeID;
+
+  formatClass = (*env)->FindClass
+    (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec");
+
+  g_assert(formatClass != NULL);
+
+  addExtensionID = (*env)->GetMethodID (env, formatClass, 
+				        "addExtension", 
+					"(Ljava/lang/String;)V");
+
+  addMimeTypeID = (*env)->GetMethodID (env, formatClass, 
+				       "addMimeType", 
+				       "(Ljava/lang/String;)V");
+  
+  formats = gdk_pixbuf_get_formats ();
+
+  for (f = formats; f; f = f->next)
+    {
+      format = (GdkPixbufFormat *) f->data;
+      name = gdk_pixbuf_format_get_name(format);
+
+      jformat = (*env)->CallStaticObjectMethod 
+	(env, clazz, registerFormatID, 				    
+	 (*env)->NewStringUTF(env, name),
+	 (jboolean) gdk_pixbuf_format_is_writable(format));
+
+      g_assert(jformat != NULL);
+      
+      ch = gdk_pixbuf_format_get_extensions(format);
+      while (*ch)
+	{
+	  (*env)->CallVoidMethod (env, jformat, addExtensionID, 
+				  (*env)->NewStringUTF(env, *ch)); 
+	  ++ch;
+	}
+      
+      ch = gdk_pixbuf_format_get_mime_types(format);
+      while (*ch)
+	{
+	  (*env)->CallVoidMethod (env, jformat, addMimeTypeID, 
+				  (*env)->NewStringUTF(env, *ch)); 
+	  ++ch;
+	}
+    }
+  
+  g_slist_free(formats);  
+}
+
+
 JNIEXPORT void JNICALL
 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState 
   (JNIEnv *env, jclass clazz)
 {
+  jclass dataOutputClass;
+
   (*env)->GetJavaVM(env, &vm);
 
   areaPreparedID = (*env)->GetMethodID (env, clazz, 
@@ -206,6 +270,20 @@
   areaUpdatedID = (*env)->GetMethodID (env, clazz,
 				       "areaUpdated",
 				       "(IIII[II)V");
+
+  registerFormatID = (*env)->GetStaticMethodID 
+    (env, clazz, 
+     "registerFormat", 
+     "(Ljava/lang/String;Z)"
+     "Lgnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec;");
+
+  
+  dataOutputClass = (*env)->FindClass(env, "java/io/DataOutput");
+  dataOutputWriteID = (*env)->GetMethodID (env, dataOutputClass,
+					     "write", "([B)V");
+
+  query_formats (env, clazz);
+  
   NSA_PB_INIT (env, clazz);
 }
 
@@ -226,6 +304,115 @@
   gdk_threads_leave (); 
 }
 
+struct stream_save_request
+{
+  JNIEnv *env;
+  jobject *stream;
+};
+
+static gboolean
+save_to_stream(const gchar *buf,
+	       gsize count,
+	       GError **error __attribute__((unused)),
+	       gpointer data)
+{
+  struct stream_save_request *ssr = (struct stream_save_request *)data;
+
+  jbyteArray jbuf;
+  jbyte *cbuf;
+
+  gdk_threads_leave ();
+  jbuf = (*(ssr->env))->NewByteArray ((ssr->env), count);
+  cbuf = (*(ssr->env))->GetByteArrayElements ((ssr->env), jbuf, NULL);
+  memcpy (cbuf, buf, count);
+  (*(ssr->env))->ReleaseByteArrayElements ((ssr->env), jbuf, cbuf, 0);
+  (*(ssr->env))->CallVoidMethod ((ssr->env), *(ssr->stream), 
+				 dataOutputWriteID, jbuf);  
+  gdk_threads_enter ();
+  return TRUE;
+}
+
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage
+(JNIEnv *env, jclass clazz __attribute__((unused)), 
+ jintArray jarr, jstring jenctype, jint width, jint height, 
+ jboolean hasAlpha, jobject stream)
+{
+  GdkPixbuf* pixbuf;  
+  jint *ints;
+  guchar a, r, g, b, *pix, *p;
+  GError *err = NULL;
+  const char *enctype;
+  int i;
+
+  struct stream_save_request ssr;
+  ssr.stream = &stream;
+  ssr.env = env;
+
+  ints = (*env)->GetIntArrayElements (env, jarr, NULL);
+  pix = g_malloc(width * height * (hasAlpha ? 4 : 3));
+
+  enctype = (*env)->GetStringUTFChars (env, jenctype, NULL);
+  g_assert(enctype != NULL);
+
+  g_assert (pix != NULL);
+  g_assert (ints != NULL);
+
+  p = pix;
+  for (i = 0; i < width*height; ++i)
+    {
+      /* 
+       * Java encodes pixels as integers in a predictable arithmetic order:
+       * 0xAARRGGBB. Since these are jints, JNI has already byte-swapped
+       * them for us if necessary, so they're in "our" endianness, whatever
+       * that is. It uses 4 bytes per pixel whether or not there's an alpha
+       * channel.
+       */
+
+      a = 0xff & (ints[i] >> 24);
+      r = 0xff & (ints[i] >> 16);
+      g = 0xff & (ints[i] >> 8);
+      b = 0xff & ints[i];
+
+      /* 
+       * GDK-pixbuf has a very different storage model:
+       *
+       *  - A different alpha order (alpha after colors).
+       *  - A different packing model (no alpha -> 3-bytes-per-pixel).
+       *  - A different "RGB" order (host memory order, not endian-neutral).
+       */
+
+      *p++ = r;
+      *p++ = g;
+      *p++ = b;
+      if (hasAlpha)
+	*p++ = a;
+    }
+
+  gdk_threads_enter ();
+  pixbuf =  gdk_pixbuf_new_from_data (pix,
+				      GDK_COLORSPACE_RGB,
+				      (gboolean) hasAlpha,
+				      8, width, height, 
+				      width * (hasAlpha ? 4 : 3), /* rowstride */
+				      NULL, NULL);
+  g_assert (pixbuf != NULL);
+
+  g_assert(gdk_pixbuf_save_to_callback (pixbuf,
+					&save_to_stream,
+					&ssr,
+					enctype,
+					&err, NULL));
+
+  g_object_unref (pixbuf);
+
+  gdk_threads_leave ();
+  g_free(pix);
+
+  (*env)->ReleaseStringUTFChars (env, jenctype, enctype);  
+  (*env)->ReleaseIntArrayElements (env, jarr, ints, 0);
+}
 
 JNIEXPORT void JNICALL
 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]