Generar animaciones en archivos webp

Google ha creado un nuevo formato de imagen webp que ofrece imágenes de alta calidad con un tamaño mucho menor que los formatos gif, jpg y png. Es posible generar animaciones en archivos webp.

Aquí os muestro un ejemplo de una animación creada con 150 jpgs a un tamaño de 700 x 184 pixeles.

Animación gif de un globo en el cielo
GIF animado 7’14 MB
Animación en archivo webp de un globo en el cielo
WEBP animado 2’64 MB

Comprobar capacidades GD del servidor

Para poder generar animaciones en archivos webp, necesitamos que la librería php GD para el manejo de imágenes este en nuestro servidor y que tenga activado el soporte para imágenes webp. Para ello hacemos una petición a la función gd_info:

$supportgd = gd_info();
$supoortwebp = false;
foreach($supportgd as $clave => $valor) {
if($clave."" ===  "WebP Support" && $valor."" ===  "1") { $supoortwebp = true; }
}

Una vez que sabemos que la librería puede trabajar con el formato webp podemos pasar a generar imágenes webp.

Voy a explicar como generar webp animados a partir de una secuencia de imágenes.

Generar webp animado con php

Para generar este código me he basado en la aportación de Alejandro Malbarez, en concreto su proyecto php-GD-Animated-webp.

En la generación de animaciones en archivos webp, lo primero creamos unas funciones recursivas para calcular datos convirtiendo un valor a en un entero de 32 , 24 o 16 bits sin signo, convertir array a string y convertir un binario a bytes.

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

Creamos una función que crea la imagen webp pasandole:

function createWEbpAnim($srcframes, $gifW, $gifH, $loop) {
	// 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($gifW-1)).bytesToString(toUint24($gifH-1));
	$fileContents .= $headChunkSize. $oVP8Xflags. $oCanvasSize;
	// Chunk HEADER ANIM
	$fileContents .="ANIM";
	$animChunkSize = bytesToString(toUint32(6));
	$oLoopCount = bytesToString(toUint16($loop)); // loop count 16bits, 0 = infinito
	$oBackGround = str_repeat(chr(0), 4); // 32bits BGRA, Blue Green Red Alpha (0,0,0,0)
	$fileContents .= $animChunkSize . $oBackGround . $oLoopCount;
	// los frames
	for($i = 0; $i<count($srcframes); $i++) {
		$imagetype = exif_imagetype($srcframes[$i][0]); // 1: gif, 2: jpg, 3: png
		if($imagetype === IMAGETYPE_JPEG){ $img = imagecreatefromjpeg($srcframes[$i][0]); } 
		if($imagetype === IMAGETYPE_GIF){ $img = imagecreatefromgif($srcframes[$i][0]); } 
		if($imagetype === IMAGETYPE_PNG){ $img = imagecreatefrompng($srcframes[$i][0]); } 
		imagepalettetotruecolor($img);
		imagealphablending($img, true);
		imagesavealpha($img, true);
		$w = imagesx($img);
		$h = imagesy($img);
		ob_start();
		imagewebp($img);
		if (ob_get_length() % 2 == 1) { echo "\0"; }
		$image_data = ob_get_contents();
		ob_end_clean();
		$frameData = substr($image_data, strpos($image_data, "VP8 "));
		$fileContents .="ANMF";
		$frameDataChunkSize = bytesToString(toUint32(strlen($frameData)+16));
		$fOrigin = str_repeat(chr(0), 6);
		$fSize = bytesToString(toUint24($w - 1)).bytesToString(toUint24($h - 1));
		$fDuration = bytesToString(toUint24($srcframes[$i][1]*10));
		$fFlagsBin = "00000010";
		$fFlags = bytesToString(binaryToBytes($fFlagsBin));
		$fileContents .= $frameDataChunkSize.$fOrigin.$fSize.$fDuration.$fFlags.$frameData;
		imagedestroy($img);
	}
	// calculate Size and build file header
	$fileSize = bytesToString(toUint32(strlen($fileContents)+4));
	$fileHeader = "RIFF".$fileSize."WEBP";
	$fileWEBP = $fileHeader.$fileContents;
	return $fileWEBP;
}

Para invocar la función rellenamos un array con las imágenes y el tiempo de permanencia de cada una y le pasamos los demás parámetros.

//crear en webp animado ejemplo
$losframes = Array(
	Array("orig/horse/png/1.png",6),
	Array("orig/horse/png/2.png",6),
	Array("orig/horse/png/3.png",6),
	Array("orig/horse/png/4.png",6),
	Array("orig/horse/png/5.png",6),
	Array("orig/horse/png/6.png",6),
	Array("orig/horse/png/7.png",6),
	Array("orig/horse/png/8.png",6)
);
// array de frames, ahcho de la imagen, alto de la imagen, veces que se repite (0 es infinito)
$resultado = createWEbpAnim($losframes, 307, 230, 0);
file_put_contents("img/ejemplo.webp",$resultado);

Os dejo en un zip los archivos del ejemplo de generación animaciones en archivos webp.

Compartir