使用ImageMagick操作gif圖

這篇文章我們要學習的是一個具體的案例,也是我在實際業務開發中所接觸過的一個案例。具體的效果就是對於微信小遊戲和小程序來說,不能直接地使用動態 Gif 圖片,一張 Gif 圖片在小遊戲或小程序中是不會動的。所以在我們公司的遊戲開發中,需要一張將整個 Gif 動圖的每一幀拆出來的圖片拼成一張精靈圖交給前端,由他們來使用 JS+CSS 的能力動態地循環我們拆幀後的圖片,從而形成動圖的效果。

業務需求就是這麼個情況,當然,最後的解決方案也正是使用了 ImageMagick 來實現的。話不多說,我們直接先看代碼。

GIF 圖拆幀
原始的圖片是這樣的一張動圖:

$imgPath = '../img/4.gif';
$imagick = new \Imagick($imgPath);
$imagick = $imagick->coalesceImages();
$imageCount = $imagick->count();
echo 'image count:', $imageCount, PHP_EOL; // image count:51

$imgAttrs = [
    'width'       => $imagick->getImageWidth(),
    'height'      => $imagick->getImageHeight(),
    'frame_count' => $imageCount,
];
$column = 5;
if ($imageCount < $column) {
    $column = $imageCount;
}

$row = ceil($imageCount / $column);

$spImgWidth = $imgAttrs['width'] * $column;
$spImgHeight = $imgAttrs['height'] * $row;

 // 創建圖片
$spImg = new \Imagick();
$spImg->setSize($spImgWidth, $spImgHeight);
$spImg->newImage($spImgWidth, $spImgHeight, new \ImagickPixel('#ffffff00'));
$spImg->setImageFormat('png');

$i = 0;
$h = 0;
$cursor = 0;
do {
    if ($i == $column) {
        $i = 0;
        $h++;
    }
    if($cursor == 0){ // 保存第一幀圖片
        $imagick->writeImage($imgPath . '.first.png');
    }
    // 保存全部的圖片幀到一張 png 圖片中
    $spImg->compositeImage($imagick, \Imagick::COMPOSITE_DEFAULT, $i * $imgAttrs['width'], $h * $imgAttrs['height']);
    $i++;
    $cursor++;
} while ($imagick->nextImage());
$spImg->writeImage($imgPath . '.png');

實例化 Imagick 對象就不用多說了,我們首先調用的是 coalesceImages() 這個方法。它的作用是返回合成後的 Imagick 對象。通過這個方法,我們就獲得了整個 GIF 圖裏面的全部每一幀圖片的信息。這時,使用 count() 方法,就可以獲得圖片中的所有圖片幀的個數。比如我們測試的這張圖片就有 51 幀。

然後計算精靈圖的行和列以及相應需要的寬高,比如我們以 5 列爲基準,也就是一行放五張拆幀出來的圖片,這樣一共需要 11 行才放得下最後生成的精靈圖。同理,寬高也是以拆出來的圖片寬高乘以相應的列和行數。

接着,根據計算出來的寬高生成一張新的圖片,作爲精靈圖的背景圖,使用 newImage() 函數設置圖片寬高及背景透明。使用 setImageFormat() 方法設置圖片的格式爲 PNG 格式,使用 PNG 主要是爲了透明,其實按我們這樣緊密排列的圖片來說,不用透明也可以,但某些應用中比如網站前端需要的精靈圖可能不同的圖片之前是需要一定間隔的,所以一般會使用透明的底圖。

然後就是一個循環,也就是循環那 51 張拆幀出來的圖片,使用 nextImage() 不斷地獲取原始 GIF 圖中的下一幀圖片,並將他們組合保存在上面新建的背景圖片中,每一幀的圖片位置也是通過單幀圖片的寬高與行列情況計算出來的。在這段代碼中,我們還保存了第一幀的圖片,當然,這也是業務需要,你可以隨時保存任何一張每幀的圖片。

最後,使用 writeImage() 保存圖片。輸出的圖片就是下面的這個樣子:

組合成動態 GIF 圖
以上的業務功能是我在開發中實際使用過的功能,當然,除了可以對 GIF 圖進行拆幀之外,我們也可以將多張圖片組合成一個動態的 GIF 圖。

$gifImagek = new Imagick();
$gifImagek->setFormat('GIF');

for($i=1;$i<=5;$i++){
    $img = new Imagick('../img/3'.$i.'.jpeg');
    $img->setImageDelay(100);
    $gifImagek->addImage($img);
}

$gifImagek->writeImages("../img/5.gif", true);
$gifImagek->writeImages("../img/52.gif", false);

這段代碼就比較簡單了,依然還是創建一個圖片,並且指定格式爲 GIF 圖片。然後循環添加圖片,這裏我們使用的是上篇文章中 GraphicsMagick 中操作過的那些圖片。setImageDelay() 用於設置圖片顯示間隔,這裏我們設置的是 100 毫秒,然後再使用 addImage() 將圖片添加到我們新創建的 GIF 圖畫布中。

最後保存圖片的時候,需要使用 writeImages() 進行保存,它的作用是保存這種連續的多張圖片。它的第二個參數是指定是否將圖片保存到一張圖片中,如果是 false 的話,就類似於拆幀的效果,不過會將圖片一張一張的分開保存,比如 52-1.gif 、 52-2.gif 這樣。

最後生成的動圖就是這樣的:

總結
今天的內容有意思吧,不是那些爛大街的縮放、加水印、驗證碼之類的功能,而是比較好玩的對於 GIF 圖的操作。說實話,在業務開發中類似的業務場景還是很多的,就像自動生成精靈圖這種功能就完全可以使用 ImageMagick 來實現,而且都是 ImageMagick 擴展中自帶的函數就可以搞定了,非常方便。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章