Comment puis-je manuellement lire les fichiers PNG en C++?

Portable Network Graphics Aperçu

La disposition générale d'un fichier PNG ressemble à ceci:

Fichier d'en-Tête: 8 octets de signature.

Comment puis-je manuellement lire les fichiers PNG en C++?

Morceaux: des Blocs de données allant de propriétés de l'image à l'image elle-même.


Le Problème

Je veux lire des fichiers PNG en C++ sans utiliser de bibliothèques externes. Je veux faire cela pour acquérir une compréhension plus approfondie de format PNG et le langage de programmation C++.

J'ai commencé à l'aide de fstream de lire des images octet-par-octet, mais je ne peux pas obtenir au-delà de l'en-tête de fichier PNG. J'ai essayer d'utiliser read( char*, int ) de mettre les octets dans char des tableaux, mais read échoue sur chaque octet de l'en-tête.

Comme vu ci-dessus, je pense que mon programme obtient toujours rattrapé sur la fin-de-fichier 1A octet. Je suis le développement sur Windows 7 pour Windows 7 et Linux.


Certains de Mes (Vieux) 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 ] = '
#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 ] = '\0';
std::cout << "Data size: " << std::strlen( data ) << std::endl;
}
'
; std::cout << "Data size: " << std::strlen( data ) << std::endl; }

La sortie est toujours semblable à ceci:

Attempting to open image.png
File size: 1768222
Data size: 0

La taille du fichier est correct, mais la taille des données est clairement erronée. Notez que j'essaie d'ignorer l'en-tête (éviter la fin-de-fichier de caractères) et aussi pour ce moment de déclarer la taille de char* data.

Voici quelques données les valeurs de la taille quand je modifie le file.seekg( ... ); ligne en conséquence:

file.seekg( n );             data size
----------------             ---------
0                            8
1                            7
2                            6
...                          ...
8                            0
9                            0
10                           0

Certains de Mes Nouveau 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 ] = '
#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 ] = '\0';
std::cout << "Data size: " << ((unsigned long long)file.tellg() - 8) << std::endl;
}
'
; std::cout << "Data size: " << ((unsigned long long)file.tellg() - 8) << std::endl; }

J'ai essentiellement juste modifié le Data size: ligne. Une chose à noter est la sortie de la Data size: ligne est toujours très proche de la valeur maximale de ce type je lance file.tellg() à.

  • strlen s'arrête à la première terminateur null, vous êtes en supposant que le terminateur null vous ajoutez à la fin de la mémoire tampon est le seul. Généralement pas une bonne idée de traiter des données binaires comme une chaîne de texte.
  • Mais comment puis-je obtenir ces données, les valeurs de la taille alors? Ce que vous avez dit sorte de bon sens, mais aussi semble impliquer que chaque valeur après l'en-tête est un terminateur null en fonction de ces valeurs: fichier.seekg(0), taille des données: 8; fichier.seekg(1), la taille des données: 7; fichier.seekg(2), la taille des données: 6; ... de fichier.seekg(8), la taille des données: 0; fichier.seekg(9), la taille des données: 0; fichier.seekg(10), la taille des données: 0; ...
  • Je ne suis pas ce qui implique que tout. Les données stockées dans un format de PNG devraient être traitées comme des données binaires, cela signifie que vous ne devez jamais supposer un terminateur null et strlen va être la bonne façon de procéder. Vous avez besoin d'examiner le format de fichier PNG et de commencer à interpréter les données pour ce qu'il est réellement, au lieu de supposer que c'est juste un tas de chaînes de caractères.
  • I want to read PNG files in C++ without using anything other than STL Votre code ne pas utiliser quoi que ce soit à partir de la STL. Si elle n'a, à tout le moins, vous aurez remplacer new[] avec std::vector
  • Oh, si, comme un octet peut représenter quelques pixels, mais en même temps être un terminateur null? Pourtant, je devrait être en mesure de pousser chaque octet dans un char, non? Je veux dire, tous les octets sera un terminateur null
  • Excuse mon manque de compréhension de la STL. Je voulais dire que je ne veux pas utiliser toutes les bibliothèques externes.
  • Les données de l'image elle-même peut avoir des caractères null. Ces caractères nuls dans les données d'image n'ont rien à voir avec de la ficelle de résiliation -- c'est juste les données qui est là. Ainsi, vous n'avez pas l'incorporer à l'aide de fonctions de chaîne qui s'arrêtent sur les valeurs null.
  • Je vois. Alors, comment puis-je lire les données, alors? Est-il une sorte de "petit lecteur" que je peux utiliser n'est pas moulé pour traiter des chaînes de caractères?
  • Être informé que "un octet peut représenter un pixel" n'est pas vrai pour les raw, PNG fichier de données. Si vous attendez, vous avez besoin de lire les spécifications de nouveau, et de décider ensuite si vous voulez mettre en œuvre dégonfler la décompression, ou choisir une simple image pour commencer (TGA, BMP, ou PCX).
  • Pour info les fichiers PNG sont compressés, je ne voudrais pas re-écrire zlib si...
  • Si vous ne vous souciez pas de savoir le format, vous pouvez utiliser le MFC CImage classe et de son Load méthode

InformationsquelleAutor user3745189 | 2015-06-26