概要
一般的にgif画像をcanvas上に貼り付けるとアニメーション機能が停止され、静止gif画像のように取り扱われます。そこで、私はgif画像をフォームから受けとり、それをフレームに分解して、HTML5のCanvas上で再生するWebアプリを作ってみました。。JavaScript上で動くゲームアプリを作るときに、gifアニメ画像をそのままゲーム素材として使えたら便利だと思います。##技術
バックエンドはJavaサーブレットを使っています。
フロントエンドは、flashの技術をJavaScriptに応用して使えると言われているadobe社のCREATEJSを使用しています。
使用するJavaのクラスのうち重要なもの
org.apache.commons.fileupload.FileItem
このクラスはクライアントから受け取る multipart/form-data のPOSTリクエスト内のファイルまたはアイテムを表現したものです。利用にはApacheCommonsのjarファイルをダウンロードする必要があります。
javax.imageio.stream.ImageInputStream
ImageReader で使用されるシーク可能な入力ストリームインタフェースです。InputStream や File などのさまざまな入力ソース、および将来の高速な入出力ソースを、このインタフェースの適切な実装で「ラップ」することで、イメージ入出力 API から使用可能にできます。
javax.imageio.ImageReader
イメージを解析して復号化する抽象スーパークラスです。
java.awt.image.BufferedImage
イメージデータのアクセス可能なバッファーを備えたImageオブジェクトを記述します。
javax.imageio.metadata.IIOMetadata
イメージとストリームに関連付けられたメタデータ (イメージ以外のデータ) を表すオブジェクトにより拡張される abstract クラスです。
javax.imageio.metadata.IIOMetadataNode
メタデータツリーのノードを表すクラスです。
org.w3c.dom.NodeList
順序付けられたノードのコレクションの抽象を提供します
org.w3c.dom.NamedNodeMap
名前を指定してアクセスできるノードのコレクションを表すインターフェースです。
手順
gifファイルには、メタ情報が存在します。ファイル名、画像の高さ、幅、フレーム数、アニメ待機時間、画像データなど、画像についての情報がメタデータとして画像ファイルには付与されています。HTMLのPOSTリクエストから受け取ったgifファイルからメタデータを取得します。
//fileItem:FileItemクラスのオブジェクト
ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(readAll(fileItem)));
ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();
reader.setInput(iis);
// i番目の画像データを取得する
BufferedImage bi = reader.read(i);
// i番目のフレームのメタデータを取得する
IIOMetadata meta = reader.getImageMetadata(i);
メタデータツリーのノードの中から、画像記述子を探し出す。
Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
//もし画像記述子ならば
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
//nodeItemを使用した次の処理
}
}
画像の位置とサイズをノードから取得して、後で、いつでも使えるようにMapに格納しておく。
//最終的にデータを格納する用のMap
Map<String, Integer> imageAttrMap = new HashMap<>();
//左位置、上位置、幅、高さを表す文字列を配列している
String[] imageAttArray = new String[] { "imageLeftPosition", "imageTopPosition", "imageWidth", "imageHeight" };
for (int k = 0; k < imageAttArray.length; k++) {
NamedNodeMap attr = nodeItem.getAttributes();
Node attnode = attr.getNamedItem(imageatt[k]);
//Mapに追加
imageAttrMap.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
}
今まで取得した情報に基づいてイメージオブジェクトを書き出す。
BufferedImage master = new BufferedImage(imageAttr.get("imageWidth"),imageAttr.get("imageHeight"),BufferedImage.TYPE_INT_ARGB);
master.getGraphics().drawImage(bi,imageAttr.get("imageLeftPosition"),imageAttr.get("imageTopPosition"), null);
イメージオブジェクトをファイルに書き出してみる。
ImageIO.write(master, "gif", new File("hoge.gif"));
##ソースコード
##サンプルアプリ
サンプルアプリ
https://rocky-sands-26639.herokuapp.com/top
サンプルアプリを実行する際には、以下のようなサイトからgifアニメ画像をダウンロードするなどして準備してください。
http://www.civillink.net/sozai/move.html
https://studio.beatnix.co.jp/kids-it/kids-programming/scratch/free-gif-animation-site/
##参考URL
以下のスタックオーバーフローの質問を参考にしています。
https://stackoverflow.com/questions/20077913/read-delay-between-frames-in-animated-gif