目前我在使用GD调整图像大小时遇到了一些问题。
一切正常,直到我想要缩放动画gif,这将在黑色背景上提供第一帧。
我尝试使用getimagesize
,但它只给出了尺寸,无法区分普通gif和动画gif。
对于动画gif,实际上不需要调整大小,只需要能够跳过它们即可满足我们的需求。
有任何线索吗?
附言:我没有访问imagemagick的权限。
此致
Kris
目前我在使用GD调整图像大小时遇到了一些问题。
一切正常,直到我想要缩放动画gif,这将在黑色背景上提供第一帧。
我尝试使用getimagesize
,但它只给出了尺寸,无法区分普通gif和动画gif。
对于动画gif,实际上不需要调整大小,只需要能够跳过它们即可满足我们的需求。
有任何线索吗?
附言:我没有访问imagemagick的权限。
此致
Kris
在寻找解决相同问题的方案时,我注意到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;
}
\x00
可以省略。 - rr-在imagecreatefromgif()
函数的PHP手册页面中,有一小段代码片段应该是你所需要的:
/**
* 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;
}
这是对当前最受欢迎的答案的改进,但我还没有足够的声望来发表评论。那个答案的问题在于它以100Kb的块读取文件,而帧结束标记可能会分裂在两个块之间。解决方法是将前一个帧的最后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;
}
如果要读取的文件太大,使用file_get_contents
读取整个文件可能会占用过多的内存。我已经重新设计了之前的函数,它只读取足够的字节来检查帧并在找到至少两个帧时立即返回。
<?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;
}
fread($fp, 1) === "\x2c" || fread($fp, 2) === "\x21\xf9"
首先会读取一个字节(A),然后检查它是否为 0x2C,如果不是,则会读取字节A之后的两个字节,而不是在0x00字节之后。因此匹配的字节字符串为00 2C
或00 ** 21 F9
,其中**
表示任意字节。我不确定这是否是有意为之的,但从代码中并不十分清晰。 - Qtax动画 GIF 必须具有以下字符串
"\x21\xFF\x0B\x4E\x45\x54\x53\x43\x41\x50\x45\x32\x2E\x30"
我已在几个动画 GIF 上进行了测试,发现字符串位于文件的位置 781(使用 file_get_contents 和 strpos 找到)。