text: /* Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
   /* File for "Textures" lesson of the OpenGL tutorial on
    * www.videotutorialsrock.com
    */
  
  
  
  #include <assert.h>
  #include <fstream>
  
  #include "ImageLoader.h"
  
  using namespace std;
  
  Image::Image( char* ps, int w, int h ) : pixels( ps ), width( w ), height( h )
  {
  
  }
  
  Image::~Image()
  {
  	delete[] pixels;
  }
  
  namespace
  {
  	//Converts a four-character array to an integer, using little-endian form
  	int toInt( const char* bytes )
  	{
  		return (int)( ( (unsigned char)bytes[ 3 ] << 24 ) |
  			( (unsigned char)bytes[ 2 ] << 16 ) |
  					  ( (unsigned char)bytes[ 1 ] << 8 ) |
  					  (unsigned char)bytes[ 0 ] );
  	}
  
  	//Converts a two-character array to a short, using little-endian form
  	short toShort( const char* bytes )
  	{
  		return (short)( ( (unsigned char)bytes[ 1 ] << 8 ) |
  			(unsigned char)bytes[ 0 ] );
  	}
  
  	//Reads the next four bytes as an integer, using little-endian form
  	int readInt( ifstream &input )
  	{
  		char buffer[ 4 ];
  		input.read( buffer, 4 );
  		return toInt( buffer );
  	}
  
  	//Reads the next two bytes as a short, using little-endian form
  	short readShort( ifstream &input )
  	{
  		char buffer[ 2 ];
  		input.read( buffer, 2 );
  		return toShort( buffer );
  	}
  
  	//Just like auto_ptr, but for arrays
  	template<class T>
  	class auto_array
  	{
  	private:
  		T* array;
  		mutable bool isReleased;
  	public:
  		explicit auto_array( T* array_ = NULL ) :
  			array( array_ ), isReleased( false )
  		{
  		}
  
  		auto_array( const auto_array<T> &aarray )
  		{
  			array = aarray.array;
  			isReleased = aarray.isReleased;
  			aarray.isReleased = true;
  		}
  
  		~auto_array()
  		{
  			if( !isReleased && array != NULL )
  			{
  				delete[] array;
  			}
  		}
  
  		T* get() const
  		{
  			return array;
  		}
  
  		T &operator*() const
  		{
  			return *array;
  		}
  
  		void operator=( const auto_array<T> &aarray )
  		{
  			if( !isReleased && array != NULL )
  			{
  				delete[] array;
  			}
  			array = aarray.array;
  			isReleased = aarray.isReleased;
  			aarray.isReleased = true;
  		}
  
  		T* operator->() const
  		{
  			return array;
  		}
  
  		T* release()
  		{
  			isReleased = true;
  			return array;
  		}
  
  		void reset( T* array_ = NULL )
  		{
  			if( !isReleased && array != NULL )
  			{
  				delete[] array;
  			}
  			array = array_;
  		}
  
  		T* operator+( int i )
  		{
  			return array + i;
  		}
  
  		T &operator[]( int i )
  		{
  			return array[ i ];
  		}
  	};
  }
  
  Image* loadBMP( const char* filename )
  {
  	ifstream input;
  	input.open( filename, ifstream::binary );
  	assert( !input.fail() || !"Could not find file" );
  	char buffer[ 2 ];
  	input.read( buffer, 2 );
  	assert( buffer[ 0 ] == 'B' && buffer[ 1 ] == 'M' || !"Not a bitmap file" );
  	input.ignore( 8 );
  	int dataOffset = readInt( input );
  
  	//Read the header
  	int headerSize = readInt( input );
  	int width;
  	int height;
  	switch( headerSize )
  	{
  		case 40:
  			//V3
  			width = readInt( input );
  			height = readInt( input );
  			input.ignore( 2 );
  			assert( readShort( input ) == 24 || !"Image is not 24 bits per pixel" );
  			assert( readShort( input ) == 0 || !"Image is compressed" );
  			break;
  		case 12:
  			//OS/2 V1
  			width = readShort( input );
  			height = readShort( input );
  			input.ignore( 2 );
  			assert( readShort( input ) == 24 || !"Image is not 24 bits per pixel" );
  			break;
  		case 64:
  			//OS/2 V2
  			assert( !"Can't load OS/2 V2 bitmaps" );
  			break;
  		case 108:
  			//Windows V4
  			assert( !"Can't load Windows V4 bitmaps" );
  			break;
  		case 124:
  			//Windows V5
  			assert( !"Can't load Windows V5 bitmaps" );
  			break;
  		default:
  			assert( !"Unknown bitmap format" );
  	}
  
  	//Read the data
  	int bytesPerRow = ( ( width * 3 + 3 ) / 4 ) * 4 - ( width * 3 % 4 );
  	int size = bytesPerRow * height;
  	auto_array<char> pixels( new char[ size ] );
  	input.seekg( dataOffset, ios_base::beg );
  	input.read( pixels.get(), size );
  
  	//Get the data into the right format
  	auto_array<char> pixels2( new char[ width * height * 3 ] );
  	for( int y = 0; y < height; y++ )
  	{
  		for( int x = 0; x < width; x++ )
  		{
  			for( int c = 0; c < 3; c++ )
  			{
  				pixels2[ 3 * ( width * y + x ) + c ] =
  					pixels[ bytesPerRow * y + 3 * x + ( 2 - c ) ];
  			}
  		}
  	}
  
  	input.close();
  	return new Image( pixels2.release(), width, height );
  }
  
  
  
  
  
  
  
  
  
  