之前写过一个使用 FFmpeg 类对图片实现了解码、转码、裁剪、缩放等功能,发现比 ImageMagic 快多了。详情见 【AVD】杀鸡用牛刀,FFmpeg API 加载存储图片,比 ImageMagic 和 stb_image 快多了。但是,在文章 【AVD】C++ 不解码获取 JPG 图片宽高、旋转信息等 EXIF 信息 中提到过,如果我们只需要获取图片的宽高信息,而不用解码图片时,现有的 FFmpeg 代码并不能在解码之前得到一些格式的图片,现在已知的 PNG、JPG 均不能获得,而 GIF 是可以在解码前就通过 AVStream->codecpar 获得的。
因此,找了一个直接读文件头的方法来获取 PNG 图片的宽高。这要比用 FFmpeg 对 PNG 图片进行解码后获取其宽高快一些。
在 Windows 上可以用 UltraEditor、7yuv 等软件 打开一张 png 然后看到它的 HEX 十六进制表示,在 Linux 上可以使用 vi 或 vim 打开一张 png ,然后输入 :%!xxd
查看其十六进制内容。下图是一张 png 图片在 Linux 上的十六进制图:
参考文章 PNG文件头格式解析,我们可以知道,PNG 文件以 89 50 4E 47 0D 0A 1A 0A
开始,是PNG头部署名域,表示这是一个PNG图片。
后面蓝色框中的内容表示了 IHDR 头部的大小,是 13 个字节。
再往后的绿色框内被称为 Chunk Type Code,这里表示了这个 chunk 是个 IHDR。
根据上图,我们可以得知,该 png 图片的 Width = 0x0000 01d0 = 464,Height = 0x0000 0198 = 408,Bit depth = 0x08 = 8,Color Type = 0x06 = 带α通道真彩色,压缩方法、滤波器方法均为 0,非隔行扫描。
因此,拿 C++ 读文件,然后按上述方法去提取相关信息即可:
#include
using std::ifstream;
using std::string;
unsigned int get_unsigned_int(unsigned char data[4]) {
return (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3];
}
void GetPngWH(string filename) {
ifstream fin;
fin.open(filename, std::ios_base::binary | std::ios_base::in);
if (!fin.good()) {
AVLOGE("Error to open file %s, because %d.", filename.c_str(), fin.get());
return false;
}
char data[4];
fin.read(data, 4);
if (string(data + 1).compare("PNG") != 0) {
AVLOGE("File %s is not a png file.", filename.c_str());
fin.close();
return false;
}
fin.seekg(8, std::ios_base::cur);
fin.read(data, 4);
if (string(data).compare("IHDR") != 0) {
AVLOGE("Error to find IHDR of the png file %s", filename.c_str());
fin.close();
return false;
}
fin.read(data, 4);
width_ = get_unsigned_int((unsigned char *)data);
fin.read(data, 4);
height_ = get_unsigned_int((unsigned char *)data);
fin.close();
}