web-dev-qa-db-fra.com

Puis-je détecter des gifs animés en utilisant php et gd?

Je rencontre actuellement des problèmes de redimensionnement d'images à l'aide de D.ieu.

Tout fonctionne bien jusqu'à ce que je veuille redimensionner un gif animé, qui affiche la première image sur un fond noir.

J'ai essayé d'utiliser getimagesize mais cela ne me donne que des dimensions et ne permet pas de distinguer entre n'importe quel gif et un animé.

Le redimensionnement réel n’est pas requis pour les gifs animés, il suffit de pouvoir les ignorer suffirait à nos fins.

Des indices?

PS Je n'ai pas accès à imagemagick.

Sincères amitiés,

Kris

43
Kris

Il y a un bref extrait de code dans la page de manuel PHP de la fonction imagecreatefromgif() qui devrait correspondre à ce dont vous avez besoin:

imagecreatefromgif comment # 59787 de ZeBadger

18
Davide Gualano

En cherchant une solution au même problème, j'ai remarqué que le site php.net avait une suite au code auquel Davide et Kris font référence, mais selon l'auteur, il nécessitait moins de mémoire, voire de disque. .

Je vais le reproduire ici, car cela pourrait être intéressant.

source: 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;
}
38
Martijn Heemels

Voici la fonction de travail:

/**
 * 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;
}
7
Kris

Lire un fichier entier avec file_get_contents peut prendre trop de mémoire si le fichier donné est trop volumineux. J'ai re-factorisé la fonction donnée précédemment, qui lit juste assez d'octets pour vérifier les images et revient dès qu'elle trouve au moins 2 images.

<?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;
}
2
hdogan

Ceci est une amélioration de la réponse actuelle la plus votée mais je n'ai pas encore assez de réputation pour commenter. Le problème avec cette réponse est qu’il lit le fichier en morceaux de 100 Ko et que le marqueur de fin de trame peut être divisé en 2 morceaux. Une solution pour cela consiste à ajouter le dernier 20b de l'image précédente à l'image suivante:

<?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;
}
0
Simon Duduica