我上传到我网站的PNG(以及JPEG)图片。
它们应该是静态的(即一帧)。
有APNG之类的东西。
(它将在Firefox中设置动画)。
根据Wikipedia article ...
APNG隐藏PNG辅助组块中的后续帧,使得APNG不知道的应用程序会忽略它们,但是格式没有改变,以允许软件区分动画和非动画图像。
这是否意味着无法确定PNG是否使用代码进行动画制作?
如果有可能,请指点我正确的方向PHP(GD,ImageMagick)?
答案 0 :(得分:15)
APNG图像旨在为不支持它们的读者“伪装”为PNG。也就是说,如果一个阅读器不支持它们,它只会假设它是一个普通的PNG文件而只显示第一帧。这意味着它们具有与PNG(image / png)相同的MIME类型,它们具有相同的幻数(89 50 4e 47 0d 0a 1a 0a
)并且通常它们以相同的扩展名保存(尽管这不是一个很好的方法检查文件类型)。
那么,你如何区分它们?
APNG中有一个“acTL”块。因此,如果您搜索字符串acTL
(或者,在十六进制中,61 63 54 4C
(块标记之前的4个字节(即00 00 00 08
)是大端格式的块的大小,不计算字段末尾的大小,标记或CRC32))你应该很好。为了更好地做到这一点,请检查此块在第一次出现“IDAT”块之前出现(只需查找IDAT
)。
此代码(取自http://foone.org/apng/identify_apng.php)将起到作用:
<?php
# Identifies APNGs
# Written by Coda, functionified by Foone/Popcorn Mariachi#!9i78bPeIxI
# This code is in the public domain
# identify_apng returns:
# true if the file is an APNG
# false if it is any other sort of file (it is not checked for PNG validity)
# takes on argument, a filename.
function identify_apng($filename)
{
$img_bytes = file_get_contents($filename);
if ($img_bytes)
{
if(strpos(substr($img_bytes, 0, strpos($img_bytes, 'IDAT')),
'acTL')!==false)
{
return true;
}
}
return false;
}
?>
答案 1 :(得分:5)
AFAIK,不支持APNG的图书馆将只占用PNG的第一帧。在您的情况下,您可以从APNG(或PNG,JPEG等)创建新图像并将其重新保存为PNG。如果使用GD,它应该剥离动画数据,除非库已更新为支持APNG。
答案 2 :(得分:0)
我想建议一个更优化的版本,它不会读取整个文件,因为它们可能很大,并且在IDAT规则之前仍然依赖acTL:
function identify_apng($filepath) {
$apng = false;
$fh = fopen($filepath, 'r');
$previousdata = '';
while (!feof($fh)) {
$data = fread($fh, 1024);
if (strpos($data, 'acTL') !== false) {
$apng = true;
break;
} elseif (strpos($previousdata.$data, 'acTL') !== false) {
$apng = true;
break;
} elseif (strpos($data, 'IDAT') !== false) {
break;
} elseif (strpos($previousdata.$data, 'IDAT') !== false) {
break;
}
$previousdata = $data;
}
fclose($fh);
return $apng;
}
根据文件的大小,速度从5倍提高到10倍或更多,并且占用的内存更少。
NB:这可以通过读取的大小或将前一个块与当前块串联来进一步调整。顺便说一下,我们需要这种连接,因为acTL / IDAT字可能会在两个读取的块之间分割。
答案 3 :(得分:0)
这是我的函数,它扫描块结构,而不仅仅是文件内的子字符串(如果 acTL
子字符串出现在元数据中而不是块名称中,则防止误报)。
为简单起见,我使用了 SplFileObject,直接使用 fopen/fread/fclose 可以提高速度。
function is_apng(string $filename): bool
{
$f = new \SplFileObject($filename, 'rb');
$header = $f->fread(8);
if ($header !== "\x89PNG\r\n\x1A\n") {
return false;
}
while (!$f->eof()) {
$bytes = $f->fread(8);
if (strlen($bytes) < 8) {
return false;
}
$chunk = unpack('Nlength/a4name', $bytes);
switch ($chunk['name']) {
case 'acTL':
return true;
case 'IDAT':
return false;
}
$f->fseek($chunk['length'] + 4, SEEK_CUR);
}
return false;
}