JavaでアニメーションGIF

Java SE6.0 (Mustang)がリリースされ、GIFの書き出しが標準機能で実装できるようになりました。以前からJAIJAI ImageIO ToolsGif-pluginなどの選択肢はありましたが、今後は追加パッケージ無しで実装可能になります。

GIFImageWriterの取得

GIFの書き出しはこれまでjpegやpngなどの書き出しと同様に ImageWriter を取得して行います。以下は例です(エラー処理は省略)。

1
2
3
Iterator it = ImageIO.getImageWritersByFormatName("gif");
ImageWriter iw = it.hasNext()?
    (ImageWriter)it.next() : null;

シーケンスの書き出し

各フレームのイメージは writeToSequence を使って書き出します。以下は例です。

1
2
3
4
5
6
7
8
9
10
11
12
BufferedImage bi1;
BufferedImage bi2;

// bi1, bi2 を作成

iw.prepareWriteSequence(null);

iw.writeToSequence(new IIOImage(bi1, nullnull), null);
iw.writeToSequence(new IIOImage(bi2, nullnull), null);

iw.endWriteSequence();

各シーケンスのディレイタイム

シーケンスのディレイタイムはイメージメタデータの GraphicControlExtension に指定する必要があります。このメタデータには他にも 前画像の消去方法(disposalMethod)などの指定が可能です。以下は指定の例です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ImageWriteParam iwp = iw.getDefaultWriteParam();

IIOMetadata meta = iw.getDefaultImageMetadata(
    new ImageTypeSpecifier(bi1), iwp);

String metaFormat = meta.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(metaFormat);

IIOMetadataNode child = new IIOMetadataNode("GraphicControlExtension");

child.setAttribute("disposalMethod""none");
child.setAttribute("userInputFlag""FALSE");
child.setAttribute("transparentColorFlag""FALSE");
child.setAttribute("transparentColorIndex""0");
child.setAttribute("delayTime""100");
root.appendChild(child);

meta.setFromTree(metaFormat, root);

iw.writeToSequence(new IIOImage(bi1, null, meta), iwp);

delayTimeは 1/100秒単位の指定になります。上記例ではdelayTimeを1秒に指定します。disposeMethodには none, doNotDispose, restoreToBackgroundColorなどが指定可能です。詳細はGIFメタデータ仕様をご覧ください。

ループカウントの指定

デフォルトではアニメーションは1回のみ実行されます。繰り返し実行したり回数を指定する場合にも(Stream)メタデータで指定する必要があります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int loop = 3;

IIOMetadataNode child = new IIOMetadataNode(
    "ApplicationExtensions");
IIOMetadataNode child2 = new IIOMetadataNode(
    "ApplicationExtension");

child2.setAttribute("applicationID""NETSCAPE");
child2.setAttribute("authenticationCode""2.0");

byte high = (byte)((loop >> 8) & 0xff);
byte low  = (byte)(loop & 0xff);
child2.setUserObject(new byte[]{ 0x1, low, high });

child.appendChild(child2);
root.appendChild(child);

"NETSCAPE"となっているのは繰り返し回数の指定がNetscapeによる拡張仕様であったためです。回数は setUserObject を使って指定します。1バイト目の 0x1は固定です。2-3バイトに low, highバイトの順に指定します。繰り返し回数に0を指定するとアニメーションが永久に繰り返されます。1を指定すると合計で2回実行されることになります。