Способ упрощения обработки заголовка PNM (PBM / PGM / PPM) состоит в построчном построении строки заголовка до тех пор, пока вы не соберете все необходимые данные. Для этого не нужно слишком много кода, используя только стандартные библиотеки C ++ ...
#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");
Это обрабатывает комментарии (если они есть) и особый случай PBM (без 'maxsample') - и работает независимо от того, включены ли исключения во входном потоке.
После прочтения заголовка чтение данных изображения обычно становится простым делом, поскольку формат определяется как просто последовательный дамп данных (который может быть либо ASCII, либо двоичным в зависимости от значения «magic»). В случае 16-битных двоично-кодированных выборок спецификация формата указывает, что «самый старший байт - первый» (с прямым порядком байтов), поэтому в этом случае может потребоваться некоторая специфичная для платформы обработка.
Как написано, для этого требуется C ++ 11 - возможно, из-за того, что я использую stringstream
в качестве временного.
Одно предостережение: в патологическом случае это может привести к потере большого количества времени / ОЗУ при попытке прочитать недопустимый заголовок - поскольку вызов getline
по своей природе не ограничен , Существует относительно простое решение ( заменить getline
чем-то более надежным ), но для этого требуется немного больше кода.
Для приложений производственного качества рассмотрите возможность использования libnetpbm .