diff --git a/cvd/image_io.h b/cvd/image_io.h index c3f14fad..9924c5dc 100644 --- a/cvd/image_io.h +++ b/cvd/image_io.h @@ -234,7 +234,7 @@ namespace CVD throw Exceptions::Image_IO::EofBeforeImage(); if(c == 'P') - PNM::readPNM(im, i); + CVD::Internal::readImage(im, i); #ifdef CVD_HAVE_JPEG else if(c == 0xff) CVD::Internal::readImage(im, i); @@ -296,7 +296,7 @@ namespace CVD case ImageType::PNM: case ImageType::Automatic: case ImageType::Unknown: - Internal::writeImage(im, o, p); break; + Internal::writeImage(im, o, p); break; #ifdef CVD_HAVE_JPEG case ImageType::JPEG: Internal::writeImage(im,o, p); break; #endif diff --git a/cvd/internal/io/pnm_grok.h b/cvd/internal/io/pnm_grok.h index e18f3e36..dac6fd86 100644 --- a/cvd/internal/io/pnm_grok.h +++ b/cvd/internal/io/pnm_grok.h @@ -32,128 +32,43 @@ namespace CVD { namespace PNM { - class pnm_in - { + class pnm_in; + + using CVD::Internal::TypeList; + using CVD::Internal::Head; + class Reader + { public: - pnm_in(std::istream&); - bool is_2_byte()const {return m_is_2_byte;} - int channels(){return m_channels;} - long x_size() const {return xs;} - long y_size() const {return ys;} - long elements_per_line() const {return xs * m_channels;} - void get_raw_pixel_lines(unsigned char*, unsigned long nlines); - void get_raw_pixel_lines(unsigned short*, unsigned long nlines); - - + Reader(std::istream&); + ~Reader(); + + ImageRef size(); + + void get_raw_pixel_line(bool*); + void get_raw_pixel_line(unsigned char*); + void get_raw_pixel_line(unsigned short*); + void get_raw_pixel_line(Rgb*); + void get_raw_pixel_line(Rgb*); + + std::string datatype(); + std::string name(); + + typedef TypeList, + TypeList, + Head> > > > > Types; + private: - std::istream& i; - bool is_text; - int type, maxval; - int lines_so_far; - void read_header(); - bool can_proc_lines(unsigned long); - long xs, ys; - bool m_is_2_byte; - int m_channels; - }; - - template struct PNMReader; - - template struct PNMReader - { - typedef Rgb array; - static void readPixels(BasicImage& im, pnm_in& pnm) - { - std::vector rowbuf(pnm.x_size()); - for (int r=0; r::convert(&(rowbuf[0]), im[r], pnm.x_size()); - } - } + std::auto_ptr p; + }; - - template struct PNMReader - { - static void readPixels(BasicImage& im, pnm_in& pnm) - { - std::vector rowbuf(pnm.x_size()); - for (int r=0; r::convert(&(rowbuf[0]), im[r], pnm.x_size()); - } - } - }; - - template <> struct PNMReader,byte,3> - { - static void readPixels(BasicImage >& im, pnm_in& pnm) - { - pnm.get_raw_pixel_lines((byte*)im.data(), pnm.y_size()); - } - }; - template <> struct PNMReader - { - static void readPixels(BasicImage& im, pnm_in& pnm) - { - pnm.get_raw_pixel_lines(im.data(), pnm.y_size()); - } - }; - template <> struct PNMReader,unsigned short,3> - { - static void readPixels(BasicImage >& im, pnm_in& pnm) - { - pnm.get_raw_pixel_lines((unsigned short*)im.data(), pnm.y_size()); - } - }; - template <> struct PNMReader - { - static void readPixels(BasicImage& im, pnm_in& pnm) - { - pnm.get_raw_pixel_lines(im.data(), pnm.y_size()); - } - }; - - template void readPNM(BasicImage& im, pnm_in& pnm) - { - if (pnm.is_2_byte()) - { - if (pnm.channels() == 3) - PNMReader::readPixels(im, pnm); - else - PNMReader::readPixels(im, pnm); - } - else - { - if (pnm.channels() == 3) - PNMReader::readPixels(im, pnm); - else - PNMReader::readPixels(im, pnm); - } - } - - template void readPNM(BasicImage&im, std::istream& in) - { - pnm_in pnm(in); - ImageRef size(pnm.x_size(), pnm.y_size()); - if(size != im.size()) - throw Exceptions::Image_IO::ImageSizeMismatch(size, im.size()); - - readPNM(im, pnm); - } - - template void readPNM(Image&im, std::istream& in) - { - pnm_in pnm(in); - im.resize(ImageRef(pnm.x_size(), pnm.y_size())); - readPNM(im, pnm); - } //////////////////////////////////////////////////////////////////////////////// // // PNM writing. @@ -164,11 +79,12 @@ namespace CVD template<> struct ComponentMapper<0,1> { typedef byte type; }; template<> struct ComponentMapper<0,0> { typedef unsigned short type; }; - class pnm_writer + class pnm_writer; + class Writer { public: - pnm_writer(std::ostream&, ImageRef size, const std::string& type, const std::map >& p); - ~pnm_writer(); + Writer(std::ostream&, ImageRef size, const std::string& type, const std::map >& p); + ~Writer(); //void write_raw_pixel_line(const bool*); void write_raw_pixel_line(const unsigned char*); @@ -184,14 +100,7 @@ namespace CVD std::numeric_limits::digits <= 8>::type type; }; private: - - template void sanity_check(const P*); - void write_shorts(const unsigned short*, int n); - - long row; - std::ostream& o; - ImageRef size; - std::string type; + std::auto_ptr p; }; diff --git a/pnm_src/pnm_grok.cxx b/pnm_src/pnm_grok.cxx index 84dd033f..5ed9b474 100644 --- a/pnm_src/pnm_grok.cxx +++ b/pnm_src/pnm_grok.cxx @@ -1,26 +1,26 @@ /* - This file is part of the CVD Library. + This file is part of the CVD Library. - Copyright (C) 2005 The Authors + Copyright (C) 2005 The Authors - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /******************************************************************************* - pnm_grok.cxx Ed Rosten 2003 + pnm_grok.cxx Ed Rosten 2003 This file provides very basic functionality for accessing PNM images. PNM images are either greyscale (PBMs are treated as PGMs of depth 1) or RGB, and either 1 @@ -86,511 +86,618 @@ namespace CVD namespace PNM { - //All possible error codes. These are loosely divided in to data errors (ie - //problems with the input image) and programmer errors (runtime errors - //which are almost definitely caused by some logic error in the program). - enum errors - { - E_NONE = 0, //Data errors - E_NOT_PNM, - E_HEOF, - E_DEOF, - E_BAD_SIZE, - E_BAD_MAXVAL, - E_PBM_NOT_IMPLEMENTED, - E_OUT_OF_MEMORY, - E_UNTERMINATED_HEADER, - E_PAM_NOT_IMPLEMENTED, - E_LAST_DATA_ERROR, - - E_PROGRAMMER_ERROR = 255, //All data errors are sit below this + //All possible error codes. These are loosely divided in to data errors (ie + //problems with the input image) and programmer errors (runtime errors + //which are almost definitely caused by some logic error in the program). + enum errors + { + E_NONE = 0, //Data errors + E_NOT_PNM, + E_HEOF, + E_DEOF, + E_BAD_SIZE, + E_BAD_MAXVAL, + E_PBM_NOT_IMPLEMENTED, + E_OUT_OF_MEMORY, + E_UNTERMINATED_HEADER, + E_PAM_NOT_IMPLEMENTED, + E_LAST_DATA_ERROR, + + E_PROGRAMMER_ERROR = 255, //All data errors are sit below this - //Probably programmer errors: - E_NO_LONG_PBM, - E_WRITE_PAST_END, - E_NOT_2_BYTE, - E_NOT_1_BYTE, - E_READ_PAST_END, - - E_MAX //All errors sit below this - }; - - string name_error(errors e) - { - const char* data_error_names[]= - { - "None.", - "Not a PNM.", - "EOF in header.", - "EOF in data.", - "Bad size specified.", - "Invalid colour MAXVAL.", - "Binary PBMs are not implemented.", - "Out of memory.", - "Unterminated header.", - "Portable Arbirtary Maps are not implemented.", - "Last data error. This should *never* occur." + //Probably programmer errors: + E_NO_LONG_PBM, + E_WRITE_PAST_END, + E_NOT_2_BYTE, + E_NOT_1_BYTE, + E_READ_PAST_END, + + E_MAX //All errors sit below this }; - - const char* prog_error_names[]= + + string name_error(errors e) { - "Long PBMs are silly.", - "Trying to write past end of file.", - "This is not a 2 byte PNM.", - "This is not a 1 byte PNM.", - "Trying to read past the end of the file." - }; + const char* data_error_names[]= + { + "None.", + "Not a PNM.", + "EOF in header.", + "EOF in data.", + "Bad size specified.", + "Invalid colour MAXVAL.", + "Binary PBMs are not implemented.", + "Out of memory.", + "Unterminated header.", + "Portable Arbirtary Maps are not implemented.", + "Last data error. This should *never* occur." + }; + + const char* prog_error_names[]= + { + "Long PBMs are silly.", + "Trying to write past end of file.", + "This is not a 2 byte PNM.", + "This is not a 1 byte PNM.", + "Trying to read past the end of the file." + }; + + const char* invalid = "Not a valid error."; + + string ret; + + if(e < 0 || e >= E_MAX) + ret=string("internal error: ") + invalid; + else if(e >= E_LAST_DATA_ERROR && e <= E_PROGRAMMER_ERROR) + ret=string("internal error: ") + invalid; + else if(e < E_LAST_DATA_ERROR) + ret=data_error_names[e]; + else + ret=string("internal error: ") + prog_error_names[e-E_PROGRAMMER_ERROR - 1]; - const char* invalid = "Not a valid error."; + return "Error in PNM image: " + ret; + } - string ret; - if(e < 0 || e >= E_MAX) - ret=string("internal error: ") + invalid; - else if(e >= E_LAST_DATA_ERROR && e <= E_PROGRAMMER_ERROR) - ret=string("internal error: ") + invalid; - else if(e < E_LAST_DATA_ERROR) - ret=data_error_names[e]; - else - ret=string("internal error: ") + prog_error_names[e-E_PROGRAMMER_ERROR - 1]; + class pnm_in + { + public: + pnm_in(std::istream&); + ImageRef size(); + string datatype(); + + template + void get_raw_pixel_lines(C * c, unsigned long nlines); + + private: + bool m_is_2_byte; + int m_channels; + string elemtype; + std::istream& i; + bool is_text; + int type, maxval; + int lines_so_far; + void read_header(); + bool can_proc_lines(unsigned long); + long xs, ys; + }; - return "Error in PNM image: " + ret; - } + bool clean(istream& i) + { + //////////////////////////////////////////////////////////////////////////// + // + // Strip leading whitespae and PNM comments from an istream + // comments start with a '#' and and with a newline + unsigned char c; + for(;;) + { + i >> c; + + if(i.eof()) //I'm pedantic like that. + return 0; + else if(c == '#') //Eat a whole line of comment + { + do + { + i >> c; + if(i.eof()) //I'm still a pedant + return 0; + }while(c != '\n'); + } + else if(!isspace(c)) + { + i.putback(c); + return 1; + } + } + } + //////////////////////////////////////////////////////////////////////////////// + // + // base PNM class definitions + // + //////////////////////////////////////////////////////////////////////////////// - bool clean(istream& i) - { - //////////////////////////////////////////////////////////////////////////// - // - // Strip leading whitespae and PNM comments from an istream - // comments start with a '#' and and with a newline + bool pnm_in::can_proc_lines(unsigned long nl) + { + //Slightly oddly named. Update the number of processed lines and return + //an error condition if the lines can not be processed. - unsigned char c; + lines_so_far += nl; + if(lines_so_far > ys) + return false; + else + return true; + } - for(;;) - { - i >> c; + //////////////////////////////////////////////////////////////////////////////// + // + // Input PNM class definitions + // + //////////////////////////////////////////////////////////////////////////////// - if(i.eof()) //I'm pedantic like that. - return 0; - else if(c == '#') //Eat a whole line of comment - { - do - { - i >> c; - if(i.eof()) //I'm still a pedant - return 0; - }while(c != '\n'); - } - else if(!isspace(c)) + pnm_in::pnm_in(std::istream& in) + :i(in) { - i.putback(c); - return 1; + lines_so_far=0; + read_header(); } - } - } - - //////////////////////////////////////////////////////////////////////////////// - // - // base PNM class definitions - // - //////////////////////////////////////////////////////////////////////////////// - - bool pnm_in::can_proc_lines(unsigned long nl) - { - //Slightly oddly named. Update the number of processed lines and return - //an error condition if the lines can not be processed. - - lines_so_far += nl; - if(lines_so_far > ys) - return false; - else - return true; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Input PNM class definitions - // - //////////////////////////////////////////////////////////////////////////////// - - pnm_in::pnm_in(std::istream& in) - :i(in) - { - lines_so_far=0; - read_header(); - } - - void pnm_in::read_header() - { - //Fix the stream before returning. -#define RETURN(X) do{ \ - i >> skipws; \ - if(X == E_NONE) \ - return; \ - else \ - throw Exceptions::Image_IO::MalformedImage(name_error(X)); \ - }while(0) - -#define CLEAN do{ \ - if(!clean(i)) \ - throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \ - }while(0) - -#define GET(X) do{ \ - CLEAN; \ - i>>X; \ - if(i.eof()) \ - throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \ - }while(0) - - char c1, c2; - - // i >> (int) will eat whitespace before and after the integer. This breaks - // reading maxval since it can (and does) eat in to the image. - i >> noskipws; + void pnm_in::read_header() + { + //Fix the stream before returning. +#define RETURN(X) do{ \ + i >> skipws; \ + if(X == E_NONE) \ + return; \ + else \ + throw Exceptions::Image_IO::MalformedImage(name_error(X)); \ + }while(0) + +#define CLEAN do{ \ + if(!clean(i)) \ + throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \ + }while(0) + +#define GET(X) do{ \ + CLEAN; \ + i>>X; \ + if(i.eof()) \ + throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \ + }while(0) + + char c1, c2; + + // i >> (int) will eat whitespace before and after the integer. This breaks + // reading maxval since it can (and does) eat in to the image. + i >> noskipws; - //Read and check magic number - - i >> c1 >> c2; - if(i.eof()) - RETURN(E_HEOF); - if(c1 != 'P' || c2 < '1' || c2 > '7') - RETURN(E_NOT_PNM); + //Read and check magic number - if(c2 == '7') - RETURN(E_PAM_NOT_IMPLEMENTED); + i >> c1 >> c2; + if(i.eof()) + RETURN(E_HEOF); - if(strchr("123", c2)) - is_text = true; - else - is_text = false; + if(c1 != 'P' || c2 < '1' || c2 > '7') + RETURN(E_NOT_PNM); - if(c2 == '1' || c2 == '4') - type = PBM; - else if(c2 == '2' || c2 == '5') - type = PGM; - else - type = PPM; + if(c2 == '7') + RETURN(E_PAM_NOT_IMPLEMENTED); + if(strchr("123", c2)) + is_text = true; + else + is_text = false; - if(type == PGM) - m_channels = 1; - else - m_channels = 3; + if(c2 == '1' || c2 == '4') + type = PBM; + else if(c2 == '2' || c2 == '5') + type = PGM; + else + type = PPM; + - //Read and check image dimensions - GET(xs); - GET(ys); + if(type == PGM) + m_channels = 1; + else + m_channels = 3; - if(xs <= 0 || ys <= 0) - RETURN(E_BAD_SIZE); - - //Read if necessasy and set MAXVAL - if(type != PBM) - { - GET(maxval); + //Read and check image dimensions + GET(xs); + GET(ys); - if(maxval <= 0 || maxval > 65535) - RETURN(E_BAD_MAXVAL); + if(xs <= 0 || ys <= 0) + RETURN(E_BAD_SIZE); + + //Read if necessasy and set MAXVAL + if(type != PBM) + { + GET(maxval); + + if(maxval <= 0 || maxval > 65535) + RETURN(E_BAD_MAXVAL); + + if(maxval <= 255) + m_is_2_byte = false; + else + m_is_2_byte = true; + } + else + { + maxval = 255; + m_is_2_byte = false; + } + + if(type == PBM) + elemtype = "bool"; + else if(type == PGM) + { + if(m_is_2_byte) + elemtype = PNM::type_name::name(); + else + elemtype = PNM::type_name::name(); + } + if(type == PPM) + { + if(m_is_2_byte) + elemtype = PNM::type_name >::name(); + else + elemtype = PNM::type_name >::name(); + } + - if(maxval <= 255) - m_is_2_byte = false; - else - m_is_2_byte = true; - } - else - { - maxval = 255; - m_is_2_byte = false; - } - - //Remove everything to the beginning of the image - if(is_text) - CLEAN; - else - { - //Binary PNMs have a single byte of whitespace. - - unsigned char tmp; - i >> tmp; - if(!isspace(tmp)) - RETURN(E_UNTERMINATED_HEADER); - } + + //Remove everything to the beginning of the image + if(is_text) + CLEAN; + else + { + //Binary PNMs have a single byte of whitespace. + + unsigned char tmp; + i >> tmp; + if(!isspace(tmp)) + RETURN(E_UNTERMINATED_HEADER); + } - //hee hee hee. This makes the SGI compiler segfault :-) - // RETURN(E_NONE); - i >> skipws; + //hee hee hee. This makes the SGI compiler segfault :-) + // RETURN(E_NONE); + i >> skipws; - //HACK - //cerr << "is_rgb=" << m_is_rgb <<". xsize=" << xs << ". ysize=" << ys << ". is_text=" << is_text << ". type=" << type << ". maxval=" << maxval << endl; + //HACK + //cerr << "is_rgb=" << m_is_rgb <<". xsize=" << xs << ". ysize=" << ys << ". is_text=" << is_text << ". type=" << type << ". maxval=" << maxval << endl; - - return; + + return; #undef RETURN #undef CLEAN #undef GET - } + } + + + //Text reading functions + void get_num(istream& i, byte* c) + { + int j=0; + i >> j; //Ignore EOF: be leniant + *c = j & 0xff; + } - void pnm_in::get_raw_pixel_lines(unsigned char* c, unsigned long nlines) - { - //Reading into uchars is sufficiently different from reading in to - //ushorts, so it has ben done as 2 functions as opposed to one - //tmeplated one. + void get_num(istream& i, bool* c) + { + i >> ws; + int j = cin.get(); + //PBMs are inverted + *c = (j != '0'); + } -#define RETURN(X) do{ \ - if(X == E_NONE) \ - return; \ - else \ - throw Exceptions::Image_IO::MalformedImage(name_error(X)); \ - }while(0) + void get_num(istream& i, unsigned short* c) + { + int j=0; + i >> j; //Ignore EOF: be leniant + *c = j & 0xffff; + } - unsigned long k, j, npix; - unsigned char* cc = c; + template void get_num(istream& i, Rgb* c) + { + get_num(i, & (c->red)); + get_num(i, & (c->green)); + get_num(i, & (c->blue)); + } + + //Data reading functions + void really_swap(unsigned char* s, size_t n_elem) + { + #ifdef SWAP_BYTES + for(size_t j=0; j < n_elem; j++) + swap(s[2*j], s[2*j+1]); + #endif + } - if(!can_proc_lines(nlines)) - RETURN(E_READ_PAST_END); - - //Load a bunch of pixels in to memory without doing internal buffering - - npix = xs * nlines; + void maybe_swap(unsigned short* s, size_t n) + { + really_swap((unsigned char*)s, n); + } + void maybe_swap(Rgb*s , size_t n) + { + really_swap((unsigned char*)s, n*3); + } - if(m_is_2_byte) - RETURN(E_NOT_1_BYTE); - - if(type == PPM) - npix *= 3; - - if(is_text) //Load text PNM data - { - //Don't report errors. The spec says to be really leniant with - //respect to errors in text only pnms + void maybe_swap(bool*, size_t) + {} + void maybe_swap(byte*, size_t) + {} + void maybe_swap(Rgb*, size_t) + {} - for(k=0; k < npix; k++) + template + void pnm_in::get_raw_pixel_lines(C * c, unsigned long nlines) { - i >> j; + if(datatype() != PNM::type_name::name()) + throw ReadTypeMismatch(datatype(), PNM::type_name::name()); + + //Reading into uchars is sufficiently different from reading in to + //ushorts, so it has ben done as 2 functions as opposed to one + //tmeplated one. + + unsigned long k, npix; + if(!can_proc_lines(nlines)) + throw Exceptions::Image_IO::MalformedImage(name_error(E_READ_PAST_END)); + + //Load a bunch of pixels in to memory without doing internal buffering + npix = xs * nlines; + + if(is_text) //Load text PNM data + { + for(k=0; k < npix; k++) + get_num(i, c++); + } + else if(type != PBM) + { + if(type == PBM) + throw Exceptions::Image_IO::MalformedImage(name_error(E_PBM_NOT_IMPLEMENTED)); - if(i.eof()) - j=0; + i.read((char*)c, m_channels*npix * (m_is_2_byte?2:1)); + maybe_swap(c, npix); + } + + //Don't report errors. The spec says to be really leniant with + //respect to errors in text only pnms + if(!is_text && i.fail()) + throw Exceptions::Image_IO::MalformedImage(name_error(E_DEOF)); - *c++ = j & 0xff; } - if(type == PBM) + ImageRef pnm_in::size() { - //PBMs are inverted. Also, converto to a sensible maxval - for(k=0; k < npix; k++, cc++) - { - if(*cc) - *cc=0; - else - *cc=maxval; - } + return ImageRef(xs, ys); } - } - else if(type != PBM) - { - i.read((char*)c, (int)npix); - } - else - RETURN(E_PBM_NOT_IMPLEMENTED); - - if(i.eof()) - RETURN(E_DEOF); - } + string pnm_in::datatype() + { + return elemtype; + } - void pnm_in::get_raw_pixel_lines(unsigned short* s, unsigned long nlines) - { - unsigned long k, j, npix; - - //Load a bunch of pixels in to memory without doing internal buffering - - if(!can_proc_lines(nlines)) - RETURN(E_READ_PAST_END); - if(!m_is_2_byte) - RETURN(E_NOT_2_BYTE); - npix = xs * nlines; - - if(type == PPM) - npix *= 3; - - if(is_text) //Load text PNM data - { - //Don't report errors. The spec says to be really leniant with - //respect to errors in text only pnms + //////////////////////////////////////////////////////////////////////////////// + // + // Implementation of PNM reader class + // - for(k=0; k < npix; k++) + Reader::~Reader() { - i >> j; - if(i.eof()) - j=0; - *s++ = j & 0xffff; } - } - else - { -#ifndef LONG_PNM_FAST_LOAD - - unsigned char i1, i2; - for(j=0; j < npix; j++) + Reader::Reader(istream& i) + :p(new pnm_in(i)) { - i.read((char*)&i1, 1); - i.read((char*)&i2, 1); - - //Data is big-endian - *s++ = i1 << 8 | i2; + } - if(i.eof()) - cerr << j << " "; + string Reader::datatype() + { + return p->datatype(); } -#else - i.read((char*)s, npix*2); - -#ifdef SWAP_BYTES - unsigned char t; - for(j=0; j < npix; j++, s++) + string Reader::name() { - t = *(unsigned char*)s; - *(unsigned char*)s = *(1+(unsigned char*)s); - *(1+(unsigned char*)s) = t; + return "PNM"; } -#endif -#endif - } - if(i.eof()) - RETURN(E_DEOF); - } - //////////////////////////////////////////////////////////////////////////////// - // - // Output PNM class definitions - // - //////////////////////////////////////////////////////////////////////////////// - - void writePNMHeader(ostream& out, int channels, ImageRef size, int maxval, bool text, const std::string& comments) - { - char m[3] = {'P',' ', '\n'}; - if (channels == 1) - m[1] = '2'; - else - m[1] = '3'; - if (!text) - m[1] += 3; - out.write(m, 3); - bool freshLine = true; - for (size_t i=0; isize(); } - } - if (!freshLine) - out << endl; - out << size.x << " " << size.y << endl << maxval << endl; - } - pnm_writer::pnm_writer(std::ostream& out, ImageRef size_, const std::string& type_, const std::map >&) - :row(0),o(out),size(size_),type(type_) - { - - if(type == "unsigned char") - writePNMHeader(out, 1, size, 255, 0, ""); - else if(type == "unsigned short") - writePNMHeader(out, 1, size, 65535, 0, ""); - else if(type == "CVD::Rgb") - writePNMHeader(out, 3, size, 255, 0, ""); - else if(type == "CVD::Rgb") - writePNMHeader(out, 3, size, 65535, 0, ""); - else - throw UnsupportedImageSubType("PNM", type); - } - pnm_writer::~pnm_writer() - {} + //Mechanically generate the pixel reading calls. + #define GEN1(X) void Reader::get_raw_pixel_line(X*d){p->get_raw_pixel_lines(d, 1);} + #define GEN2(X) GEN1(X) GEN1(Rgb) + GEN1(bool) + GEN2(unsigned char) + GEN2(unsigned short) + + + #undef GEN1 + #undef GEN3 + //////////////////////////////////////////////////////////////////////////////// + // + // Output PNM class definitions + // + //////////////////////////////////////////////////////////////////////////////// + + class pnm_writer + { + public: + pnm_writer(std::ostream&, ImageRef size, const std::string& type, const std::map >& p); + ~pnm_writer(); + + //void write_raw_pixel_line(const bool*); + template void write_raw_pixel_line(const C*); + + private: + + void write_shorts(const unsigned char* data, size_t n_shorts); + void write_binary(const unsigned char* data, size_t n); + void write_binary(const unsigned short* data, size_t n); + void write_binary(const Rgb* data, size_t n); + void write_binary(const Rgb* data, size_t n); + + + template void sanity_check(const P*); + + bool text; + long row; + std::ostream& o; + ImageRef size; + std::string type; + vector rowbuf; + }; + + + void writePNMHeader(ostream& out, int channels, ImageRef size, int maxval, bool text, const std::string& comments) + { + char m[3] = {'P',' ', '\n'}; + if (channels == 1) + m[1] = '2'; + else + m[1] = '3'; + if (!text) + m[1] += 3; + out.write(m, 3); + bool freshLine = true; + for (size_t i=0; i >& p) + :text(0),row(0),o(out),size(size_),type(type_) + { + if(p.count("pnm.raw")) { - unsigned char lohi[2] = {bdata[1], bdata[0]}; - o.write((const char*)lohi,2); + try{ + text=!(p.find("pnm.raw")->second.get()); + } + catch(std::bad_cast c){ + cerr << "Warning pnm.raw is not a bool.\n"; + } } - #else - o.write((const char*)data, n*sizeof(unsigned short)); - #endif - } + + if(type == "unsigned char") + writePNMHeader(out, 1, size, 255, text, ""); + else if(type == "unsigned short") + writePNMHeader(out, 1, size, 65535, text, ""); + else if(type == "CVD::Rgb") + writePNMHeader(out, 3, size, 255, text, ""); + else if(type == "CVD::Rgb") + writePNMHeader(out, 3, size, 65535, text, ""); + else + throw UnsupportedImageSubType("PNM", type); + } - template void pnm_writer::sanity_check(const P*) - { - if(type != PNM::type_name

