我的程序从不确定长度的数据流中读取。当它读取 PNG 文件时,它需要将其存储以备后用,而不是对其进行解码。libpng 是否提供任何函数来提供 PNG 的大小以便它知道要存储多少字节?
如果没有,我是否必须编写自己的 PNG 块解析器来跳过块直到结束块?我想我也可以包括 CRC 验证,但它看起来很复杂,这就是为什么我希望 libpng 能够提供一种透明地执行此操作的方法。
最简洁的答案是不。Libpng 甚至不返回单个块的长度,因此如果您使用 libpng,您确实必须跳过所有块以找到 PNG 数据流的结尾。
libpng 不返回块长度的原因是 libpng 是为流式设计的,因此它不一定知道您的不确定长度的数据流将有多长。它所知道的只是它已经读取了其标题的块的长度。
您可以使用“pngcheck”来读取 PNG 文件并生成带有长度的块列表。请参阅http://www.libpng.org/pub/png/apps/pngcheck.html Pngcheck 是开源的,因此您可以根据自己的需要调整它。
This is a simple PNG chunk-dumper. It doesn't attempt CRC verification or storing the data for the chunks, but should give at least some general notion of how to read through the chunks. Warning: this is just something I whipped together in a hurry and have never had a need to upgrade, so it's not an example of the cleanest code ever, by any stretch of the imagination. It does, however, verify that the file contains at least some of the correct signatures for a PNG file, and display the dimensions of the image contained in the file.
#include <iostream>
#include <fstream>
#include <winsock.h>
#include <algorithm>
#pragma comment(lib, "ws2_32.lib")
typedef unsigned int uint32_t;
bool file_header(std::istream &in) {
unsigned char buffer[8];
static const unsigned char valid[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
in.read((char *)&buffer, sizeof(buffer));
return std::mismatch(buffer, buffer+sizeof(buffer), valid).first == buffer+sizeof(buffer);
}
long read_net_long(std::istream &in) {
long temp;
in.read((char *)&temp, sizeof(temp));
return ntohl(temp);
}
bool is_iend(std::istream &in) {
uint32_t length;
char sig[5] = {0};
char iend[5] = "IEND";
uint32_t CRC;
length = read_net_long(in);
in.read((char *)&sig, 4);
in.ignore(length);
CRC = read_net_long(in);
std::cout << "found: " << sig << ", length = " << length << "\n";
return std::mismatch(sig, sig+sizeof(sig), iend).first == sig+sizeof(sig);
}
#pragma pack(push, 1)
class ihdr {
uint32_t signature;
uint32_t length;
uint32_t width;
uint32_t height;
unsigned char depth;
unsigned char color_type;
unsigned char compression_method;
unsigned char filter_method;
unsigned char interlacing;
uint32_t CRC;
public:
friend std::istream &operator>>(std::istream &is, ihdr &h) {
is.read((char *)&h, sizeof(h));
if (h.signature != 218103808)
std::cerr << "Invalid chunk: " << h.signature << "\n";
h.width = ntohl(h.width);
h.height = ntohl(h.height);
return is;
}
friend std::ostream &operator<<(std::ostream &os, ihdr const &h) {
return std::cout << "width: " << h.width << ", height: " << h.height << "\n";
}
};
#pragma pack(pop)
void dump_ihdr(std::istream &in) {
ihdr header;
in >> header;
std::cout << header;
}
int main(int argc, char **argv) {
if (argc != 2) {
std::cerr << "Usage: read_png <png_file>";
return 1;
}
std::ifstream in(argv[1], std::ios::binary);
std::cout << argv[1] << "\n";
if (file_header(in)) {
dump_ihdr(in);
while (!is_iend(in))
if (!in) {
std::cout << "Reached EOF without finding IEND chunk\n";
return 1;
}
}
else {
std::cout << "Didn't find a PNG header\n";
return 1;
}
return 0;
}
这可能非常愚蠢,但是如果您不需要解码 PNG,为什么不忘记它是 PNG,并通过分配固定的最小内存量并将分配的大小加倍来存储它realloc
呢?