A way of simplifying PNM (PBM/PGM/PPM) header processing is to build up a header string line-by-line until you have captured all of the requisite data. It doesn't take too much code to do this using only the standard C++ libraries...
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
...
std::string header, magic;
int width=0, height=0, maxsample=0, samples=0, bits=0, bytes=0;
do {
try { getline(is,magic); } catch ( const std::ios_base::failure & ) {}
if ( !magic.empty() && magic[0] != '#' ) header += magic+" ";
if ( !( std::stringstream(header+" 1") >> magic >> width >> height >> maxsample ).eof() ) break;
if ( ( (magic=="P1"||magic=="P4") && maxsample==1 ) || !is.good() ) break;
} while ( true );
samples = magic=="P1"?1:magic=="P2"?1:magic=="P3"?3:magic=="P4"?1:magic=="P5"?1:magic=="P6"?3:0;
bits = (magic=="P1"||magic=="P4")?1:maxsample<256?8:maxsample<256*256?16:0, bytes = (width*samples*bits+7)>>3;
if ( width<=0 || height<=0 || maxsample<=0 || samples<=0 || bits<=0 ) throw std::runtime_error("invalid PNM header");
This handles comments (if present) and the special-case of PBM (no 'maxsample') -- and it works regardless of whether or not exceptions are enabled on the input stream.
Once you've read the header, reading the image data is usually a simple matter since the format is defined to just be a sequential data dump (which may be either ASCII or binary depending on the 'magic' value). In the case of 16-bit binary-encoded samples, the format specification indicates that "The most significant byte is first" (big endian), so this case may require some platform-specific handling.
As written, this requires C++11 -- probably due to the way I'm using stringstream
as a temporary.
One caveat: In a pathological case, it is possible for this to waste a lot of time/RAM while attempting to read an invalid header -- since the getline
call isn't inherently bounded. There's a relatively simple solution (replace getline
with something more robust), but it requires a bit more code.
For production-quality applications, consider using libnetpbm.