::name()) - throw WriteTypeMismatch(type, PNM::type_name

::name()); - - //Do some sanity checking - if(row >= size.y) - throw InternalLibraryError("CVD", "Write past end of image."); - - row++; - } + pnm_writer::~pnm_writer() + {} - void pnm_writer::write_raw_pixel_line(const unsigned char* data) - { - sanity_check(data); - o.write(reinterpret_cast(data), size.x); - } + void pnm_writer::write_shorts(const unsigned char* data, size_t n_shorts) + { + #ifdef CVD_ARCH_LITTLE_ENDIAN + rowbuf.resize(n_shorts*sizeof(short)); + copy(data, data + sizeof(short)*n_shorts, rowbuf.begin()); + + for (size_t i=0; i(data), n); + } + void pnm_writer::write_binary(const unsigned short* data, size_t n) + { + write_shorts(reinterpret_cast(data), n); + } + void pnm_writer::write_binary(const Rgb* data, size_t n) + { + o.write(reinterpret_cast(data), n*3); + } + void pnm_writer::write_binary(const Rgb* data, size_t n) + { + write_shorts(reinterpret_cast(data), n*3); + } - void pnm_writer::write_raw_pixel_line(const Rgb* data) - { - sanity_check(data); - o.write(reinterpret_cast(data), 3*size.x); - } + template void write_text(const C* data, size_t n, ostream& o) + { + for(size_t i=0; i < n; i++) + o << (int) data[i] << endl; + } - void pnm_writer::write_raw_pixel_line(const Rgb* data) - { - sanity_check(data); - write_shorts(reinterpret_cast(data), 3*size.x); - } + template void write_text(const Rgb* data, size_t n, ostream& o) + { + for(size_t i=0; i < n; i++) + o << (int) data[i].red << " " << (int)data[i].green << " " << (int)data[i].blue << endl; + } + + template + void pnm_writer::write_raw_pixel_line(const C* data) + { + if(type != PNM::type_name::name()) + throw WriteTypeMismatch(type, PNM::type_name::name()); + + //Do some sanity checking + if(row >= size.y) + throw InternalLibraryError("CVD", "Write past end of image."); + + row++; + + if(text) + write_text(data, size.x, o); + else + write_binary(data, size.x); + } + //////////////////////////////////////////////////////////////////////////////// + // + // Public interface + // -/* - template void writePNMPixelsText(ostream& out, const T* data, size_t count) - { - size_t lines = count / 25; - size_t k=0; - for (size_t i=0; i >& p) + :p(new pnm_writer(out, size_, type_, p)) + {} - } + Writer::~Writer() + {} + + //Mechanically generate the pixel writing calls. + #define GEN1(X) void Writer::write_raw_pixel_line(const X*d){p->write_raw_pixel_line(d);} + #define GEN2(X) GEN1(X) GEN1(Rgb) + //GEN1(bool) + GEN2(unsigned char) + GEN2(unsigned short) + + + + } } diff --git a/test/test_images.cxx b/test/test_images.cxx index 7b7c5f59..4ba1169d 100644 --- a/test/test_images.cxx +++ b/test/test_images.cxx @@ -206,17 +206,25 @@ template void loadsave_safe(const char*n) } } +map > empty() +{ + map > nothing; + return nothing; +} + template struct randtest { typedef typename T::Type Type; - static void exec(ImageType::ImageType fmt) - { - try{ - for(int i=0; i < 10; i++) - { + static void exec(ImageType::ImageType fmt, const map >& p = empty()) + { + for(int i=0; i < 10; i++) + { + try{ //Make a random image - Image in(ImageRef(1000,1000)), out; + Image in(ImageRef(1000+i,1000+i)), out; + + cerr << "Testing " << in.size() << " " << fmt << " " << CVD::PNM::type_name::name() << " "; for(int y=0; y < in.size().y; y++) for(int x=0; x < in.size().x; x++) @@ -225,7 +233,7 @@ template struct randtest stringstream s; //Save the image - img_save(in, s, fmt); + img_save(in, s, fmt, p); s.seekg(0, ios_base::beg); s.seekp(0, ios_base::beg); @@ -235,10 +243,10 @@ template struct randtest //Compare the results if(out.size() != in.size()) - cerr << "Image R/W test for type " << fmt << " " << CVD::PNM::type_name::name() << " size mismatch.\n"; + cerr << " size mismatch.\n"; else if(!equal(in.begin(), in.end(), out.begin())) { - cerr << "Image R/W test for type " << fmt << " " << CVD::PNM::type_name::name() << " data mismatch.\n"; + cerr << " data mismatch.\n"; typedef typename Pixel::Component::type Ct; double t=0, minval = HUGE_VAL, maxval=-HUGE_VAL; @@ -264,22 +272,23 @@ template struct randtest } else - cerr << "Image R/W test for type " << fmt << " " << CVD::PNM::type_name::name() << " OK.\n"; + cerr << "OK.\n"; + } + catch(Exceptions::All w) + { + cerr << w.what << endl; } - } - catch(Exceptions::All w) - { - cerr << w.what << endl; } - randtest::exec(fmt); + randtest::exec(fmt,p); } }; template<> struct randtest { - static void exec(ImageType::ImageType){} + static void exec(ImageType::ImageType, const map >& = empty()) + {} }; int main(int ac, char** av) @@ -322,7 +331,7 @@ int main(int ac, char** av) loadsave_safe >(av[i]); } - cerr << "Testing TEXT (type " << ImageType::BMP << ")\n"; + cerr << "Testing TEXT (type " << ImageType::TEXT << ")\n"; randtest< TypeList, Head> > >::exec(ImageType::BMP); - + cerr << "Testing PNM (type " << ImageType::PNM << ")\n"; randtest< TypeList, Head> > > > >::exec(ImageType::PNM); + cerr << "Testing PNM (type " << ImageType::PNM << "), text I/O\n"; + map > p; + p["pnm.raw"] = Parameter(0); + randtest< + TypeList, + TypeList, + Head> > > > >::exec(ImageType::PNM, p); + cerr << "Testing FITS (type " << ImageType::FITS << ")\n"; randtest< TypeList