В настоящее время я сталкиваюсь с некоторыми проблемами при изменении размера изображений с помощью GD.
Все работает нормально, пока я не хочу изменить размер анимированного gif, который показывает первый кадр на черном фоне.
Я пробовал использовать getimagesize, но это дает мне только размеры и ничего не может отличить любой gif от анимированного.
Для анимированных гифок фактическое изменение размера не требуется, для наших целей достаточно просто пропустить их.
Какие-нибудь подсказки?
PS. У меня нет доступа к imagemagick.
С уважением,
Крис






На странице руководства PHP функции imagecreatefromgif () есть краткий фрагмент кода, который должен быть тем, что вам нужно:
imagecreatefromgif, комментарий # 59787, автор: ZeBadger
Странно, что я не нашел этого в моем зеркале руководства по умолчанию, но очень много за ссылку. Я изменил функцию и дал как исходный постер, так и вашу благодарность в комментариях.
Оптимизированная версия здесь.
Эта ссылка по какой-то причине не работает, но ее все еще можно найти здесь: php.net/manual/en/function.imagecreatefromgif.php#104473
Вот рабочая функция:
/**
* Thanks to ZeBadger for original example, and Davide Gualano for pointing me to it
* Original at http://it.php.net/manual/en/function.imagecreatefromgif.php#59787
**/
function is_animated_gif ( $filename )
{
$raw = file_get_contents( $filename );
$offset = 0;
$frames = 0;
while ($frames < 2)
{
$where1 = strpos($raw, "\x00\x21\xF9\x04", $offset);
if ( $where1 === false )
{
break;
}
else
{
$offset = $where1 + 1;
$where2 = strpos( $raw, "\x00\x2C", $offset );
if ( $where2 === false )
{
break;
}
else
{
if ( $where1 + 8 == $where2 )
{
$frames ++;
}
$offset = $where2 + 1;
}
}
}
return $frames > 1;
}
В поисках решения той же проблемы я заметил, что на сайте php.net есть продолжение кода, на который ссылаются Давид и Крис, но, по словам автора, он менее требователен к памяти и, возможно, менее требователен к диску. .
Я воспроизведу его здесь, потому что это может быть интересно.
источник: http://www.php.net/manual/en/function.imagecreatefromgif.php#88005
function is_ani($filename) {
if (!($fh = @fopen($filename, 'rb')))
return false;
$count = 0;
//an animated gif contains multiple "frames", with each frame having a
//header made up of:
// * a static 4-byte sequence (\x00\x21\xF9\x04)
// * 4 variable bytes
// * a static 2-byte sequence (\x00\x2C)
// We read through the file til we reach the end of the file, or we've found
// at least 2 frame headers
while(!feof($fh) && $count < 2) {
$chunk = fread($fh, 1024 * 100); //read 100kb at a time
$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', $chunk, $matches);
}
fclose($fh);
return $count > 1;
}
В недавно добавленной заметке упоминается, что фотошоп может использовать \x00\x21 вместо \x00\x2C.
Замечание Фрэнка не так откровенно, как должно быть. Для других, просматривающих эту страницу, пожалуйста, посмотрите его полное объяснение на здесь
По сравнению с решением Криса, приведенным ниже, может быть снижение производительности из-за использования preg_match_all в пользу strpos. С другой стороны, файл читается удобными для памяти фрагментами, в отличие от file_get_contents. Однако решение не покрывает краеугольный камень маркера кадра, занимающего более двух частей, поэтому я немного отредактировал его.
Я обнаружил, что Paint Tool SAI и Photoshop CS6 иногда не включают 00 в конце блока расширения приложения, что означает, что первый \x00 можно опустить.
Анимированный GIF должен иметь следующую строку
"\x21\xFF\x0B\x4E\x45\x54\x53\x43\x41\x50\x45\x32\x2E\x30"
Я тестировал несколько анимированных гифок, и кажется, что строка находится на позиции 781 файла (найдена с помощью file_get_contents и strpos)
Чтение всего файла с помощью file_get_contents может занять слишком много памяти, если данный файл слишком велик. Я реорганизовал приведенную ранее функцию, которая читает ровно столько байтов, чтобы проверять кадры, и возвращает, как только находит хотя бы 2 кадра.
<?php
/**
* Detects animated GIF from given file pointer resource or filename.
*
* @param resource|string $file File pointer resource or filename
* @return bool
*/
function is_animated_gif ($file)
{
$fp = null;
if (is_string($file)) {
$fp = fopen($file, "rb");
} else {
$fp = $file;
/* Make sure that we are at the beginning of the file */
fseek($fp, 0);
}
if (fread($fp, 3) !== "GIF") {
fclose($fp);
return false;
}
$frames = 0;
while (!feof($fp) && $frames < 2) {
if (fread($fp, 1) === "\x00") {
/* Some of the animated GIFs do not contain graphic control extension (starts with 21 f9) */
if (fread($fp, 1) === "\x2c" || fread($fp, 2) === "\x21\xf9") {
$frames++;
}
}
}
fclose($fp);
return $frames > 1;
}
+1 за абсолютную правду. В то время файлы были небольшими, и их чтение было быстрее, чем побайтово, по причинам, которые я не исследовал или не помню, чтобы исследовать.
Обратите внимание, что fread($fp, 1) === "\x2c" || fread($fp, 2) === "\x21\xf9" сначала прочитает один байт (A), проверьте, что это 0x2C, в противном случае он будет читать два байта после байта A, а не после байта 0x00. Таким образом, соответствующие строки байтов - это 00 2C или 00 ** 21 F9, где ** означает любой байт. Я не уверен, задумано это или нет, но из кода это не очень ясно.
Это улучшение текущего ответа, получившего наибольшее количество голосов, но у меня пока недостаточно репутации, чтобы комментировать. Проблема с этим ответом заключается в том, что он считывает файл фрагментами по 100 КБ, и маркер конца кадра может быть разделен между двумя фрагментами. Исправить это можно, добавив последние 20 байт предыдущего кадра к следующему:
<?php
function is_ani($filename) {
if (!($fh = @fopen($filename, 'rb')))
return false;
$count = 0;
//an animated gif contains multiple "frames", with each frame having a
//header made up of:
// * a static 4-byte sequence (\x00\x21\xF9\x04)
// * 4 variable bytes
// * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?)
// We read through the file til we reach the end of the file, or we've found
// at least 2 frame headers
$chunk = false;
while(!feof($fh) && $count < 2) {
//add the last 20 characters from the previous string, to make sure the searched pattern is not split.
$chunk = ($chunk ? substr($chunk, -20) : "") . fread($fh, 1024 * 100); //read 100kb at a time
$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
}
fclose($fh);
return $count > 1;
}
Автор сказал, что у него нет ImageMagick. Но для всех людей, которые ищут какой-либо способ узнать, является ли гифка анимированной, они попали сюда из Google (как и я): ImageMagick делает это довольно легко: php.net/manual/en/imagick.getimageiterations.php