Могу ли я обнаруживать анимированные гифки с помощью php и gd?

В настоящее время я сталкиваюсь с некоторыми проблемами при изменении размера изображений с помощью GD.

Все работает нормально, пока я не хочу изменить размер анимированного gif, который показывает первый кадр на черном фоне.

Я пробовал использовать getimagesize, но это дает мне только размеры и ничего не может отличить любой gif от анимированного.

Для анимированных гифок фактическое изменение размера не требуется, для наших целей достаточно просто пропустить их.

Какие-нибудь подсказки?

PS. У меня нет доступа к imagemagick.

С уважением,

Крис

Автор сказал, что у него нет ImageMagick. Но для всех людей, которые ищут какой-либо способ узнать, является ли гифка анимированной, они попали сюда из Google (как и я): ImageMagick делает это довольно легко: php.net/manual/en/imagick.getimageiterations.php

Lukas 29.09.2014 19:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
45
1
21 893
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

На странице руководства PHP функции imagecreatefromgif () есть краткий фрагмент кода, который должен быть тем, что вам нужно:

imagecreatefromgif, комментарий # 59787, автор: ZeBadger

Странно, что я не нашел этого в моем зеркале руководства по умолчанию, но очень много за ссылку. Я изменил функцию и дал как исходный постер, так и вашу благодарность в комментариях.

Kris 11.11.2008 15:25

Оптимизированная версия здесь.

Kevin Robatel 18.02.2016 13:37

Эта ссылка по какой-то причине не работает, но ее все еще можно найти здесь: php.net/manual/en/function.imagecreatefromgif.php#104473

John Mellor 24.05.2017 05:16

Вот рабочая функция:

/**
 * 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.

Frank Farmer 16.06.2011 23:24

Замечание Фрэнка не так откровенно, как должно быть. Для других, просматривающих эту страницу, пожалуйста, посмотрите его полное объяснение на здесь

billmalarky 22.11.2011 09:40

По сравнению с решением Криса, приведенным ниже, может быть снижение производительности из-за использования preg_match_all в пользу strpos. С другой стороны, файл читается удобными для памяти фрагментами, в отличие от file_get_contents. Однако решение не покрывает краеугольный камень маркера кадра, занимающего более двух частей, поэтому я немного отредактировал его.

untill 08.08.2014 17:08

Я обнаружил, что Paint Tool SAI и Photoshop CS6 иногда не включают 00 в конце блока расширения приложения, что означает, что первый \x00 можно опустить.

rr- 23.05.2015 10:37

Анимированный 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 за абсолютную правду. В то время файлы были небольшими, и их чтение было быстрее, чем побайтово, по причинам, которые я не исследовал или не помню, чтобы исследовать.

Kris 13.02.2017 15:29

Обратите внимание, что fread($fp, 1) === "\x2c" || fread($fp, 2) === "\x21\xf9" сначала прочитает один байт (A), проверьте, что это 0x2C, в противном случае он будет читать два байта после байта A, а не после байта 0x00. Таким образом, соответствующие строки байтов - это 00 2C или 00 ** 21 F9, где ** означает любой байт. Я не уверен, задумано это или нет, но из кода это не очень ясно.

Qtax 03.08.2017 21:23

Это улучшение текущего ответа, получившего наибольшее количество голосов, но у меня пока недостаточно репутации, чтобы комментировать. Проблема с этим ответом заключается в том, что он считывает файл фрагментами по 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;
}

Другие вопросы по теме