#ifndef IMAGE_INCLUDED #define IMAGE_INCLUDED #define SUPPORT_TILES #include #include "MyMiscellany.h" struct ImageReader { virtual unsigned int nextRow( unsigned char* row ) = 0; static unsigned char* Read( const char* fileName , unsigned int& width , unsigned int& height , unsigned int& channels ) { ImageReader* reader = Get( fileName ); width = reader->width() , height = reader->height() , channels = reader->channels(); unsigned char* pixels = new unsigned char[ width*height*channels ]; for( unsigned int j=0 ; jnextRow( pixels + j*width*channels ); delete reader; return pixels; } static unsigned char* ReadColor( const char* fileName , unsigned int& width , unsigned int& height ) { unsigned int channels; ImageReader* reader = Get( fileName ); width = reader->width() , height = reader->height(); if( channels!=1 && channels!=3 ) ERROR_OUT( "Requres one- or three-channel input" ); unsigned char* pixels = new unsigned char[ width*height*3 ]; unsigned char* pixelRow = new unsigned char[ width*channels]; for( unsigned int j=0 ; jnextRow( pixelRow ); if ( channels==3 ) memcpy( pixels+j*width*3 , pixelRow , sizeof(unsigned char)*width*3 ); else if( channels==1 ) for( unsigned int i=0 ; inextRow( pixels + j*width*channels ); delete writer; return true; } static ImageWriter* Get( const char* fileName , unsigned int width , unsigned int height , unsigned int channels , ImageWriterParams params=ImageWriterParams() ); virtual ~ImageWriter( void ){ } unsigned int width( void ) const { return _width; } unsigned int height( void ) const { return _height; } unsigned int channels( void ) const { return _channels; } protected: unsigned int _width , _height , _channels; }; #ifdef SUPPORT_TILES struct TiledImageReader : public ImageReader { unsigned int nextRow( unsigned char* row ); TiledImageReader( const char* fileName , unsigned int& width , unsigned int& height , unsigned int& channels ); ~TiledImageReader( void ); static bool GetInfo( const char* fileName , unsigned int& width , unsigned int& height , unsigned int& channels ); protected: ImageReader** _tileReaders; char** _tileNames; unsigned int _tileRows , _tileColumns , _currentPixelRow , _currentTileRow , *_tileWidths , *_tileHeights; }; struct TiledImageWriter : public ImageWriter { unsigned int nextRow( const unsigned char* row ); TiledImageWriter( const char* fileName , unsigned int width , unsigned int height , unsigned int channels , ImageWriterParams params ); ~TiledImageWriter( void ); protected: ImageWriter** _tileWriters; char** _tileNames; unsigned int _tileWidth , _tileHeight , _tileRows , _tileColumns , _currentPixelRow; ImageWriterParams _params; }; #endif // SUPPORT_TILES // [WARNING] Need to include "png.h" before "jpeg.h" so that "setjmp.h" is not already included (?) #include "PNG.h" #include "JPEG.h" struct FileNameParser { #if defined( _WIN32 ) || defined( _WIN64 ) static const char Separator = (char)'\\'; #else // !_WIN static const char Separator = (char)'/'; #endif // _WIN static inline char* Extension ( const char* fileName ){ return __Split( fileName , '.' , false , false ); } static inline char* Header ( const char* fileName ){ return __Split( fileName , '.' , true , false ); } static inline char* Local ( const char* fileName ){ return __Split( fileName , Separator , false , false ); } static inline char* Dir ( const char* fileName ){ return __Split( fileName , Separator , true , false ); } static inline char* LocalHeader( const char* fileName ) { char* localFileName = Local( fileName ); if( !localFileName ) ERROR_OUT( "Couldn't get local file name: %s" , fileName ); char* localFileHeader = Header( localFileName ); delete[] localFileName; return localFileHeader; } protected: static inline char* __Split( const char* fileName , char splitChar , bool front , bool first ) { int position; char* out; if( first ){ for( position=0 ; position=0 ; position-- ) if( fileName[position]==splitChar ) break; } if( front ) { if( position==-1 ) out = NULL; else { out = new char[ strlen(fileName)+1 ]; strcpy( out , fileName ); out[ position ] = 0; } } else { if( position==strlen(fileName) ) out = NULL; else { out = new char[ strlen(fileName)-position ]; strcpy( out , fileName+position+1 ); } } return out; } }; inline ImageReader* ImageReader::Get( const char* fileName ) { unsigned int width , height , channels; ImageReader* reader = NULL; char* ext = FileNameParser::Extension( fileName ); #ifdef WIN32 if ( !_stricmp( ext , "jpeg" ) || !_stricmp( ext , "jpg" ) ) reader = new JPEGReader( fileName , width , height , channels ); else if( !_stricmp( ext , "png" ) ) reader = new PNGReader( fileName , width , height , channels ); else if( !_stricmp( ext , "iGrid" ) ) reader = new TiledImageReader( fileName , width , height , channels ); #else // !WIN32 if( !strcasecmp( ext , "jpeg" ) || !strcasecmp( ext , "jpg" ) ) reader = new JPEGReader( fileName , width , height , channels ); else if( !strcasecmp( ext , "png" ) ) reader = new PNGReader( fileName , width , height , channels ); else if( !strcasecmp( ext , "iGrid" ) ) reader = new TiledImageReader( fileName , width , height , channels ); #endif // WIN32 reader->_width = width; reader->_height = height; reader->_channels = channels; delete[] ext; return reader; } inline void ImageReader::GetInfo( const char* fileName , unsigned int& width , unsigned int& height , unsigned int& channels ) { char* ext = FileNameParser::Extension( fileName ); #ifdef WIN32 if( !_stricmp( ext , "jpeg" ) || !_stricmp( ext , "jpg" ) ) JPEGReader::GetInfo( fileName , width , height , channels ); else if( !_stricmp( ext , "png" ) ) PNGReader::GetInfo( fileName , width , height , channels ); else if( !_stricmp( ext , "iGrid" ) ) TiledImageReader::GetInfo( fileName , width , height , channels ); #else // !WIN32 if( !strcasecmp( ext , "jpeg" ) || !strcasecmp( ext , "jpg" ) ) JPEGReader::GetInfo( fileName , width , height , channels ); else if( !strcasecmp( ext , "png" ) ) PNGReader::GetInfo( fileName , width , height , channels ); else if( !strcasecmp( ext , "iGrid" ) ) TiledImageReader::GetInfo( fileName , width , height , channels ); #endif // WIN32 delete[] ext; } inline ImageWriter* ImageWriter::Get( const char* fileName , unsigned int width , unsigned int height , unsigned int channels , ImageWriterParams params ) { ImageWriter* writer = NULL; char* ext = FileNameParser::Extension( fileName ); #ifdef WIN32 if( !_stricmp( ext , "jpeg" ) || !_stricmp( ext , "jpg" ) ) writer = new JPEGWriter( fileName , width , height , channels , params.quality ); else if( !_stricmp( ext , "png" ) ) writer = new PNGWriter( fileName , width , height , channels , params.quality ); #ifdef SUPPORT_TILES else if( !_stricmp( ext , "iGrid" ) ) writer = new TiledImageWriter( fileName , width , height , channels , params ); #endif // SUPPORT_TILES #else // !WIN32 if( !strcasecmp( ext , "jpeg" ) || !strcasecmp( ext , "jpg" ) ) writer = new JPEGWriter( fileName , width , height , channels , params.quality ); else if( !strcasecmp( ext , "png" ) ) writer = new PNGWriter( fileName , width , height , channels , params.quality ); #ifdef SUPPORT_TILES else if( !strcasecmp( ext , "iGrid" ) ) writer = new TiledImageWriter( fileName , width , height , channels , params ); #endif // SUPPORT_TILES #endif // WIN32 else ERROR_OUT( "Unrecognized file extension: %s" , ext ); writer->_width = width; writer->_height = height; writer->_channels = channels; delete[] ext; return writer; } #ifdef SUPPORT_TILES bool TiledImageReader::GetInfo( const char* fileName , unsigned int& width , unsigned int& height , unsigned int& channels ) { char* fileDir = FileNameParser::Dir( fileName ); unsigned int *_tileHeights , *_tileWidths; unsigned int _tileRows , _tileColumns , _channels; FILE* fp = fopen( fileName , "r" ); if( !fp ){ WARN( "Couldn't open file for reading: %s" , fileName ) ; return false; } { char line[1024]; if( !fgets( line , 1024 , fp ) ) ERROR_OUT( "Failed to read column line from: %s" , fileName ); line[strlen(line)-1] = 0; if( sscanf( line , "Columns: %d" , &_tileColumns )!=1 ) ERROR_OUT( "Failed to read column count from: %s (%s)" , fileName , line ); if( !fgets( line , 1024 , fp ) ) ERROR_OUT( "Failed to read row line from: %s" , fileName ); line[strlen(line)-1] = 0; if( sscanf( line , "Rows: %d" , &_tileRows )!=1 ) ERROR_OUT( "Failed to read row count from: %s (%s)" , fileName , line ); _tileHeights = new unsigned int[ _tileRows+1 ]; _tileWidths = new unsigned int[ _tileColumns+1 ]; char tileName[1024]; for( unsigned int r=0 ; r<_tileRows ; r++ ) for( unsigned int c=0 ; c<_tileColumns ; c++ ) { if( !fgets( line , 1024 , fp ) ) ERROR_OUT( "Failed to read tile name from: %s" , fileName ); line[strlen(line)-1] = 0; if( fileDir ) sprintf( tileName , "%s%c%s" , fileDir , FileNameParser::Separator , line ); else sprintf( tileName , "%s" , line ); unsigned int _w , _h , _c; ImageReader::GetInfo( tileName , _w , _h , _c ); if( !r && !c ) _channels = _c; else if( _channels!=_c ) ERROR_OUT( "Number of color channels don't match: %d != %d" , _channels , _c ); if( !r ) _tileWidths[c+1] = _w; else if( _tileWidths[c+1]!=_w ) ERROR_OUT( "Images in the same column must have the same width: %d != %d" , _tileWidths[c+1] , _w ); if( !c ) _tileHeights[r+1] = _h; else if( _tileHeights[r+1]!=_h ) ERROR_OUT( "Images in the same row must have the same heights: %d != %d" , _tileHeights[r+1] , _h ); } } fclose( fp ); if( fileDir ) delete[] fileDir; _tileWidths[0] = _tileHeights[0] = 0; for( unsigned int c=0 ; c<_tileColumns ; c++ ) _tileWidths[c+1] += _tileWidths[c]; for( unsigned int r=0 ; r<_tileRows ; r++ ) _tileHeights[r+1] += _tileHeights[r]; width = _tileWidths[_tileColumns] , height = _tileHeights[_tileRows] , channels = _channels; return true; } TiledImageReader::TiledImageReader( const char* fileName , unsigned int& width , unsigned int& height , unsigned int& channels ) { char* fileDir = FileNameParser::Dir( fileName ); FILE* fp = fopen( fileName , "r" ); if( !fp ) ERROR_OUT( "Couldn't open file for reading: %s" , fileName ); { char line[1024]; if( !fgets( line , 1024 , fp ) ) ERROR_OUT( "Failed read column line from: %s" , fileName ); line[strlen(line)-1] = 0; if( sscanf( line , "Columns: %d" , &_tileColumns )!=1 ) ERROR_OUT( "Failed to read column count from: %s (%s)" , fileName , line ); if( !fgets( line , 1024 , fp ) ) ERROR_OUT( "Failed read row line from: %s" , fileName ); line[strlen(line)-1] = 0; if( sscanf( line , "Rows: %d" , &_tileRows )!=1 ) ERROR_OUT( "Failed to read row count from: %s (%s)" , fileName , line ); _tileReaders = new ImageReader*[ _tileColumns ]; _tileHeights = new unsigned int[ _tileRows+1 ]; _tileWidths = new unsigned int[ _tileColumns+1 ]; _tileNames = new char*[ _tileColumns * _tileRows ]; char tileName[1024]; for( unsigned int r=0 ; r<_tileRows ; r++ ) for( unsigned int c=0 ; c<_tileColumns ; c++ ) { if( !fgets( line , 1024 , fp ) ) ERROR_OUT( "Failed to read tile name from: %s" , fileName ); line[strlen(line)-1] = 0; if( fileDir ) sprintf( tileName , "%s%c%s" , fileDir , FileNameParser::Separator , line ); else sprintf( tileName , "%s" , line ); _tileNames[r*_tileColumns+c] = new char[ strlen(tileName)+1 ]; strcpy( _tileNames[r*_tileColumns+c] , tileName ); } } fclose( fp ); if( fileDir ) delete[] fileDir; for( unsigned int r=0 ; r<_tileRows ; r++ ) for( unsigned int c=0 ; c<_tileColumns ; c++ ) { unsigned int _w , _h , _c; ImageReader::GetInfo( _tileNames[r*_tileColumns+c] , _w , _h , _c ); if( !r && !c ) _channels = _c; else if( _channels!=_c ) ERROR_OUT( "Number of color channels don't match: %d != %d" , _channels , _c ); if( !r ) _tileWidths[c+1] = _w; else if( _tileWidths[c+1]!=_w ) ERROR_OUT( "Images in the same column must have the same width: %d != %d" , _tileWidths[c+1] , _w ); if( !c ) _tileHeights[r+1] = _h; else if( _tileHeights[r+1]!=_h ) ERROR_OUT( "Images in the same row must have the same heights: %d != %d" , _tileHeights[r+1] , _h ); } _tileWidths[0] = _tileHeights[0] = 0; for( unsigned int c=0 ; c<_tileColumns ; c++ ) _tileWidths[c+1] += _tileWidths[c]; for( unsigned int r=0 ; r<_tileRows ; r++ ) _tileHeights[r+1] += _tileHeights[r]; width = _width = _tileWidths[_tileColumns] , height = _height = _tileHeights[_tileRows] , channels = _channels; _currentPixelRow = _currentTileRow = 0; } TiledImageReader::~TiledImageReader( void ) { delete[] _tileReaders; for( unsigned int i=0 ; i<_tileColumns*_tileRows ; i++ ) delete[] _tileNames[i]; delete[] _tileNames; delete[] _tileWidths; delete[] _tileHeights; } unsigned TiledImageReader::nextRow( unsigned char* row ) { // If it's the first row, set up the readers if( _currentPixelRow==_tileHeights[ _currentTileRow ] ) for( unsigned int c=0 ; c<_tileColumns ; c++ ) _tileReaders[c] = ImageReader::Get( _tileNames[ _currentTileRow * _tileColumns + c ] ); // Read the row fragments for( unsigned int c=0 ; c<_tileColumns ; c++ ) _tileReaders[c]->nextRow( row + c * _tileWidths[c] * _channels ); // If it's the last row of the tile, free up the readers if( _currentPixelRow==_tileHeights[_currentTileRow+1]-1 ) { for( unsigned int c=0 ; c<_tileColumns ; c++ ) delete _tileReaders[c]; _currentTileRow++; } return _currentPixelRow++; } TiledImageWriter::TiledImageWriter( const char* fileName , unsigned int width , unsigned int height , unsigned int channels , ImageWriterParams params ) { _width = width , _height = height , _channels = channels , _tileWidth = params.tileWidth , _tileHeight = params.tileHeight; _tileColumns = ( _width + ( _tileWidth-1 ) ) / _tileWidth , _tileRows = ( _height + ( _tileHeight-1 ) ) / _tileHeight; _tileWriters = new ImageWriter*[ _tileColumns ]; _tileNames = new char*[ _tileColumns * _tileRows ]; if( params.tileParams ) _params = *params.tileParams; char tileName[1024]; char* tileHeader = FileNameParser::Header( fileName ); for( unsigned int r=0 ; r<_tileRows ; r++ ) for( unsigned int c=0 ; c<_tileColumns ; c++ ) { sprintf( tileName , "%s.%d.%d.%s" , tileHeader , c , r , params.tileExtension ); _tileNames[r*_tileColumns+c] = new char[ strlen(tileName)+1 ]; strcpy( _tileNames[r*_tileColumns+c] , tileName ); } delete[] tileHeader; FILE* fp = fopen( fileName , "w" ); if( !fp ) ERROR_OUT( "Failed to open file for writing: %s" , fileName ); fprintf( fp , "Columns: %d\n" , _tileColumns ); fprintf( fp , "Rows: %d\n" , _tileRows ); for( unsigned int i=0 ; i<_tileRows*_tileColumns ; i++ ) { char* localTileName = FileNameParser::Local( _tileNames[i] ); fprintf( fp , "%s\n" , localTileName ); delete[] localTileName; } fclose( fp ); _currentPixelRow = 0; } TiledImageWriter::~TiledImageWriter( void ) { delete[] _tileWriters; for( unsigned int i=0 ; i<_tileColumns*_tileRows ; i++ ) delete[] _tileNames[i]; delete[] _tileNames; } unsigned int TiledImageWriter::nextRow( const unsigned char* row ) { unsigned int r = _currentPixelRow / _tileHeight; if( ( _currentPixelRow % _tileHeight )==0 ) { for( unsigned int c=0 ; c<_tileColumns ; c++ ) _tileWriters[c] = ImageWriter::Get( _tileNames[ r * _tileColumns + c ] , std::min< unsigned int >( _tileWidth , _width - _tileWidth*c ) , std::min< unsigned int >( _tileHeight , _height - _tileHeight*r ) , _channels , _params ); } for( int c=0 ; c<(int)_tileColumns ; c++ ) _tileWriters[c]->nextRow( row + c * _tileWidth * _channels ); if( ( _currentPixelRow % _tileHeight )==( _tileHeight-1 ) || _currentPixelRow==(_height-1) ) for( unsigned int c=0 ; c<_tileColumns ; c++ ) delete _tileWriters[c]; return _currentPixelRow++; } #endif // SUPPORT_TILES #endif // IMAGE_INCLUDED