【发布时间】:2011-08-11 08:47:09
【问题描述】:
我的程序从不确定长度的数据流中读取。当它读取 PNG 文件时,它需要将其存储以备后用,而不是对其进行解码。 libpng 是否提供任何函数来提供 PNG 的大小以便知道要存储多少字节?
如果没有,我是否必须编写自己的 PNG 块解析器来跳过块直到结束块?我想我也可以包括 CRC 验证,但它看起来很复杂,这就是为什么我希望 libpng 能够提供一种透明地执行此操作的方法。
【问题讨论】:
我的程序从不确定长度的数据流中读取。当它读取 PNG 文件时,它需要将其存储以备后用,而不是对其进行解码。 libpng 是否提供任何函数来提供 PNG 的大小以便知道要存储多少字节?
如果没有,我是否必须编写自己的 PNG 块解析器来跳过块直到结束块?我想我也可以包括 CRC 验证,但它看起来很复杂,这就是为什么我希望 libpng 能够提供一种透明地执行此操作的方法。
【问题讨论】:
这是一个简单 PNG 块转储器。它不会尝试 CRC 验证或存储块的数据,但应该至少给出一些关于如何读取块的一般概念。警告:这只是我匆忙拼凑起来的东西,从来没有升级的需要,所以无论如何它都不是最干净的代码的例子。但是,它会验证文件是否至少包含一些正确的 PNG 文件签名,并显示文件中包含的图像的尺寸。
#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,只需通过分配固定的最小内存量并将分配的大小加倍来存储它@@ 987654321@?
【讨论】:
简短的回答是否定的。 Libpng 甚至不返回单个块的长度,因此如果您使用 libpng,您确实必须跳过所有块才能找到 PNG 数据流的结尾。
libpng 不返回块长度的原因是 libpng 是为流式传输而设计的,因此它不一定知道您的不确定长度的数据流将有多长。它所知道的只是它已经读取了其头部的块的长度。
您可以使用“pngcheck”来读取 PNG 文件并生成带有长度的块列表。请参阅http://www.libpng.org/pub/png/apps/pngcheck.html Pngcheck 是开源的,因此您可以根据自己的需要进行调整。
【讨论】: