昔やってたブログ(閉鎖)のネタ再利用ですが
たまたま検索してみたら動く情報があんまりなかったので再度動作確認して記事にしてみました。
携帯でよくあるアニメーションGIFですがリサイズをを行うにはどうすればいいでしょうか?
例えば以下のコードの場合はダメです
意図したように動かないコード
<?php //元ファイル $src='src.gif'; //書き込みファイル $dest='dest.gif'; $im=new Imagick($src); //サムネ作成 $im->thumbnailImage(40, 0); //書き込み $im->writeImage($dest); $im->destroy(); exit;
静止画のGIFが出来上がります。
そこで画像シーケンスを書き込むwriteImagesを使ってみます
$im->writeImages($dest,true);
アニメーションGIFはできるのですが最後のフレームのみがリサイズされてしまいます。
そこでアニメーションを維持しつつリサイズを行う方法について解説します。
テスト環境
以下の環境で行いました
・php 5.3.4
・ImageMagick 6.2.8
・imagick 3.0.1
注意する点
携帯向けのアニメーションGIFのリサイズを行う上で幾つか注意する点があります。
・各フレームの移動
・各フレームのオフセット調整
・ローカルパレットとグローバルパレット
キーになる定数とメソッド
フレームの移動 setFirstIterator()
フレームの移動 nextImage();
パレット関係 resizeImage();
パレット関係 imagick::FILTER_POINT
オフセット調整 getImagePage()
オフセット調整 setImagePage
アニメーション書き出し writeImages()
ローカルパレットとグローバルパレットについて
アニメーションGIFは、パラパラマンガなわけですが
その各フレームでパレットを持つか、全体でひとつのパレットを持つかが選べます。
各フレームで持つのがローカルパレットで
全体でひとつなのがグローバルパレットです。
もちろん、各フレームでパレットをを持つローカルパレットはサイズが若干大きめです。
また、これが大事なのですが携帯については基本グローバルパレットが望ましいです。
これは一部のAU端末において、ローカルパレットのGIFを読み込ませても
最初のフレームのパレットしか使用しないため、もし2フレーム以降で同じ色を使っていても色が想定外になります。
例えば以下のようにローカルパレットを定義しているとします。
1フレーム目 0=000000 1=ffffff 2=ff0000 3=00ff00 4=0000ff (インデックス:色)
2フレーム目 0=000000 1=ffffff 2=00ff00 3=ff0000 4=0000ff (インデックス:色)
2フレーム目で赤色をだそうとインデックス3を使ったとしても、AU一部端末で最初のフレームのパレットが使われるため緑になってしまいます。
コード
<?php
//読み込み画像の指定
$src="src.gif";
//出力画像の指定
$dest="dest.gif";
//横幅の倍率
$scale_w=0.5;
//縦幅の倍率
$scale_h=0.5;
//画像読み込み
$image=new Imagick($src);
//イテレーターの初期化(フレームを先頭にする)
$image->setFirstIterator();
do{//各フレーム処理
//フレームのジオメトリ情報の取得(オフセット位置付き)
$par=$image->getImagePage();
//リサイズする
$image->resizeImage(
ceil($image->getImageWidth()*$scale_w),
ceil($image->getImageHeight()*$scale_h),
imagick::FILTER_POINT,//アンチエイリアスしない
1
);
//フレームのジオメトリ情報の設定
$image->setImagePage(
ceil($par['width']*$scale_w),
ceil($par['height']*$scale_h),
ceil($par['x']*$scale_w),
ceil($par['y']*$scale_h)
);
}while($image->nextImage());//次のフレームへ
//書き出し
$image->writeImages($dest, true);
ポイントの解説
$image->setFirstIterator();
見るフレームの位置を先頭に設定します。
$par=$image->getImagePage();
ジオメトリの情報を取得します
にたようなのでgetImageGeometryってのもありますが
オフセットが取れないのでこっちです
imagick::FILTER_POINT
通常のリサイズの場合scaleImageあたりを使ったりしますが
この場合デフォルトでアンチエイリアスが走って確実にパレットの情報が変わります。
しかもローカルパレット(各フレームで異なるパレット保持)になるので携帯だと見れません。
正直ジャギーが目立ちますがしょうがありません。
PC向けならscaleImageでかまわないとおもいます。
$image->setImagePage
ここで指定するwidth,heightは、リサイズで指定した画像サイズではありませんので注意してください。
$image->nextImage()
フレームを進めます。
$image->writeImages($dest, true);
writeImageじゃないので注意。
trueを指定しないと、フレームごとに連番で保存されます。