22397
下記が現在のソースです

```

    package creategif;
    
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Rectangle;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.Iterator;
    import javax.imageio.IIOImage;
    import javax.imageio.ImageIO;
    import javax.imageio.ImageWriter;
    import javax.imageio.stream.ImageOutputStream;
    import javax.swing.JOptionPane;
    
    /**
     * メインクラスです.
     * @author udon
     */
    public class CreateGif {
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            File file = new File(System.getProperty("user.dir") + File.separator + "anime.gif");
            try {
                //書き込みストリームの作成
                Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("gif");
                ImageWriter writer = it.hasNext() ? it.next() : null;
                ImageOutputStream stream = ImageIO.createImageOutputStream(file);
                if(writer == null) {
                    throw new IOException();
                }
                writer.setOutput(stream);
                writer.prepareWriteSequence(null);
                //画像の作成
                BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB);
                Rectangle rect = new Rectangle(16, 16, 8, 8);
                final Color FG = Color.WHITE;
                final Color BG = Color.BLACK;
                //50回□を動かすアニメーション
                for(int i=0; i<50; i++) {
                    Graphics g = image.createGraphics();
                    //背景を塗りつぶす
                    g.setColor(BG);
                    g.fillRect(0, 0, 128, 128);
                    //□を描画
                    g.setColor(FG);
                    g.fillRect(rect.x, rect.y, rect.width, rect.height);
                    //動かす
                    rect.x += 2;
                    //フレームの追加
                    writer.writeToSequence(new IIOImage(image, null, null), null);
                }
                writer.endWriteSequence();
                stream.close();
            } catch(IOException ioe) {
                JOptionPane.showMessageDialog(null, "失敗しました\n" + ioe.getMessage());
            }
        }
    }
    
```

また、下記の環境でアニメーション再生されることも確認済みです

```

    Chrome 48.0.2564.116 m
    IE 11.0.9600.18205
    
```

ただこれはアニメーションを一通り再生するとそのまま停止してしまうのですが、  
これを無限にループ再生させる方法はありますか？
CC BY-SA 3.0
2016-02-22T10:56:09.667
22392
2016-02-23T12:44:17.343
2016-02-23T12:44:17.343
46
10677
question
java
ループ再生されるGIF画像の作り方
[Create animated GIF using imageio | Oracle
Community](https://community.oracle.com/thread/1264385)を参考にして、以下のような`IIOMetadata`を書き込むようにすると、ループ可能になるようです。

```

    //Create animated GIF using imageio | Oracle Community
    //https://community.oracle.com/thread/1264385
    ImageWriteParam iwp = writer.getDefaultWriteParam();
    IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(image), iwp);
    String metaFormat = metadata.getNativeMetadataFormatName();
    Node root = metadata.getAsTree(metaFormat);
    IIOMetadataNode aes = new IIOMetadataNode("ApplicationExtensions");
    IIOMetadataNode ae = new IIOMetadataNode("ApplicationExtension");
    ae.setAttribute("applicationID", "NETSCAPE");
    ae.setAttribute("authenticationCode", "2.0");
    byte[] uo = {
      //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);
    metadata.setFromTree(metaFormat, root);
    
    //...
    for (int i = 0; i < 50; i++) {
      //...
      //フレームの追加
      writer.writeToSequence(new IIOImage(image, null, metadata), null);
      metadata = null; //先頭フレームだけで良い？
    
```

[GIFメタデータ形式の仕様](http://docs.oracle.com/javase/jp/8/docs/api/javax/imageio/metadata/doc-
files/gif_metadata.html)を見ればもっと詳しい仕様が書いてあるかもと思ったのですが、ここではアニメーションのループは良く分からないですね...。

**CreateGif2.java**

```

    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;
    
    public class CreateGif2 {
      public static void main(String[] args) {
        File file = new File(System.getProperty("user.dir") + File.separator + "anime.gif");
        try {
          //書き込みストリームの作成
          Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("gif");
          ImageWriter writer = it.hasNext() ? it.next() : null;
          ImageOutputStream stream = ImageIO.createImageOutputStream(file);
          if (writer == null) {
            throw new IOException();
          }
          writer.setOutput(stream);
          writer.prepareWriteSequence(null);
          //画像の作成
          BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB);
    
          //Create animated GIF using imageio | Oracle Community
          //https://community.oracle.com/thread/1264385
          ImageWriteParam iwp = writer.getDefaultWriteParam();
          IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(image), iwp);
          String metaFormat = metadata.getNativeMetadataFormatName();
          Node root = metadata.getAsTree(metaFormat);
          IIOMetadataNode aes = new IIOMetadataNode("ApplicationExtensions");
          IIOMetadataNode ae = new IIOMetadataNode("ApplicationExtension");
          ae.setAttribute("applicationID", "NETSCAPE");
          ae.setAttribute("authenticationCode", "2.0");
          byte[] uo = {
            //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);
          metadata.setFromTree(metaFormat, root);
    
          Rectangle rect = new Rectangle(16, 16, 8, 8);
          final Color FG = Color.WHITE;
          final Color BG = Color.BLACK;
          //50回□を動かすアニメーション
          for (int i = 0; i < 50; i++) {
            Graphics g = image.createGraphics();
            //背景を塗りつぶす
            g.setColor(BG);
            g.fillRect(0, 0, 128, 128);
            //□を描画
            g.setColor(FG);
            g.fillRect(rect.x, rect.y, rect.width, rect.height);
            g.dispose();
    
            //動かす
            rect.x += 2;
    
            //フレームの追加
            writer.writeToSequence(new IIOImage(image, null, metadata), null);
            metadata = null; //先頭フレームだけで良い？
          }
          writer.endWriteSequence();
          stream.close();
        } catch (IOException ioe) {
          EventQueue.invokeLater(() -> {
            JOptionPane.showMessageDialog(null, "失敗しました\n" + ioe.getMessage());
          });
        }
      }
    }
    
```

  * 追記: ループは Netscape による拡張らしいです 
    * [JDK-6738539 Support GIF Netscape Loop Extension in javax.imageio.ImageWriter - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-6738539)
    * [GIFフォーマットの詳細](http://www.tohoho-web.com/wwwgif.htm)

> Application Extension は Netscape によって拡張された Loop Control として使用され、Application
> Identifier には "NETSCAPE"、Application Authentication Code には "2.0"
> の文字が格納される。Application Data として1バイトの 0x01
> および2バイトのループ回数(0～65535)が、例えばループ回数が5回の場合は 0x01 0x05 0x00 の3バイトが格納される。
CC BY-SA 3.0
2016-02-22T12:06:30.933
22397
2016-02-23T07:07:35.970
2016-02-23T07:07:35.970
46
46
22392
answer