web-dev-qa-db-ja.com

phpとgdを使用してアニメーションGIFを検出できますか?

現在、Gdを使用して画像のサイズを変更する際に問題が発生しています。

黒の背景に最初のフレームを配信するアニメーションGIFのサイズを変更するまで、すべて正常に機能します。

getimagesizeを使用してみましたが、寸法が表示されるだけで、gifとアニメーションのgifを区別することはできません。

アニメーションGIFの場合、実際のサイズ変更は必要ありません。スキップできるだけで十分です。

手がかりはありますか?

PS。 imagemagickにアクセスできません。

敬具、

クリス

44
Kris

imagecreatefromgif()関数のPHPマニュアルページ)には、必要なコードの簡単なスニペットがあります。

imagecreatefromgifコメント#59787 by ZeBadger

18
Davide Gualano

同じ問題の解決策を探しているときに、php.netサイトにDavideとKrisが参照しているコードのフォローアップがあることに気付きましたが、著者によると、メモリとディスクの負荷が少ないとのことです。 。

興味があるかもしれないので、ここで複製します。

ソース: 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

仕事関数は次のとおりです。

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

指定されたファイルが大きすぎる場合、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;
}
2
hdogan

これは現在の上位投票の回答を改善したものですが、まだコメントするのに十分な評判がありません。その答えの問題は、ファイルを100Kbチャンクで読み取り、フレームの終わりマーカーが2つのチャンクに分割される可能性があることです。その修正は、前のフレームの最後の20bを次のフレームに追加することです。

<?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