Index: gnu/java/awt/ClasspathToolkit.java =================================================================== RCS file: /cvsroot/classpath/classpath/gnu/java/awt/ClasspathToolkit.java,v retrieving revision 1.7 diff -u -r1.7 ClasspathToolkit.java --- gnu/java/awt/ClasspathToolkit.java 11 Jan 2005 15:06:03 -0000 1.7 +++ gnu/java/awt/ClasspathToolkit.java 11 Jan 2005 22:05:12 -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); Index: gnu/java/awt/image/ImageDecoder.java =================================================================== RCS file: /cvsroot/classpath/classpath/gnu/java/awt/image/ImageDecoder.java,v retrieving revision 1.12 diff -u -r1.12 ImageDecoder.java --- gnu/java/awt/image/ImageDecoder.java 31 Jul 2004 17:15:13 -0000 1.12 +++ gnu/java/awt/image/ImageDecoder.java 11 Jan 2005 22:05:12 -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) { Index: gnu/java/awt/peer/gtk/GdkGraphics2D.java =================================================================== RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java,v retrieving revision 1.28 diff -u -r1.28 GdkGraphics2D.java --- gnu/java/awt/peer/gtk/GdkGraphics2D.java 26 Dec 2004 13:35:47 -0000 1.28 +++ gnu/java/awt/peer/gtk/GdkGraphics2D.java 11 Jan 2005 22:05:12 -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]); } } Index: gnu/java/awt/peer/gtk/GdkPixbufDecoder.java =================================================================== RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java,v retrieving revision 1.8 diff -u -r1.8 GdkPixbufDecoder.java --- gnu/java/awt/peer/gtk/GdkPixbufDecoder.java 26 Dec 2004 13:35:47 -0000 1.8 +++ gnu/java/awt/peer/gtk/GdkPixbufDecoder.java 11 Jan 2005 22:05:12 -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 (); } - - } Index: gnu/java/awt/peer/gtk/GtkToolkit.java =================================================================== RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java,v retrieving revision 1.62 diff -u -r1.62 GtkToolkit.java --- gnu/java/awt/peer/gtk/GtkToolkit.java 11 Jan 2005 15:06:03 -0000 1.62 +++ gnu/java/awt/peer/gtk/GtkToolkit.java 11 Jan 2005 22:05:12 -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); Index: include/gnu_java_awt_peer_gtk_GdkPixbufDecoder.h =================================================================== RCS file: /cvsroot/classpath/classpath/include/gnu_java_awt_peer_gtk_GdkPixbufDecoder.h,v retrieving revision 1.4 diff -u -r1.4 gnu_java_awt_peer_gtk_GdkPixbufDecoder.h --- include/gnu_java_awt_peer_gtk_GdkPixbufDecoder.h 28 May 2004 17:27:51 -0000 1.4 +++ include/gnu_java_awt_peer_gtk_GdkPixbufDecoder.h 11 Jan 2005 22:05:12 -0000 @@ -14,6 +14,7 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initState (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes (JNIEnv *env, jobject, jbyteArray, jint); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish (JNIEnv *env, jobject); +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage (JNIEnv *env, jclass, jintArray, jstring, jint, jint, jboolean, jobject); #ifdef __cplusplus } Index: javax/imageio/ImageIO.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/imageio/ImageIO.java,v retrieving revision 1.3 diff -u -r1.3 ImageIO.java --- javax/imageio/ImageIO.java 15 Nov 2004 14:13:26 -0000 1.3 +++ javax/imageio/ImageIO.java 11 Jan 2005 22:05:12 -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)); + } + } Index: javax/imageio/ImageReader.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/imageio/ImageReader.java,v retrieving revision 1.3 diff -u -r1.3 ImageReader.java --- javax/imageio/ImageReader.java 5 Oct 2004 10:27:03 -0000 1.3 +++ javax/imageio/ImageReader.java 11 Jan 2005 22:05:13 -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; Index: javax/imageio/ImageWriter.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/imageio/ImageWriter.java,v retrieving revision 1.4 diff -u -r1.4 ImageWriter.java --- javax/imageio/ImageWriter.java 20 Oct 2004 09:20:30 -0000 1.4 +++ javax/imageio/ImageWriter.java 11 Jan 2005 22:05:13 -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"); Index: javax/imageio/spi/IIORegistry.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/imageio/spi/IIORegistry.java,v retrieving revision 1.3 diff -u -r1.3 IIORegistry.java --- javax/imageio/spi/IIORegistry.java 30 Dec 2004 18:49:21 -0000 1.3 +++ javax/imageio/spi/IIORegistry.java 11 Jan 2005 22:05:13 -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; @@ -78,6 +80,8 @@ super(defaultCategories.iterator()); // XXX: Register built-in Spis here. + + ((ClasspathToolkit)Toolkit.getDefaultToolkit()).registerImageIOSpis(this); registerApplicationClasspathSpis(); } Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c =================================================================== RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c,v retrieving revision 1.10 diff -u -r1.10 gnu_java_awt_peer_gtk_GdkPixbufDecoder.c --- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c 28 Oct 2004 20:58:25 -0000 1.10 +++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c 11 Jan 2005 22:05:13 -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