// ========================================================================== // $Id: pnm_image_io.cpp,v 1.1 2011/12/05 17:25:20 jlang Exp $ // Templated PNM Image IO class based on CSI2372 lab 5 // ========================================================================== // (C)opyright: // // Jochen Lang // SITE, University of Ottawa // 800 King Edward Ave. // Ottawa, On., K1N 6N5 // Canada. // http://www.site.uottawa.ca // // Creator: jlang (Jochen Lang) // Email: jlang@site.uottawa.ca // ========================================================================== // $Log: pnm_image_io.cpp,v $ // Revision 1.1 2011/12/05 17:25:20 jlang // Adapted pnm_image_io to lecture 14 // // Revision 1.2 2007/11/11 01:00:09 jlang // Created solution to project assignment // // // ========================================================================== #include #include #include #include "pnm_image_io.h" using std::cerr; using std::endl; using std::stringstream; #define DEBUG_PNM_IMAGE_IO template PNM_ImageIO::PNM_ImageIO() { switch (noChannels) { case 1: MAGIC_ASCII = string("P2"); MAGIC_BIN = string("P5"); DEFAULT_FILE_EXTENSION = string(".pgm"); break; case 3: MAGIC_ASCII = string("P3"); MAGIC_BIN = string("P6"); DEFAULT_FILE_EXTENSION = string(".ppm"); break; default: throw( new string( "No of channels has to be 1 or 3 in pnm!" )); } if ( sizeof(T) < noChannels ) throw ( new string( "Type is too small" )); } template T* PNM_ImageIO::addAlpha( unsigned char *_image, int _nRows, int _nCols, unsigned char _alpha ) { d_nRows = _nRows; d_nCols = _nCols; T* result = new T[d_nRows*d_nCols]; T* pRes = result; for ( int row = 0; row < d_nRows; ++row ) { for ( int col = 0; col < d_nCols; ++col ) { *pRes = 0; for ( int channel = 0; channel < noChannels; ++channel ) { *pRes |= *_image++ << 8*channel; } *pRes++ |= _alpha << 8*noChannels; } } #ifdef DEBUG_PNM_IMAGE_IO cerr << "Added alpha: " << std::hex << *result << std::dec << endl; #endif return result; } template unsigned char* PNM_ImageIO::removeAlpha( T *_image, int _nRows, int _nCols ) { d_nRows = _nRows; d_nCols = _nCols; unsigned char* result = new unsigned char[d_nRows*d_nCols*noChannels]; unsigned char* pRes = result; for ( int row = 0; row < d_nRows; ++row ) { for ( int col = 0; col < d_nCols; ++col ) { for ( int channel = 0; channel < noChannels; ++channel ) { *pRes++ = (*_image & 0xFFFFFF) >> 8*channel; } ++_image; } } #ifdef DEBUG_PNM_IMAGE_IO cerr << "Removed alpha: " << std::hex << static_cast(result[0]) << static_cast(result[1]) << static_cast(result[2]) << std::dec << endl; #endif return result; } template unsigned char *PNM_ImageIO::read( string _fileName, int &_nRows, int &_nCols ) { d_mode = fstream::in | fstream::binary; // start in binary mode fstream inFile; if ( !openFile(_fileName, inFile )) return 0; // open file if ( !parseHeader( inFile )) return 0; // close the file and re-open in the appropriate mode if ( !(d_mode & fstream::binary) ) { int pos = inFile.tellg(); #ifdef DEBUG_PNM_IMAGE_IO cerr << "Last position: " << pos << endl; #endif inFile.close(); inFile.clear(); d_mode = fstream::in; openFile(_fileName, inFile ); // re-open file in ascii inFile.seekg(pos); // seek to the next position } // read the bytes unsigned char *data = new unsigned char[d_nRows*d_nCols*noChannels]; unsigned char *dataPos = data; if (d_mode & fstream::binary) { inFile.read( reinterpret_cast(data), d_nRows*d_nCols*noChannels ); if ( !inFile ) { cerr << "binary read error" << endl; return 0; } } else { for ( int row = 0; row < d_nRows; ++row ) { for ( int col = 0; col < d_nCols; ++col ) { for ( int channel = 0; channel < noChannels; ++channel ) { // read a pixel from file int val; inFile >> val; *dataPos = static_cast(val); ++dataPos; } } } } inFile.close(); _nRows = d_nRows; _nCols = d_nCols; return data; } template bool PNM_ImageIO::write( string _fileName, unsigned char *_image, int _nRows, int _nCols ) { d_nRows = _nRows; d_nCols = _nCols; fstream outFile; setBinary(d_mode & fstream::binary); // construct output stream for writing if ( !openFile(_fileName, outFile )) return false; // open file if ( !writeHeader(outFile)) return false; // write header // Write pixels if (d_mode & fstream::binary) { outFile.write( reinterpret_cast(_image), d_nRows*d_nCols*noChannels); } else { for ( int row = 0; row < d_nRows; ++row ) { for ( int col = 0; col < d_nCols; ++col ) { for ( int channel = 0; channel < noChannels; ++channel ) { // write a pixel to file outFile << static_cast(*_image++) << ' '; } } } } outFile.close(); return true; } template void PNM_ImageIO::setBinary( bool _bin) { if ( _bin ) { d_mode = fstream::binary | fstream::out; } else { d_mode = fstream::out; } return; } template bool PNM_ImageIO::writeHeader( fstream& _outFile ) { if ( d_mode & fstream::binary ) { // Write binary PPM header _outFile << MAGIC_BIN << endl; // PNM id for binary } else { // Write ASCII PPM header _outFile << MAGIC_ASCII << endl; // PNM id for ascii } _outFile << d_nCols << " " << d_nRows << endl; // image size _outFile << "255" << endl; // max value of a char return (!_outFile != true); } template bool PNM_ImageIO::parseHeader( fstream &_inFile ) { string line, token; stringstream converter; if (!getline(_inFile, line)) return false; converter << line; converter >> token; // mode is always ASCII at first if (!_inFile) return false; // could not read if ( token == MAGIC_BIN ) { #ifdef DEBUG_PNM_IMAGE_IO cerr << "Got " << MAGIC_BIN << endl; #endif d_mode |= fstream::binary; // set binary mode } else { if ( token != MAGIC_ASCII ) { return false; // Not a valid image format } #ifdef DEBUG_PNM_IMAGE_IO cerr << "Got " << MAGIC_ASCII << endl; #endif d_mode &= ~fstream::binary; // unset binary mode } converter.str(""); converter.clear(); // The next line(s) may be comment while (getline(_inFile, line)) { // Get first character if ( line[0] != '#' ) { // Line is not a comment break; } #ifdef DEBUG_PNM_IMAGE_IO cerr << "Comment: " << line << endl; #endif } if ( !_inFile ) { // Ouhps - something went wrong return false; } // line should be size converter.str(""); converter.clear(); converter << line; converter >> d_nCols; converter >> d_nRows; #ifdef DEBUG_PNM_IMAGE_IO cerr << "Size: " << d_nRows << " " << d_nCols << endl; #endif // see if we have more tokens int maxValue; converter >> maxValue; if ( !converter ) { // if not read one more token converter.str(""); converter.clear(); getline(_inFile, line); converter << line; converter >> maxValue; } #ifdef DEBUG_PNM_IMAGE_IO cerr << "Max value: " << maxValue << endl; #endif // max value should be 255 for characters if ( maxValue != 255 ) return false; return true; } template bool PNM_ImageIO::openFile(string _fileName, fstream& _file ) { // Check if filename ends in .ppm if not add if ( _fileName.size() <= 4 || _fileName.substr(_fileName.size()-4,_fileName.size()) != DEFAULT_FILE_EXTENSION) { _fileName += DEFAULT_FILE_EXTENSION; } // bind the file to the stream _file.open( _fileName.c_str(), d_mode ); // check if an error has occurred if (!_file) { cerr << "Error: could not open file: " << _fileName << endl; return false; } return true; }