3
1

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 1 year has passed since last update.

アニメーションWebP(Animated WebP)の作り方

Last updated at Posted at 2021-03-20

はじめに

複数のWebPファイルを元にアニメーションWebP(Animated WebP)を作成するプログラムを実装しました。

WebPについて

  • 米Googleが開発しているオープンな静止画像フォーマットです。
  • ウェブサイトのトラフィック量軽減と表示速度短縮を目的に、JPEGやGIF、PNGの置き換えを意図する規格だそうです。
  • 各種ブラウザも表示対応しています。(ChromeFirefoxEdgeSafariOpera
  • アニメーション形式にも対応しています。

Wikipediaより

環境

WebPファイルのアニメーションを作成するために、libwebpを使用します。
WebPの静止画像を作成するだけなら、こちらで紹介した方法もあります。

  • Windows 10
  • dmd v2.098.0
  • libwebp ver 1.2.2 windows x64版

ソースコード

以下に、実装したソースコードを紹介します。

awebp.d
/+ dub.sdl:
	name "awebp"
	dependency "libwebp-d" repository="git+https://github.com/devmynote/libwebp-d.git" version="3c927671d7fef977a793cf3da0850104dc865216"
	lflags "/LIBPATH:./lib"
+/
// dub build --build=release --arch=x86_64 --single awebp.d
import std.stdio;
import std.string;
import core.stdc.stdio;
import webp.decode;
import webp.encode;
import webp.mux;
import webp.muxtypes;
import webp.types;

ubyte[] loadWebP(string ifile)
{
	auto file = File(ifile, "rb");
	scope(exit) file.close();
	auto size = file.size();
	ubyte[] buf = file.rawRead(new ubyte[size]);
	return ( buf );
}

void saveWebP(string ifile, string ofile)
{
	ubyte[] buf = loadWebP(ifile);
	auto file = File(ofile, "wb");
	scope(exit) file.close();
	file.rawWrite(buf);
}

ubyte* getImageRGBA(string ifile, int* width, int* height)
{
	ubyte[] buf = loadWebP(ifile);
	return ( WebPDecodeRGBA(buf.ptr, buf.length, width, height) );
}

void saveAnimatedWebP(string[] ifiles, string ofile, uint loopCount, ushort delayTime)
{
	WebPAnimEncoderOptions enc_options;
	WebPAnimEncoderOptionsInit(&enc_options);
	WebPConfig config = void;
	WebPConfigInit(&config);
	
	WebPAnimEncoder* enc;
	foreach ( i, ifile; ifiles ){
		int width, height;
		ubyte* rgba = getImageRGBA(ifiles[i], &width, &height);
		if ( i == 0 ){
			enc = WebPAnimEncoderNew(width, height, &enc_options);
		}
		WebPPicture frame;
		WebPPictureInit(&frame);
		frame.use_argb = 1;
		frame.width    = width;
		frame.height   = height;
		int stride = width * 4;
		WebPPictureImportRGBA(&frame, rgba, stride);
		WebPAnimEncoderAdd(enc, &frame, cast(int)i * delayTime, &config);
		WebPFree(rgba);
	}
	WebPAnimEncoderAdd(enc, null, cast(int)ifiles.length * delayTime, null);
	WebPData webp_data;
	WebPAnimEncoderAssemble(enc, &webp_data);
	scope(exit) WebPFree(cast(void*)webp_data.bytes);
	WebPMux*	 mux = WebPMuxCreate(&webp_data, 1);
	WebPMuxAnimParams anim_params;
	anim_params.loop_count = loopCount;
	WebPMuxSetAnimationParams(mux, &anim_params);
	WebPMuxAssemble(mux, &webp_data);
	
	FILE* fp = fopen(ofile.toStringz, "wb"); 
	scope(exit) fclose(fp);
	fwrite(webp_data.bytes, webp_data.size, ubyte.sizeof, fp);
	
	WebPMuxDelete(mux);
	WebPAnimEncoderDelete(enc);
}

void main(string[] args)
{
	if ( args.length == 3 ){
		saveWebP(args[1], args[2]);
	} else {
		uint   loopCount = 0;	// 0:infinite
		ushort delayTime = 500;	// delayTime * ms
		saveAnimatedWebP(args[1 .. $-1], args[$-1], loopCount, delayTime);
	}
}

ソースコード補足

loadWebP

入力WebPファイルifileをメモリに読み込みます。

saveWebP

入力WebPファイルが1つの場合、アニメーションWebPファイルを作成せず静止画像WebPファイルofileを作成します。

getImageRGBA

入力WebPファイルから画像データ(RGBA)を抽出します。

saveAnimatedWebP

アニメーションWebPファイル作成の本処理となります。

  • 複数の入力WebPファイルから画像データを抽出して、出力用アニメーションWebPファイルにセットします。
  • アニメーションWebPファイルの幅、高さは1つ目の入力WebPファイルの情報を使います。このため、すべての入力WebPファイルはフレームの幅、高さが同じであることが前提です。
  • loopCountは、アニメーションの繰り返し回数です。0をセットすると無制限に繰り返します。
  • delayTimeは、各画像の表示間隔です。単位はミリ秒です。

コンパイル

libwebpの入手

A new image format for the Webより、libwebp-1.2.2-windows-x64.zipをダウンロードします。
libwebp-1.2.2-windows-x64.zip内のlibを以下に配置します。

コマンドプロンプト
D:\Dev\AWebP> tree
フォルダー パスの一覧:  ボリューム ****
ボリューム シリアル番号は ****-**** です
D:.
└─lib

D:\Dev\AWebP> dir lib
フォルダー パスの一覧:  ボリューム ****
ボリューム シリアル番号は ****-**** です

 D:\Dev\AWebP\lib のディレクトリ

2021/03/20  18:23    <DIR>          .
2021/03/20  18:23    <DIR>          ..
2021/01/21  12:53         1,658,226 libwebp.lib
2021/01/21  12:53            42,868 libwebpdemux.lib
2021/01/21  12:53           118,690 libwebpmux.lib

libが配置できたら、コンパイルを実行するだけです。

コマンドプロンプト
D:\Dev\AWebP> dub build --build=release --arch=x86_64 --single awebp.d
parsePackageRecipe dub.sdl
Performing "release" build using D:\App\Dev\dmd2\windows\bin64\dmd.exe for x86_64.
awebp ~master: building configuration "application"...
Linking...

実行、実行結果

コマンド実行例です。実行結果はこちら

コマンドプロンプト
D:\Dev\AWebP> awebp i1.webp i2.webp i3.webp i4.webp i5.webp ani.webp

おまけ1:静止画像ファイルの作成

副産物として、適当なWebP静止画像ファイルの作成例を紹介します。

iwebp.d
/+ dub.sdl:
	name "iwebp"
	dependency "libwebp-d" repository="git+https://github.com/devmynote/libwebp-d.git" version="3c927671d7fef977a793cf3da0850104dc865216"
	lflags "/LIBPATH:./lib"
+/
// dub build --build=release --arch=x86_64 --single iwebp.d
import std.string;
import core.stdc.stdio;
import webp.encode;
import webp.types;

void saveImageWebP(string ofile, ubyte[] rgba, int width, int height)
{
	int stride = width * 4;
	ubyte* output;
	size_t size = WebPEncodeLosslessRGBA(rgba.ptr, width, height, stride, &output);
	scope(exit) WebPFree(output);
	FILE* fp = fopen(ofile.toStringz, "wb"); 
	scope(exit) fclose(fp);
	fwrite(output, size, ubyte.sizeof, fp);
}

void saveImageSample()
{
	int width  = 200;
	int height = 100;
	ubyte[] rgba = new ubyte[width * height * 4];
	for ( int y = 0; y < height; y++ ){
		for ( int x = 0; x < width; x++ ){
			int ind = x * 4 + y * width * 4;
			rgba[ind    ] = 0;
			rgba[ind + 1] = 64;
			rgba[ind + 2] = 128;
			rgba[ind + 3] = 255;
		}
	}
	saveImageWebP("image.webp", rgba, width, height);
}

void main(string[] args)
{
	saveImageSample();
}

おまけ2:dstepを使ってみた

今回、libwebpのライブラリを使用するためのバインドファイル(パッケージ)を作成しました。
おまけとして、バインドファイルを作成した際のメモ書きをまとめました。
参考:C言語のヘッダーファイルをD言語用に変換する dstep の使い方

dstep使用時の環境

  • Windows 10
  • dmd v2.096.0
  • clang version 9.0.0
  • Visual C++ ビルドツール 2019
  • libwebp-1.2.2-windows-x64.zip
  • dstep version 1.0.2

dstepのビルド

私の開発環境の場合、clangのインストールフォルダが標準と異なるため、ライブラリlibclang.libが見つからないというエラーが発生しました。

コマンドプロンプト
LINK : fatal error LNK1181: 入力ファイル 'libclang.lib' を開けません。
Error: linker exited with status 1181
D:\Dev\dmd2\windows\bin\dmd.exe failed with exit code 1.

dstepのdub.json/LIBPATHlibclang.libがあるフォルダに修正します。(修正箇所は2か所あります)
以下は、D:\Dev\LLVM\libフォルダを指定する場合の修正例です。

dub.json
"lflags-windows-x86_64": [
    "/LIBPATH:D:\\Dev\\LLVM\\lib",
    "libclang.lib",
    "Ole32.lib"
],

dstepの実行

libwebp-1.2.2-windows-x64.zipincludeD:\Dev\AWebPに配置しました。
その後で、dubコマンドを順番に実行しました。出力先フォルダとしてsourceを作成しています。
コマンド実行中にDeprecatedのメッセージが出ますが、特に対処不要です。問題なくファイルが変換されました。
大半の変換はうまくいったので、とても便利だと感じました。ただし、すべて自動変換というわけにはいかず、一部手を加えました。
完成したファイルはこちらにもあります。

コマンドプロンプト
D:\Dev\AWebP> dub run dstep -- include\webp\decode.h    -o source/decode.d
D:\Dev\AWebP> dub run dstep -- include\webp\demux.h     -o source/demux.d
D:\Dev\AWebP> dub run dstep -- include\webp\encode.h    -o source/encode.d
D:\Dev\AWebP> dub run dstep -- include\webp\mux.h       -o source/mux.d
D:\Dev\AWebP> dub run dstep -- include\webp\mux_types.h -o source/muxtypes.d
D:\Dev\AWebP> dub run dstep -- include\webp\types.h     -o source/types.d

参考情報

A new image format for the Web
libwebp-1.2.2-windows-x64.zip
WebPAnimEncoder API
C++ (Cpp) WebPAnimEncoderAssemble Examples
C言語のヘッダーファイルをD言語用に変換する dstep の使い方
dub v1.23.0/Support dependencies as git url with exact commit
アニメーションWebPの作り方

更新履歴

2021.3.20 初回投稿
2022.1.26 バージョン1.2.2にあわせて更新

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?