【问题标题】:libpng size of pnglibpng png的大小
【发布时间】:2011-08-11 08:47:09
【问题描述】:

我的程序从不确定长度的数据流中读取。当它读取 PNG 文件时,它需要将其存储以备后用,而不是对其进行解码。 libpng 是否提供任何函数来提供 PNG 的大小以便知道要存储多少字节?

如果没有,我是否必须编写自己的 PNG 块解析器来跳过块直到结束块?我想我也可以包括 CRC 验证,但它看起来很复杂,这就是为什么我希望 libpng 能够提供一种透明地执行此操作的方法。

【问题讨论】:

    标签: c libpng


    【解决方案1】:

    这是一个简单 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;
    }
    

    【讨论】:

      【解决方案2】:

      这可能很愚蠢,但是如果您不需要解码 PNG,为什么不忘记它是 PNG,只需通过分配固定的最小内存量并将分配的大小加倍来存储它@@ 987654321@?

      【讨论】:

      • 数据流中还包含PNG之后的其他数据,所以程序需要知道PNG数据的结尾在哪里。
      【解决方案3】:

      简短的回答是否定的。 Libpng 甚至不返回单个块的长度,因此如果您使用 libpng,您确实必须跳过所有块才能找到 PNG 数据流的结尾。

      libpng 不返回块长度的原因是 libpng 是为流式传输而设计的,因此它不一定知道您的不确定长度的数据流将有多长。它所知道的只是它已经读取了其头部的块的长度。

      您可以使用“pngcheck”来读取 PNG 文件并生成带有长度的块列表。请参阅http://www.libpng.org/pub/png/apps/pngcheck.html‎ Pngcheck 是开源的,因此您可以根据自己的需要进行调整。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-24
        • 2017-11-17
        • 2017-10-29
        • 2016-02-24
        • 2012-11-14
        • 1970-01-01
        • 2019-07-27
        • 2014-03-30
        相关资源
        最近更新 更多