Как конвертировать анимированный GIF в анимированный WEBP?
я использую GD для управления изображениями в PHP, на основе этот код
<?php
/**
Función para detectar si una imagen es animación
*/
function is_ani($filename) {
if(!($fh = @fopen($filename, 'rb')))
return false;
$count = 0;
while(!feof($fh) && $count < 2) {
$chunk = fread($fh, 1024 * 100);
$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
}
fclose($fh);
return $count > 1;
}
?>
я намерен разделить фреймы внутри GIF (я могу даже использовать эту библиотеку) и сохранить их в формате WEBP.
как я могу собрать/создать анимированное изображение в формате WEBP с помощью GD в PHP?
--отредактировано--
версия PHP:
PHP 7.2.4 (built: Apr 25 2018 12:05:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
Gd_info:
#php -r 'print_r(gd_info());'
Array
(
[GD Version] => bundled (2.1.0 compatible)
[FreeType Support] =>
[GIF Read Support] => 1
[GIF Create Support] => 1
[JPEG Support] => 1
[PNG Support] => 1
[WBMP Support] => 1
[XPM Support] =>
[XBM Support] => 1
[WebP Support] => 1
[BMP Support] => 1
[JIS-mapped Japanese Font Support] =>
)
Эта система работает на Debian 8.
--отредактировано 2--
Используя в качестве основы проект alo-malbarez и класс для извлечения кадров из GIF's Я сделал проект с более чистым и понятным классом экстрактора для людей, которым нужны определенные фрагменты GIF. В этом проекте вместе оба класса, чтобы сделать конвертер GIF в WEBP, у него все еще есть ошибки, но я работаю над их исправлением.
2 answers
Будет отсутствовать тема того, как вы извлекаете кадры из gif, но для этого есть ссылка в конце
Вот PdC о том, как собрать анимированный webp в php, используя только GD (с поддержкой webp)
Часть 1: кодирование каждого кадра
1) frame преобразуется в webp с GD и захватывается в переменную с ob_start
2) мы отбрасываем заголовок fileformat из этого потока и сохраняем frameData (начинается с " VP8 " и идет до конца) в array (buffer) рядом с info ширина, высота и продолжительность кадра
3) возвращается к 1, пока больше нет фреймов
Примечание: чтобы изменить качество сжатия изменить строку
imagewebp($image);
Поставив, например,
imagewebp($image, null, 75);
null
предназначен для того, чтобы вы не записывали файл
Часть 2: Собрать анимированный webp в соответствии со спецификацией https://developers.google.com/speed/webp/docs/riff_container
An animated image with EXIF metadata may look as follows:
RIFF / WEBP
+- VP8X (descriptions of features used)
+- ANIM (global animation parameters)
+- ANMF (frame1 parameters + data)
+- ANMF (frame2 parameters + data)
+- ANMF (frame3 parameters + data)
+- ANMF (frame4 parameters + data)
+- EXIF (metadata) <- opcional
0) open stream или собрать в var
1) заголовок fileformat (RIFF+filesize+WEBP)
1. а) вычислить filesize (uint32) filesize - это общее количество байтов файла -8 заголовка или общее количество байтов всех chunks + 4
2) chunk header VP8X
2. а) вычислить chunksize (uint32) = 10
2. b) поместите биты альфа и анимации в 1
2. c) кодировать ширину и высоту canvas в uint24 (-1)
3) chunk header ANIM
3. а) вычислить chunksize (uint32) = 6
3. b) цвет фона: BGRA (0,0,0,0)
3. c) loop count (uint16) 0=infinityandbeyond
4) для каждого кадра, сохраненного в массиве (буфер), часть 1.2
4. а) chunk header ANMF
4. b) вычислить chunksize (uint32) = 16 + всего байтов в frameData
4. c) происхождение х, у кадра (uint24) (/2)
4. d) ширина и высота кадра (uint24) (-1)
4. e) продолжительность миллисекунд (uint24)
4. f) reserved (6 бит) + alpha blending (1 бит) + отбросить frame (1 бит)
4. g) данные кадра, сохраненные в буфере, часть 1.2
5) save to disk или close stream
#!/usr/bin/env php
<?php
function toUint32($n){
$ar = unpack("C*", pack("L", $n));
return $ar;
}
function toUint24($n){
$ar = unpack("C*", pack("L", $n));
array_pop($ar);
return $ar;
}
function toUint16($n){
$ar = unpack("C*", pack("S", $n));
return $ar;
}
function bytesToString($bytes){
return implode(array_map("chr", $bytes));
}
function binaryToBytes($bits){
$octets = explode(' ', $bits);
return array_map("bindec", $octets);
}
$oWidth=120;
$oHeight=20;
$frameArray = [];
function getFrameData($image, $msec){
$w = imagesx($image);
$h = imagesy($image);
ob_start();
imagewebp($image);
if (ob_get_length() % 2 == 1) :
echo "\0";
endif;
$image_data = ob_get_contents();
ob_end_clean();
$frameData = substr($image_data, strpos($image_data, "VP8 "));
return Array(
"frameData" => $frameData,
"duration" => bytesToString(toUint24($msec)),
"width" => bytesToString(toUint24($w - 1)),
"height" => bytesToString(toUint24($h -1 )),
);
}
// Create a blank image and add some text
$im = imagecreatetruecolor($oWidth, $oHeight);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5, 'WebP with PHP', $text_color);
$frameArray[] = getFrameData($im, 70);
imagedestroy($im);
// Create a blank image and add some text
$im = imagecreatetruecolor($oWidth, $oHeight);
$text_color = imagecolorallocate($im, 14, 233, 91);
imagestring($im, 1, 5, 5, 'WebP with PHP', $text_color);
$frameArray[] = getFrameData($im, 70);
imagedestroy($im);
// create new WEBP
$fileWEBP = "";
$fileHeader = "";
$fileContents = "";
// Chunk HEADER VP8X
$fileContents .="VP8X";
$headChunkSize = bytesToString(toUint32(10));
// bit flags Rsv|I|L|E|X|A|R| Reserved
$oVP8XflagsBin = "00010010 00000000 00000000 00000000";
$oVP8Xflags = bytesToString(binaryToBytes($oVP8XflagsBin));
$oCanvasSize = bytesToString(toUint24($oWidth-1)).bytesToString(toUint24($oHeight-1));
$fileContents .= $headChunkSize. $oVP8Xflags. $oCanvasSize;
// Chunk HEADER ANIM
$fileContents .="ANIM";
$animChunkSize = bytesToString(toUint32(6));
// loop count 16bits, 0 = infinito
// bytesToString(toUint16(0));
$oLoopCount = str_repeat(chr(0), 2);
// 32bits BGRA, Blue Green Red Alpha (0,0,0,0)
$oBackGround = str_repeat(chr(0), 4);
$fileContents .= $animChunkSize . $oBackGround . $oLoopCount;
foreach ($frameArray as $frame) :
// Chunk HEADER ANMF
$fileContents .="ANMF";
$frameDataChunkSize = bytesToString(toUint32(strlen($frame['frameData'])+16));
// frame origin X Y
// bytesToString(toUint24(originX)) . bytesToString(toUint24(originY))
// (0,0)
$fOrigin = str_repeat(chr(0), 6);
// frame size (uint24) width-1 , (uint24) height-1
$fSize = $frame['width'].$frame['height'];
// frame duration in miliseconds (uint24)
$fDuration = $frame['duration'];
// frame options bits
// reserved (6 bits) + alpha blending (1 bit) + descartar frame (1 bit)
$fFlagsBin = "00000010";
$fFlags = bytesToString(binaryToBytes($fFlagsBin));
// chunk payload
$fileContents .= $frameDataChunkSize.$fOrigin.$fSize.$fDuration.$fFlags.$frame['frameData'];
endforeach;
// calculate Size and build file header
$fileSize = bytesToString(toUint32(strlen($fileContents)+4));
$fileHeader = "RIFF".$fileSize."WEBP";
$fileWEBP = $fileHeader.$fileContents;
file_put_contents('animated.webp',$fileWEBP);
Ссылка РЕПО на случай, если вы хотите расширить его или сделать вилку с частью извлечения gif frames
Https://github.com/aloMalbarez/php-GD-Animated-webp
Чтобы извлечь кадры из gif, вы можете использовать это класс
Https://github.com/jacoka/GIFDecoder
Волна foreach ( $gifDecoder -> GIFGetFrames ( ) as $frame )
Edit я поместил ссылку на класс GIFDecoder оригинального автора
На самом деле мало информации, мне было трудно найти это:
Https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php
Примеры также есть в официальной документации PHP:
Http://php.net/manual/es/function.imagewebp.php http://php.net/manual/es/function.imagecreatefromgif.php
Но я суммирую первую ссылку (которая интересна) для вашего случая в в частности:
<?php
$im = imagecreatefromgif($filepath);
imagewebp($im, $resizedFilepath);
imagedestroy($im);
?>
Затем у вас есть возможность использовать команду типа оболочки с двоичным файлом типа: https://developers.google.com/speed/webp/docs/gif2webp
Хотя я предупреждаю вас, что формат плохо поддерживается, и я не знаю, насколько он надежен, как я исследовал, я бы сказал, что он находится в очень зеленой фазе развития.