12 Replies Latest reply: Jul 15, 2009 4:34 AM by 793415 RSS

    Create animated GIF using imageio

    793415
      How do you create an animated GIF using the J2SE's javax.imageio classes?

      I have been browsing these 3 threads (one of which I participated in) to try and develop an SSCCE of creating an animated GIF.
      [Writing out animated gifs with ImageIO?|http://forums.sun.com/thread.jspa?threadID=5204877]
      [Wirting image metadata to control animated gif delays |http://forums.java.net/jive/thread.jspa?messageID=214284&]
      [Help with GIF writer in imageio|http://www.javakb.com/Uwe/Forum.aspx/java-programmer/32892/Help-with-GIF-writer-in-imageio]
      (pity I did not prompt the OP on that last one, to supply an SSCCE.)

      Unfortunately, my efforts so far have been dismal. Either, without the IIOMetadata object, the GIF has frames with no delay, and it cycles just once, or with the IIOMetadata object I get an error.
      IIOInvalidTreeException: Unknown child of root node!
      Can anyone point me in the right direction?
      import java.awt.*;
      import java.awt.image.*;
      import javax.swing.*;
      import java.io.*;
      import java.net.URL;
      import java.util.Iterator;
      import javax.imageio.*;
      import javax.imageio.metadata.*;
      import javax.imageio.stream.*;
      import org.w3c.dom.Node;
      
      class WriteAnimatedGif {
      
        /** Adapted from code via Brian Burkhalter on
        http://forums.java.net/jive/thread.jspa?messageID=214284& */
        public static Node getRootNode(String delayTime) {
          IIOMetadataNode root =
            new IIOMetadataNode("javax_imageio_gif_stream_1.0");
          IIOMetadataNode gce =
            new IIOMetadataNode("GraphicControlExtension");
          gce.setAttribute("disposalMethod", "none");
          gce.setAttribute("userInputFlag", "FALSE");
          gce.setAttribute("transparentColorFlag", "FALSE");
          gce.setAttribute("delayTime", delayTime);
          gce.setAttribute("transparentColorIndex", "255");
          root.appendChild(gce);
          IIOMetadataNode aes =
            new IIOMetadataNode("ApplicationExtensions");
          IIOMetadataNode ae =
            new IIOMetadataNode("ApplicationExtension");
          ae.setAttribute("applicationID", "NETSCAPE");
          ae.setAttribute("authenticationCode", "2.0");
          byte[] uo = new byte[] {
            (byte)0x21, (byte)0xff, (byte)0x0b,
            (byte)'N', (byte)'E', (byte)'T', (byte)'S',
            (byte)'C', (byte)'A', (byte)'P', (byte)'E',
            (byte)'2', (byte)'.', (byte)'0',
            (byte)0x03, (byte)0x01, (byte)0x00, (byte)0x00,
            (byte)0x00
          };
          ae.setUserObject(uo);
          aes.appendChild(ae);
          root.appendChild(aes);
      
          return root;
        }
      
        /** Adapted from code via GeogffTitmus on 
        http://forums.sun.com/thread.jspa?messageID=9988198 */
        public static File saveAnimate(
          BufferedImage[] frames, 
          String name, 
          String delayTime) throws Exception {
          File file = null;
          file = new File(name+".gif");
      
          Node root = getRootNode(delayTime);
      
          ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
                  
          ImageOutputStream ios = ImageIO.createImageOutputStream(file);
          iw.setOutput(ios);
          //IIOImage ii = new IIOImage(frames[0], null, null);
          //IIOMetadata im = iw.getDefaultStreamMetadata(null);
          //IIOMetadata im = new AnimatedIIOMetadata();
          //im.setFromTree("gif", root);
          iw.prepareWriteSequence(null);
            
          for (int i = 0; i < frames.length; i++) {
            BufferedImage src = frames; 
      ImageWriteParam iwp = iw.getDefaultWriteParam();
      IIOMetadata metadata = iw.getDefaultStreamMetadata(iwp);
      System.out.println( "IIOMetadata: " + metadata );
      //metadata.mergeTree(metadata.getNativeMetadataFormatName(), root);
      metadata.setFromTree(metadata.getNativeMetadataFormatName(), root);
      //Node root = metadata.getAsTree("javax_imageio_gif_image_1.0");

      IIOImage ii = new IIOImage(src, null, metadata);
      iw.writeToSequence( ii, (ImageWriteParam)null);
      }

      iw.endWriteSequence();
      ios.close();

      return file;
      }

      public static void main(String[] args) throws Exception {

      // uncomment the other two if you like, but we can
      // see it work or fail with just the first and last.
      String[] names = {
      "bronze",
      //"silver",
      //"gold",
      "platinum"
      };
      String pre = "http://forums.sun.com/im/";
      String suff = "-star.gif";
      BufferedImage[] frames = new BufferedImage[names.length];
      for (int ii=0; ii<names.length; ii++) {
      URL url = new URL(pre + names[ii] + suff);
      System.out.println(url);
      frames[ii] = ImageIO.read(url);
      }
      File f = saveAnimate(frames, "animatedstars", "500");
      JOptionPane.showMessageDialog( null, new ImageIcon(f.toURI().toURL()) );
      Desktop.getDesktop().open(f);

      ImageInputStream iis = ImageIO.createImageInputStream(f);
      //System.out.println("ImageReader: " + ir1);
      //System.out.println("IIOMetadata: " + ir1.getStreamMetadata());
      Iterator itReaders = ImageIO.getImageReaders(iis);
      while (itReaders.hasNext() ) {
      ImageReader ir = (ImageReader)itReaders.next();
      System.out.println("ImageReader: " + ir);
      System.out.println("IIOMetadata: " + ir.getStreamMetadata());
      }
      }
      }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
        • 1. This Thread is now moved
          DarrylBurke
          Note: This thread was originally posted in the [Java Programming|http://forums.sun.com/forum.jspa?forumID=31] forum, but moved to this forum for closer topic alignment.
          • 2. Re: Create animated GIF using imageio
            843799
            According to imagio's [gif metadata specification|http://java.sun.com/javase/6/docs/api/javax/imageio/metadata/doc-files/gif_metadata.html], the metadata you are specifying is image-specific metadata. The stream metadata is global metadata applicable to all the images.

            So change this,
            IIOMetadataNode root =
                new IIOMetadataNode("javax_imageio_gif_stream_1.0");
            to this,
            IIOMetadataNode root = 
                new IIOMetadataNode("javax_imageio_gif_image_1.0");
            and this,
            IIOMetadata metadata = iw.getDefaultStreamMetadata(iwp);
            System.out.println( "IIOMetadata: " + metadata );
            //metadata.mergeTree(metadata.getNativeMetadataFormatName(), root);
            metadata.setFromTree(metadata.getNativeMetadataFormatName(), root);
            //Node root = metadata.getAsTree("javax_imageio_gif_image_1.0");
            to this,
            IIOMetadata metadata = iw.getDefaultImageMetadata(
                    new ImageTypeSpecifier(src),null);
            System.out.println("IIOMetadata: " + metadata);
            metadata.mergeTree(metadata.getNativeMetadataFormatName(), root);
            Here is your program again, but with the above changes.
            import java.awt.*;
            import java.awt.image.*;
            import javax.swing.*;
            import java.io.*;
            import java.net.URL;
            import java.util.Iterator;
            import javax.imageio.*;
            import javax.imageio.metadata.*;
            import javax.imageio.stream.*;
            import org.w3c.dom.Node;
            
            class WriteAnimatedGif {
            
                /** Adapted from code via Brian Burkhalter on
                http://forums.java.net/jive/thread.jspa?messageID=214284& */
                public static Node getRootNode(String delayTime) {
                    IIOMetadataNode root =
                            new IIOMetadataNode("javax_imageio_gif_image_1.0");
                    IIOMetadataNode gce =
                            new IIOMetadataNode("GraphicControlExtension");
                    gce.setAttribute("disposalMethod", "none");
                    gce.setAttribute("userInputFlag", "FALSE");
                    gce.setAttribute("transparentColorFlag", "FALSE");
                    gce.setAttribute("delayTime", delayTime);
                    gce.setAttribute("transparentColorIndex", "255");
                    root.appendChild(gce);
                    IIOMetadataNode aes =
                            new IIOMetadataNode("ApplicationExtensions");
                    IIOMetadataNode ae =
                            new IIOMetadataNode("ApplicationExtension");
                    ae.setAttribute("applicationID", "NETSCAPE");
                    ae.setAttribute("authenticationCode", "2.0");
                    byte[] uo = new byte[]{
                        (byte) 0x21, (byte) 0xff, (byte) 0x0b,
                        (byte) 'N', (byte) 'E', (byte) 'T', (byte) 'S',
                        (byte) 'C', (byte) 'A', (byte) 'P', (byte) 'E',
                        (byte) '2', (byte) '.', (byte) '0',
                        (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00
                    };
                    ae.setUserObject(uo);
                    aes.appendChild(ae);
                    root.appendChild(aes);
            
                    return root;
                }
            
                /** Adapted from code via GeogffTitmus on
                http://forums.sun.com/thread.jspa?messageID=9988198 */
                public static File saveAnimate(
                        BufferedImage[] frames,
                        String name,
                        String delayTime) throws Exception {
                    File file = null;
                    file = new File(name + ".gif");
            
                    Node root = getRootNode(delayTime);
            
                    ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
            
                    ImageOutputStream ios = ImageIO.createImageOutputStream(file);
                    iw.setOutput(ios);
                    //IIOImage ii = new IIOImage(frames[0], null, null);
                    //IIOMetadata im = iw.getDefaultStreamMetadata(null);
                    //IIOMetadata im = new AnimatedIIOMetadata();
                    //im.setFromTree("gif", root);
                    iw.prepareWriteSequence(null);
            
                    for (int i = 0; i < frames.length; i++) {
                        BufferedImage src = frames;
            ImageWriteParam iwp = iw.getDefaultWriteParam();
            IIOMetadata metadata = iw.getDefaultImageMetadata(
            new ImageTypeSpecifier(src), null);
            System.out.println("IIOMetadata: " + metadata);
            metadata.mergeTree(metadata.getNativeMetadataFormatName(), root);

            IIOImage ii = new IIOImage(src, null, metadata);
            iw.writeToSequence(ii, (ImageWriteParam) null);
            }

            iw.endWriteSequence();
            ios.close();

            return file;
            }

            public static void main(String[] args) throws Exception {

            // uncomment the other two if you like, but we can
            // see it work or fail with just the first and last.
            String[] names = {
            "bronze",
            //"silver",
            //"gold",
            "platinum"
            };
            String pre = "http://forums.sun.com/im/";
            String suff = "-star.gif";
            BufferedImage[] frames = new BufferedImage[names.length];
            for (int ii = 0; ii < names.length; ii++) {
            URL url = new URL(pre + names[ii] + suff);
            System.out.println(url);
            frames[ii] = ImageIO.read(url);
            }
            File f = saveAnimate(frames, "animatedstars", "500");
            JOptionPane.showMessageDialog(null, new ImageIcon(f.toURI().toURL()));
            Desktop.getDesktop().open(f);

            ImageInputStream iis = ImageIO.createImageInputStream(f);
            //System.out.println("ImageReader: " + ir1);
            //System.out.println("IIOMetadata: " + ir1.getStreamMetadata());
            Iterator itReaders = ImageIO.getImageReaders(iis);
            while (itReaders.hasNext()) {
            ImageReader ir = (ImageReader) itReaders.next();
            System.out.println("ImageReader: " + ir);
            System.out.println("IIOMetadata: " + ir.getStreamMetadata());
            }
            }
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
            • 3. Re: Create animated GIF using imageio
              793415
              Thanks for your help, time and expertise. Your alterations got rid of the error.

              But I am still not seeing animated GIFs here.

              After some slight tweaks to the code (mostly minor, but documented in the [latest source|http://pscode.org/test/animgif/WriteAnimatedGif.java] at my site), this is my output from the latest runs.
              andrew@pc1:/media/disk/projects/junk/numbered/all/004WriteAnimatedGif$ /usr/lib/jvm/java-6-sun-1.6.0.10/jre/bin/java WriteAnimatedGif animated-sun.gif
              java.vendor      Sun Microsystems Inc.
              java.version      1.6.0_10
              java.vm.version      11.0-b15
              os.name      Linux
              os.version      2.6.27-14-generic
              http://forums.sun.com/im/bronze-star.gif
              http://forums.sun.com/im/platinum-star.gif
              andrew@pc1:/media/disk/projects/junk/numbered/all/004WriteAnimatedGif$ /usr/lib/jvm/java-6-openjdk/jre/bin/java WriteAnimatedGif animated-open.gif
              java.vendor      Sun Microsystems Inc.
              java.version      1.6.0_0
              java.vm.version      1.6.0_0-b12
              os.name      Linux
              os.version      2.6.27-14-generic
              http://forums.sun.com/im/bronze-star.gif
              http://forums.sun.com/im/platinum-star.gif
              andrew@pc1:/media/disk/projects/junk/numbered/all/004WriteAnimatedGif$ 
              And here are the results.

              [http://pscode.org/test/animgif/animated-sun.gif|http://pscode.org/test/animgif/animated-sun.gif]
              [http://pscode.org/test/animgif/animated-open.gif|http://pscode.org/test/animgif/animated-open.gif]

              When I browse to these using FF (or the Ubuntu Image Viewer, or put them in a JLabel) they still show no delay, and do not cycle.

              Edit 1:

              Thanks DB for moving the thread. I was not entirely sure which forum would be the 'best fit' for this question. You moving the thread, answered that question neatly. ;-)

              Edited by: AndrewThompson64 on Jul 2, 2009 10:23 PM
              • 4. Re: Create animated GIF using imageio
                843799
                Ok, so I took a closer look at your code. In your new code, this
                metadata.mergeTree(metadata.getNativeMetadataFormatName(), root);
                was changed back to this,
                metadata.setFromTree(metadata.getNativeMetadataFormatName(), root);
                I imagine you did that because the former method doesn't preserve the transparency. Here's a snapshot of the metadata for the first image both before and after calling setFromTree.
                javax_imageio_gif_image_1.0 
                   ImageDescriptor 
                      imageLeftPosition - "0" 
                      imageTopPosition - "0" 
                      imageWidth - "16" 
                      imageHeight - "16" 
                      interlaceFlag - "true" 
                   LocalColorTable 
                      sizeOfLocalColorTable - "256" 
                      sortFlag - "FALSE" 
                   //color table entries 
                   GraphicControlExtension 
                      disposalMethod - "none" 
                      userInputFlag - "false" 
                      transparentColorFlag - "true" 
                      delayTime - "0" 
                      transparentColorIndex - "31" 
                ------------------------------ 
                javax_imageio_gif_image_1.0 
                   ImageDescriptor 
                      imageLeftPosition - "0" 
                      imageTopPosition - "0" 
                      imageWidth - "0" 
                      imageHeight - "0" 
                      interlaceFlag - "false" 
                   GraphicControlExtension 
                      disposalMethod - "none" 
                      userInputFlag - "false" 
                      transparentColorFlag - "false" 
                      delayTime - "50" 
                      transparentColorIndex - "255" 
                   ApplicationExtensions 
                      ApplicationExtension 
                         applicationID - "NETSCAPE" 
                         authenticationCode - "2.0";
                The setFromTree method calls reset() before calling mergeTree. The reset in turn puts the metadata in the state it was at creation. As a result, notice that the imageWidth and imageHeight are now 0. The interlace flag was set to false, and the color table was lost. The delayTime was indeed set to half a second, but the new metadata as a whole is now incomplete. I think the ImageWriter sees this and does some things that cause the changes you made to be lost. I'm not entirely sure though, since the ImageWriter doesn't post any warnings to an IIOWriteWarningListener I added to it.

                Here is a snapshot of the metadata before and after calling mergeTree istead of setFromTree.
                javax_imageio_gif_image_1.0 
                   ImageDescriptor 
                      imageLeftPosition - "0" 
                      imageTopPosition - "0" 
                      imageWidth - "16" 
                      imageHeight - "16" 
                      interlaceFlag - "true" 
                   LocalColorTable 
                      sizeOfLocalColorTable - "256" 
                      sortFlag - "FALSE"
                   //color table entries
                   GraphicControlExtension 
                      disposalMethod - "none" 
                      userInputFlag - "false" 
                      transparentColorFlag - "true" 
                      delayTime - "0" 
                      transparentColorIndex - "31" 
                ------------------------------ 
                javax_imageio_gif_image_1.0 
                   ImageDescriptor 
                      imageLeftPosition - "0" 
                      imageTopPosition - "0" 
                      imageWidth - "16" 
                      imageHeight - "16" 
                      interlaceFlag - "true" 
                   LocalColorTable 
                      sizeOfLocalColorTable - "256" 
                      sortFlag - "FALSE" 
                   //color table entries 
                   GraphicControlExtension 
                      disposalMethod - "none" 
                      userInputFlag - "false" 
                      transparentColorFlag - "false" 
                      delayTime - "50" 
                      transparentColorIndex - "255" 
                   ApplicationExtensions 
                      ApplicationExtension 
                         applicationID - "NETSCAPE" 
                         authenticationCode - "2.0";
                The imageWidth, imageHeight, interlaceFlag, and color table all have the same values as before. The new metadata object is complete (so I don't think the ImageWriter does anything to overwrite your values). The problem now, though, is that we set the transparentColorFlag to false and the transparentColorIndex to 255. We had to specify the transparency to make a valid GraphicControlExtension node, but we didn't know what the transparency was. The only way to fix this is to modify the existing GraphicControlExtension node instead of creating a new one.

                When I made this change in addition to using mergeTree, the animation played through with correct transparency. But, it only played through once. It took a while to figure out what was wrong, but apparenly your ApplicationExtension node is incorrect. The user object should only be three bytes - 0x01, 0x00, 0x00. The last two bytes is the unsigned short (little endian) that represents the number of times to loop. 0 means loop forever.

                I'll post working code of writing the gif file in my post below. I hit the character limit.
                • 5. Re: Create animated GIF using imageio
                  843799
                  Here's working code.
                  import java.awt.*;
                  import java.awt.image.*;
                  import javax.swing.*;
                  import java.io.*;
                  import java.net.URL;
                  import javax.imageio.*;
                  import javax.imageio.metadata.*;
                  import javax.imageio.stream.*;
                  import org.w3c.dom.Node;
                  
                  class WriteAnimatedGif {
                      public static void configure(IIOMetadata meta,
                                                   String delayTime,
                                                   int imageIndex) {
                          String metaFormat = meta.getNativeMetadataFormatName();
                  
                          if (!"javax_imageio_gif_image_1.0".equals(metaFormat)) {
                              throw new IllegalArgumentException(
                                      "Unfamiliar gif metadata format: " + metaFormat);
                          }
                  
                          Node root = meta.getAsTree(metaFormat);
                  
                          //find the GraphicControlExtension node
                          Node child = root.getFirstChild();
                          while (child != null) {
                              if ("GraphicControlExtension".equals(child.getNodeName())) {
                                  break;
                              }
                              child = child.getNextSibling();
                          }
                  
                          IIOMetadataNode gce = (IIOMetadataNode) child;
                          gce.setAttribute("userInputFlag", "FALSE");
                          gce.setAttribute("delayTime", delayTime);
                  
                          //only the first node needs the ApplicationExtensions node
                          if (imageIndex == 0) {
                              IIOMetadataNode aes =
                                      new IIOMetadataNode("ApplicationExtensions");
                              IIOMetadataNode ae =
                                      new IIOMetadataNode("ApplicationExtension");
                              ae.setAttribute("applicationID", "NETSCAPE");
                              ae.setAttribute("authenticationCode", "2.0");
                              byte[] uo = new byte[]{
                                  //last two bytes is an unsigned short (little endian) that
                                  //indicates the the number of times to loop.
                                  //0 means loop forever.
                                  0x1, 0x0, 0x0
                              };
                              ae.setUserObject(uo);
                              aes.appendChild(ae);
                              root.appendChild(aes);
                          }
                  
                          try {
                              meta.setFromTree(metaFormat, root);
                          } catch (IIOInvalidTreeException e) {
                              //shouldn't happen
                              throw new Error(e);
                          }
                      }
                  
                      /** Adapted from code via GeoffTitmus on
                      http://forums.sun.com/thread.jspa?messageID=9988198 */
                      public static void saveAnimate(
                              BufferedImage[] frames,
                              //should ideally be an array -one for each frame
                              String delayTime,
                              File file) throws Exception {
                  
                          ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
                  
                          ImageOutputStream ios = ImageIO.createImageOutputStream(file);
                          iw.setOutput(ios);
                          iw.prepareWriteSequence(null);
                  
                          for (int i = 0; i < frames.length; i++) {
                              BufferedImage src = frames;
                  ImageWriteParam iwp = iw.getDefaultWriteParam();
                  IIOMetadata metadata = iw.getDefaultImageMetadata(
                  new ImageTypeSpecifier(src), iwp);

                  configure(metadata, delayTime, i);

                  IIOImage ii = new IIOImage(src, null, metadata);
                  iw.writeToSequence(ii, (ImageWriteParam) null);
                  }

                  iw.endWriteSequence();
                  ios.close();
                  }
                  public static void main(String[] args) throws Exception {

                  String[] props = {
                  "java.vendor",
                  "java.version",
                  "java.vm.version",
                  "os.name",
                  "os.version"
                  };

                  StringBuffer sb = new StringBuffer();
                  String eol = System.getProperty("line.separator");
                  for (String prop : props) {
                  sb.append(prop);
                  sb.append(" \t");
                  sb.append(System.getProperty(prop));
                  sb.append(eol);
                  }
                  System.out.print(sb);

                  String name = "animatedstars.gif";
                  // alow a runtime argument to provide the OP file name
                  if (args.length > 0) {
                  name = args[0];
                  }
                  String[] names = {
                  "bronze",
                  "silver",
                  "gold",
                  "platinum"
                  };
                  String pre = "http://forums.sun.com/im/";
                  String suff = "-star.gif";
                  BufferedImage[] frames = new BufferedImage[names.length];
                  for (int ii = 0; ii < names.length; ii++) {
                  URL url = new URL(pre + names[ii] + suff);
                  System.out.println(url);
                  frames[ii] = ImageIO.read(url);
                  }
                  File f = new File(name);
                  // ensure we are getting a fresh file
                  if (f.exists()) {
                  if (!f.delete()) {
                  System.err.println("Cannot delete file! " + f);
                  System.exit(1);
                  }
                  }
                  f.createNewFile();

                  // save an animated GIF at 2 FPS.
                  saveAnimate(frames, "50", f);

                  JOptionPane.showMessageDialog(null, new ImageIcon(f.toURI().toURL()));
                  // show it in the system default viewer
                  Desktop.getDesktop().open(f);
                  }
                  }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
                  • 6. Re: Create animated GIF using imageio
                    793415
                    >
                    Here's working code.>
                    Champion! Tested it here, and finally saw a looping animated GIF with expected delays (I am just about to knock off for the day, but will look over the code more closely tomorrow).

                    Thanks very much for your help. :-)
                    • 7. Re: Create animated GIF using imageio
                      793415
                      After a careful examination of the code, I am even more happy with your result. I was originally thinking that code solved the problem of both the animation delayTime and looping, but the last thing to figure was how to get different frame rates for the individual frames.

                      A closer look at the code today, realising you were applying the IIOParams on a per image basis, and a quick tweak to the code showed me that the delayTime can also be set on a 'per frame' basis. Cool!

                      And to something you mentioned earlier ..
                      >
                      ...
                      I imagine you did that because the former method doesn't preserve the transparency. >
                      LOL! You have an active imagination! Or perhaps you were just being very diplomatic. ;-)

                      I did that because I had no idea what I was doing, and was trying 'pushing and poking' every variant I could think of, in the vain hope that it would suddenly start working (by magic). I had meant to add params. to easily test each method, but forgot.
                      • 8. Re: Create animated GIF using imageio
                        793415
                        >
                        How do you create an animated GIF using the J2SE's javax.imageio classes?>
                        This is the final result of the code corrected by Maxideon, with a few tweaks to make it better suited to being used as a general utility.

                        The code now accepts 3 arguments. The 1st argument is an output file name. The 2nd argument is a comma separated list of input file names (frames). The 3rd argument is either a single integer delayTime (used for all frames) or a comma separate list of delayTimes.

                        It is too large for a single post, so I will do in in two parts. Just add the 2 parts together before compiling.

                        Part 1:
                        import java.awt.image.BufferedImage;
                        import java.io.File;
                        import org.w3c.dom.Node;
                        
                        import javax.imageio.*;
                        import javax.imageio.metadata.*;
                        import javax.imageio.stream.ImageOutputStream;
                        
                        /** Creates an animated GIF from GIF frames.  A thin wrapper to code
                        written by other people, as documented on the thread on the Sun forums
                        'Create animated GIF using imageio'
                        http://forums.sun.com/thread.jspa?threadID=5395006
                        See the printUsage() method for details on paramaters required.
                        @author Andrew Thompson  */ 
                        class WriteAnimatedGif {
                        
                          /** See http://forums.sun.com/thread.jspa?messageID=10755673#10755673 
                          @author Maxideon
                          @param delayTime String Frame delay for this frame. */
                          public static void configure(IIOMetadata meta,
                            String delayTime,
                            int imageIndex) {
                        
                            String metaFormat = meta.getNativeMetadataFormatName();
                             
                            if (!"javax_imageio_gif_image_1.0".equals(metaFormat)) {
                              throw new IllegalArgumentException(
                                "Unfamiliar gif metadata format: " + metaFormat);
                            }
                             
                            Node root = meta.getAsTree(metaFormat);
                             
                            //find the GraphicControlExtension node
                            Node child = root.getFirstChild();
                            while (child != null) {
                              if ("GraphicControlExtension".equals(child.getNodeName())) {
                                break;
                              }
                              child = child.getNextSibling();
                            }
                             
                            IIOMetadataNode gce = (IIOMetadataNode) child;
                            gce.setAttribute("userDelay", "FALSE");
                            gce.setAttribute("delayTime", delayTime);
                             
                            //only the first node needs the ApplicationExtensions node
                            if (imageIndex == 0) {
                              IIOMetadataNode aes =
                                new IIOMetadataNode("ApplicationExtensions");
                              IIOMetadataNode ae =
                                new IIOMetadataNode("ApplicationExtension");
                              ae.setAttribute("applicationID", "NETSCAPE");
                              ae.setAttribute("authenticationCode", "2.0");
                              byte[] uo = new byte[]{
                                //last two bytes is an unsigned short (little endian) that
                                //indicates the the number of times to loop.
                                //0 means loop forever.
                                0x1, 0x0, 0x0
                              };
                              ae.setUserObject(uo);
                              aes.appendChild(ae);
                              root.appendChild(aes);
                            }
                             
                            try {
                              meta.setFromTree(metaFormat, root);
                            } catch (IIOInvalidTreeException e) {
                              //shouldn't happen
                              throw new Error(e);
                            }
                          }
                         
                          /** See http://forums.sun.com/thread.jspa?messageID=9988198 
                          @author GeoffTitmus
                          @param file File A File in which to store the animation.
                          @param frames BufferedImage[] Array of BufferedImages, the frames of the animation.
                          @param delayTimes String[] Array of Strings, representing the frame delay times. */
                          public static void saveAnimate(
                            File file, 
                            BufferedImage[] frames,
                            String[] delayTimes ) throws Exception {
                             
                            ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
                             
                            ImageOutputStream ios = ImageIO.createImageOutputStream(file);
                            iw.setOutput(ios);
                            iw.prepareWriteSequence(null);
                        
                            for (int i = 0; i < frames.length; i++) {
                              BufferedImage src = frames;
                        ImageWriteParam iwp = iw.getDefaultWriteParam();
                        IIOMetadata metadata = iw.getDefaultImageMetadata(
                        new ImageTypeSpecifier(src), iwp);

                        configure(metadata, delayTimes[i], i);

                        IIOImage ii = new IIOImage(src, null, metadata);
                        iw.writeToSequence(ii, null);
                        }

                        iw.endWriteSequence();
                        ios.close();
                        }

                        // ...
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                        • 9. Re: Create animated GIF using imageio
                          793415
                          Part 2:
                          // ...
                            /** Dump the usage to the System.err stream. */
                            public static void printUsage() {
                              StringBuffer sb = new StringBuffer();
                              String eol = System.getProperty("line.separator");
                              sb.append( "Usage: 2 forms each using 3 arguments" );
                              sb.append( eol );
                              sb.append( "1) output (animated GIF) file name" );
                              sb.append( eol );
                              sb.append( "2) input files (animation frames), separated by ','" );
                              sb.append( eol );
                              sb.append( "3) single frame rate, or comma separared list of frame rates" );
                              sb.append( eol );
                              sb.append( "java WriteAnimatedGif animate.gif frm1.gif,frm2.gif,..,frmN.gif 100" );
                              sb.append( eol );
                              sb.append( "java WriteAnimatedGif animate.gif frm1.gif,frm2.gif,..,frmN.gif 100,40,..,N" );
                              sb.append( eol );
                              sb.append( "The 2nd form must have exactly as many integers as there are frames." );
                              sb.append( eol );
                              sb.append( "Frame rates are specified in increments of 1/100th second, NOT milliseconds." );
                              sb.append( eol );
                          
                              System.err.print(sb);
                            } 
                          
                            /** Checks that a String intended as a delayTime is an integer>0.  If not, 
                            dumps a warning message and the usage, then exits. If successful, returns 
                            the String unaltered. */
                            public static String checkDelay(String delay) {
                              try {
                                int val = Integer.parseInt(delay);
                                if (val<1) {
                                  System.err.println(
                                    "Animation frame delay '" + 
                                    val + 
                                    "' is < 1!");
                                  printUsage();
                                  System.exit(1);
                                }
                              } catch(NumberFormatException nfe) {
                                System.err.println(
                                  "Could not parse '" + 
                                  delay + 
                                  "' as an integer.");
                                printUsage();
                                System.exit(1);
                              }
                              return delay;
                            }
                          
                            /** Parse the arguments and if successful, attempt to write the animated GIF. */
                            public static void main(String[] args) throws Exception {
                           
                              if (args.length!=3) {
                                printUsage();
                                System.exit(1);
                              }
                          
                              // deal with the output file name
                              File f = new File(args[0]);
                          
                              // deal with the input file names
                              String[] names = args[1].split(",");
                              if (names.length<2) {
                                System.err.println("An animation requires 2 or more frames!");
                                printUsage();
                                System.exit(1);
                              }
                              BufferedImage[] frames = new BufferedImage[names.length];
                              for (int ii = 0; ii < names.length; ii++) {
                                frames[ii] = ImageIO.read(new File(names[ii]));
                              }
                          
                              // deal with the frame rates
                              String[] delays = args[2].split(",");
                              // note: length of names, not delays
                              String[] delayTimes = new String[names.length];
                              if (delays.length!=names.length) {
                                System.err.println( delays.length + 
                                  " delays specified for " + 
                                  names.length + 
                                  " frames!" );
                                printUsage();
                                System.exit(1);
                              } else if (delays.length==1) {
                                for (int ii=0; ii<delayTimes.length; ii++) {
                                  // fill all values with the single delayTime
                                  delayTimes[ii] = checkDelay(delays[0]);
                                }
                              } else {
                                for (int ii=0; ii<delayTimes.length; ii++) {
                                  delayTimes[ii] = checkDelay(delays[ii]);
                                }
                              }
                          
                              // save an animated GIF
                              saveAnimate(f, frames, delayTimes);
                            }
                          }
                          Edit 1:
                          microseconds -> milliseconds in usage notes!
                          Also forgot to document the int imageIndex arg of the configure method. It is the index of the animation frame being written.

                          Edited by: AndrewThompson64 on Jul 4, 2009 3:15 AM
                          • 10. Re: Create animated GIF using imageio
                            793415
                            AndrewThompson64 wrote:
                            >
                            How do you create an animated GIF using the J2SE's javax.imageio classes?>
                            This is the final result of the code ...
                            Now transformed into GIFanim, available as a sand-boxed applet/application.
                            • 11. Re: Create animated GIF using imageio
                              843799
                              Hi Andrew,

                              Thanks for the code!

                              What should be changes in the above code if my input images (frames) are in 'jpg' format?

                              I have tried to do some changes in the code but it is not working.

                              Regards,

                              Kamlesh
                              • 12. Re: Create animated GIF using imageio
                                793415
                                volatile wrote:
                                ...
                                Thanks for the code!
                                Those thanks are misplaced. I thought I had taken effort to indicate who actually was responsible for the code.
                                volatile wrote:
                                What should be changes in the above code if my input images (frames) are in 'jpg' format?
                                A couple of questions for you.

                                Are the JPEGs of identical size (WxH)?
                                Can you successfully make an animated GIF of them using GIFanim?
                                Did you note the part of the GIFanim help that discusses [image types|http://pscode.org/gifanim/help.html#open]?

                                The reason I ask is that I had tried the code with JPEG images, the 3rd and 4th of [these images|http://pscode.org/media/#image], and it seemed to work 'just fine'. Since GIFanim is closely based on that code, I expect both would work with JPEGs.
                                volatile wrote:
                                I have tried to do some changes in the code but it is not working
                                Gee, can you vague that up for me? In order to help with the problem, we would need to see the actual changes made to the code, the compilation or run-time error, and/or an exact description of the nature of the problem with the generated GIF.