4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【ゲーム用】gif画像をフレームに分解して、HTML5のcanvas上で再生してみた

Last updated at Posted at 2017-11-26

概要

一般的にgif画像をcanvas上に貼り付けるとアニメーション機能が停止され、静止gif画像のように取り扱われます。そこで、私はgif画像をフォームから受けとり、それをフレームに分解して、HTML5のCanvas上で再生するWebアプリを作ってみました。。JavaScript上で動くゲームアプリを作るときに、gifアニメ画像をそのままゲーム素材として使えたら便利だと思います。

##技術

バックエンドはJavaサーブレットを使っています。
フロントエンドは、flashの技術をJavaScriptに応用して使えると言われているadobe社のCREATEJSを使用しています。

使用するJavaのクラスのうち重要なもの

:one: org.apache.commons.fileupload.FileItem
このクラスはクライアントから受け取る multipart/form-data のPOSTリクエスト内のファイルまたはアイテムを表現したものです。利用にはApacheCommonsのjarファイルをダウンロードする必要があります。

:two: javax.imageio.stream.ImageInputStream
ImageReader で使用されるシーク可能な入力ストリームインタフェースです。InputStream や File などのさまざまな入力ソース、および将来の高速な入出力ソースを、このインタフェースの適切な実装で「ラップ」することで、イメージ入出力 API から使用可能にできます。

:three: javax.imageio.ImageReader
イメージを解析して復号化する抽象スーパークラスです。

:four: java.awt.image.BufferedImage
イメージデータのアクセス可能なバッファーを備えたImageオブジェクトを記述します。

:five: javax.imageio.metadata.IIOMetadata
イメージとストリームに関連付けられたメタデータ (イメージ以外のデータ) を表すオブジェクトにより拡張される abstract クラスです。

:six: javax.imageio.metadata.IIOMetadataNode
メタデータツリーのノードを表すクラスです。

:seven: org.w3c.dom.NodeList
順序付けられたノードのコレクションの抽象を提供します

:eight: org.w3c.dom.NamedNodeMap
名前を指定してアクセスできるノードのコレクションを表すインターフェースです。

 

手順

:one: 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);

:two: メタデータツリーのノードの中から、画像記述子を探し出す。

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を使用した次の処理
     }
}

:three: 画像の位置とサイズをノードから取得して、後で、いつでも使えるように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()));
}

:four: 今まで取得した情報に基づいてイメージオブジェクトを書き出す。

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);

:five: イメージオブジェクトをファイルに書き出してみる。

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

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?