如何在 C++ 中手动读取 PNG 文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31079947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I manually read PNG files in C++?
提问by user3745189
Portable Network Graphics Overview
便携式网络图形概述
The general layout of any given PNG file looks like this:
任何给定 PNG 文件的一般布局如下所示:
File Header: An 8-byte signature.
文件头:一个 8 字节的签名。
Chunks: Chunks of data ranging from image properties to the actual image itself.
块:从图像属性到实际图像本身的数据块。
The Problem
问题
I want to read PNG files in C++ without using any external libraries. I want to do this to gain a deeper understanding of both PNG format and the C++ programming language.
我想在不使用任何外部库的情况下在 C++ 中读取 PNG 文件。我想这样做是为了更深入地了解 PNG 格式和 C++ 编程语言。
I started off using fstream
to read images byte-by-byte, but I can't get past the header of any PNG file. I try using read( char*, int )
to put the bytes into char
arrays, but read
fails on every byte after the header.
我开始使用fstream
逐字节读取图像,但我无法越过任何 PNG 文件的标题。我尝试使用read( char*, int )
将字节放入char
数组,但read
在标头后的每个字节上都失败。
As seen above, I think my program always gets caught up on the end-of-file 1A
byte. I'm developing on Windows 7 for Windows 7 and Linux machines.
如上所述,我认为我的程序总是被文件结尾1A
字节所困扰。我正在为 Windows 7 和 Linux 机器在 Windows 7 上进行开发。
Some of My (Old) Code
我的一些(旧)代码
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstddef>
const char* INPUT_FILENAME = "image.png";
int main()
{
std::ifstream file;
size_t size = 0;
std::cout << "Attempting to open " << INPUT_FILENAME << std::endl;
file.open( INPUT_FILENAME, std::ios::in | std::ios::binary | std::ios::ate );
char* data = 0;
file.seekg( 0, std::ios::end );
size = file.tellg();
std::cout << "File size: " << size << std::endl;
file.seekg( 0, std::ios::beg );
data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size );
data[ size ] = 'Attempting to open image.png
File size: 1768222
Data size: 0
';
std::cout << "Data size: " << std::strlen( data ) << std::endl;
}
The output is always similar to this:
输出总是类似于:
file.seekg( n ); data size
---------------- ---------
0 8
1 7
2 6
... ...
8 0
9 0
10 0
The file size is correct, but data size is clearly incorrect. Note that I try to skip the header (avoid the end-of-file character) and also account for this when declaring the size of char* data
.
文件大小正确,但数据大小显然不正确。请注意,我尝试跳过标题(避免文件结尾字符),并且在声明char* data
.
Here are some data size values when I modify the file.seekg( ... );
line accordingly:
以下是我相应地修改该file.seekg( ... );
行时的一些数据大小值:
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstddef>
const char* INPUT_FILENAME = "image.png";
int main()
{
std::ifstream file;
size_t size = 0;
std::cout << "Attempting to open " << INPUT_FILENAME << std::endl;
file.open( INPUT_FILENAME, std::ios::in | std::ios::binary | std::ios::ate );
char* data = 0;
file.seekg( 0, std::ios::end );
size = file.tellg();
std::cout << "File size: " << size << std::endl;
file.seekg( 0, std::ios::beg );
data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size );
data[ size ] = 'data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size ); // <-- here
data[ size ] = 'if (file)
std::cout << "all characters read successfully.";
else
std::cout << "error: only " << file.gcount() << " could be read";
'; // <-- and here
';
std::cout << "Data size: " << ((unsigned long long)file.tellg() - 8) << std::endl;
}
Some of My New Code
我的一些新代码
file.read( data, size-8 );
I essentially just modified the Data size:
line. A thing to note is the output of the Data size:
line is always really close to the maximum value of whatever type
I cast file.tellg()
to.
我基本上只是修改了这Data size:
条线。需要注意的是,该Data size:
行的输出总是非常接近type
我所投射file.tellg()
到的最大值。
回答by usr2564301
Your (new) code contains two essentialerrors:
您的(新)代码包含两个基本错误:
data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size );
data[ size ] = 'const char* INPUT_FILENAME = "ban hammer.png";
int main()
{
std::ifstream file;
size_t size = 0;
std::cout << "Attempting to open " << INPUT_FILENAME << std::endl;
file.open(INPUT_FILENAME, std::ios::in | std::ios::binary);
char* data = 0;
file.seekg(0, std::ios::end);
size = file.tellg();
std::cout << "File size: " << size << std::endl;
file.seekg(0, std::ios::beg);
data = new char[size - 8 + 1];
file.seekg(8); // skip the header
file.read(data, size - 8);
data[size] = 'file.read( data, size );
Size_t data_size = file.tellg() - 8;
std::cout << "Data size: " << data_size << std::endl;
';
std::cout << "Data size: " << file.tellg() << std::endl;
cin.get();
return 0;
}
';
if(file.good()) // make sure we had a good read.
std::cout << "Data size: " << file.tellg() - 8 << std::endl;
First off, you want to read the data without the 8 byte prefix, and you allocate the right amount of space (not really, see further). But at that point, size
still holds the totalamount of bytes of the file, including the 8 byte prefix. Since you ask to read size
bytes and there are only size-8
bytes remaining, the file.read
operation fails. You don't check for errors and so you do not notice file
is invalidated at that point. With an error check you should have seen this:
首先,您想要读取没有 8 字节前缀的数据,并分配适当的空间量(不是真的,请参阅进一步)。但此时,size
仍然保存了文件的总字节数,包括 8 字节前缀。由于您要求读取size
字节并且只剩下size-8
字节,因此file.read
操作失败。您不会检查错误,因此您不会注意到此时file
无效。通过错误检查,您应该已经看到了:
Size_t data_size = file.readsome( data, size );
std::cout << "Data size: " << data_size << std::endl;
Because file
is invalid from that point on, all operations such as your later file.tellg()
return -1
.
因为file
从那时起就无效了,所有的操作比如你以后都file.tellg()
返回-1
。
The second error is data[size] = '\0'
. Your buffer is not that large; it should be data[size-8] = 0;
. Currently, you are writing into memory beyond what you allocated earlier, which causes Undefined Behavior and may lead to problems later on.
第二个错误是data[size] = '\0'
。你的缓冲区没有那么大;应该是data[size-8] = 0;
。目前,您正在写入超出您之前分配的内存的内存,这会导致未定义行为并可能导致以后出现问题。
Butthat last operation clearly shows you are thinking in terms of character strings. A PNG file is not a string, it is a binary stream of data. Allocating +1
for its size and setting this value to 0
(with the unnecessary "character-wise" way of thinking, with '\0'
) is only useful if the input file is of a string type – say, a plain text file.
但是最后一个操作清楚地表明您正在考虑字符串。PNG 文件不是字符串,而是二进制数据流。分配+1
其大小并将此值设置为0
(使用不必要的“字符”思维方式,使用'\0'
)仅在输入文件是字符串类型时才有用 - 例如,纯文本文件。
A simple fix for your current issues is this (well, and add error checking for all your file operations):
对当前问题的简单修复是这样的(好吧,并为所有文件操作添加错误检查):
##代码##However, I would strongly advise you to look at a much simpler file format first. The PNG file format is compact and well documented; but it is also versatile, complicated, and contains highly compressed data. For a beginner it is way too hard.
但是,我强烈建议您首先查看更简单的文件格式。PNG 文件格式紧凑且文档齐全;但它也是通用的、复杂的,并且包含高度压缩的数据。对于初学者来说太难了。
Start with an easier image format. ppm
is a deliberately simple format, good to start with. tga
, old but easy, introduces you to several more concepts such as bit depths and color mapping. Microsoft's bmp
has some nice little caveats but can still be considered 'beginner friendly'. If you are interested in simple compression, the basic Run Length Encoding of a pcx
is a good starting point. After mastering that you could look in to the gif
format, which uses the much harder LZW compression.
从更简单的图像格式开始。ppm
是一种刻意简单的格式,很好的开始。tga
,古老而简单,向您介绍了更多的概念,例如位深度和颜色映射。微软bmp
有一些很好的小警告,但仍然可以被认为是“初学者友好的”。如果您对简单压缩感兴趣,a 的基本运行长度编码pcx
是一个很好的起点。掌握之后,您可以查看gif
使用更难的 LZW 压缩的格式。
Only if you succeed in implementing parsers for these, you may want to look at PNG again.
只有当您成功地为这些实现解析器时,您才可能想再次查看 PNG。
回答by NathanOliver
If you want to know how much data you read from the file then just use tellg()
again.
如果您想知道从文件中读取了多少数据,则只需tellg()
再次使用即可。
There is an error in you code with reading the data as well. You are reading to size
where size
is the size of the file which is 8 bytes more than you need since you are skipping the header. The correct code is
读取数据的代码也有错误。您正在阅读到size
这里size
是它是文件的大小为8个字节比你更需要,因为你是跳过头。正确的代码是
回答by cdonat
Solution 1:
解决方案1:
##代码##Even easier: Solution 2:
更简单:解决方案 2:
##代码##file.readsome() returns the number of bytes read.
file.readsome() 返回读取的字节数。