http://www.liamdevine.co.uk

Bitmap


Creative Commons License This work is licenced under a Creative Commons Licence.

When I first started university we always had the problem of how to load a bitmap, a tutor gave the class a library and no source but it had been compiled on Visual Studio 6 and was about as much use a chocolate fire guard. Then when using OpenGL there is the auxiliary library; yet this leaks memory.
So here I post a Bitmap loading class under the Creative Commone Licence, which you are free to use and abuse under the licence restrictions.
I have posted parts of this source before at various locations such as gpwiki. If anybody would like me to add to this the saving of bitmap images then just contact me.

Files:
The below inlined files and two other required files are available here.

Bitmap.h
///////////////////////////////////////////////////////////////////////////////
///  bmp.h
///  Simple bitmap loader which can load 24 bpp and 8 bpp images including
///  non multiple of four image widths.
///  @License
///  This work is licenced under a Creative Commons Licence.
///  see:
///  http://creativecommons.org/licenses/by-nc-sa/3.0/
///  and:
///  http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode
///  For more details.
///  
///  @author Liam Devine
///  @email: see www.liamdevine.co.uk for contact details.
///////////////////////////////////////////////////////////////////////////////
#ifndef BMP_H_
#	define BMP_H_
#	include <string>
#	include "sized_types.h"
#	include <vector>



	//byte-align structures
#	ifdef _MSC_VER
#		pragma pack( push, packing )
#		pragma pack( 1 )
#		define PACK_STRUCT
#	elif   defined( __GNUC__ ) 
#		define PACK_STRUCT	__attribute__((__packed__))
#	else
#		error you must byte-align these structures with the appropriate compiler directives
#	endif

namespace LVD
{
	namespace BMP

	{
		struct File_header
		{
			uint16	type;//magic number
			uint32	size;//byte size of bitmap

			uint16	res1;//must be zero
			uint16	res2;//must be zero
			uint32	off_bits;//the offset, in bytes, from the beginning of the structure to the bitmap bits.
		}PACK_STRUCT;

		struct Info_header
		{
			uint32	size;//number of bytes required by the structure.
			int32	width;//width of the bitmap, in pixels.

			int32	height;//height of the bitmap, in pixels. 
			/*
			If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. 
			If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.
			If biHeight is negative, indicating a top-down DIB, compression must be either BI_RGB or BI_BITFIELDS. 
			Top-down DIBs cannot be compressed. 
			*/
			uint16	planes;
			uint16	bit_count;
			uint32	compression;

			uint32	size_image;
			int32	x_pels_per_meter;
			int32	y_pels_per_meter;
			uint32	clr_used;
			uint32	clr_important;
		}PACK_STRUCT;

		struct Colour_32bit
		{
			byte	blue;
			byte	green;

			byte	red;
			byte	alpha;
		}PACK_STRUCT;

		typedef Colour_32bit Color_32bit;

		enum 
		{ 
			FILE =0x4D42,
			UNCOMPRESSED = 0x0

		};
	}//namespace BMP


#ifdef _MSC_VER
#	pragma pack( pop, packing )
#endif

#undef PACK_STRUCT


	class Bitmap
	{
	public:

		Bitmap();
		Bitmap(std::string const& file_name,bool convert_24 = true);

		Bitmap(Bitmap const& rhs);
		Bitmap& operator = (Bitmap const& rhs);

		operator byte* const()const;
		byte* const data()const;

		int const& width()const;
		int const& height()const;

		int const& data_width()const;
		int const& bpp()const;

		bool load(std::string const&  file_name,bool convert_24 = true);

		BMP::Colour_32bit const* palette( void )const;

		void invert();
	private:
		void convert_8_to_24_rgb();//convert to 24bit RGB bottom up data
		void convert_24_rgb();

		void convert_24_bgr();
		void rgb_palette(void);
		void file_data(std::string const& file, std::vector<byte>& tmp_buffer);

		void internal_load(std::string const& file_name,bool convert_24 = true);

		void read_image_data(bool const should_invert,std::vector<byte>& image_buffer,LVD::uint32 const image_start);

		int m_bpp,m_width,m_data_width,m_height;
		std::vector<byte> m_data;

		BMP::Colour_32bit m_palette[256];//the palette.
	};

}
#endif

Bitmap.cpp
#	include <fstream>
#	include <cassert>

#	include <stdexcept>
#	include "bmp.h"

//disable unsafe warnings for std::copy
#		if( defined(_MSC_VER) && (_MSC_VER >= 1400) )
#			pragma warning( push )
#			pragma warning (disable :4996)
#		endif//_MSC_VER >=1400

namespace LVD
{

	Bitmap::Bitmap():m_bpp(0),m_width(0),m_data_width(0),m_height(0)
	{
	}

	Bitmap::Bitmap(std::string const& file_name,bool convert_24)
	{

		internal_load(file_name,convert_24);
	}
	Bitmap::Bitmap(Bitmap const& rhs)
	{
		*this = rhs;
	}

	Bitmap& Bitmap::operator = (Bitmap const& rhs)
	{
		if(this != &rhs)
		{

			m_bpp = rhs.m_bpp;
			m_width  = rhs.m_width;

			m_data_width =  rhs.m_data_width;
			m_height =  rhs.m_height;

			m_data = rhs.m_data;
			if(m_bpp == 8)

				std::copy(reinterpret_cast<char const *>(rhs.m_palette)
								,reinterpret_cast<char const *>(rhs.m_palette) + sizeof(rhs.m_palette)
								,reinterpret_cast<char *>(m_palette) );
		}

		return *this;
	}
	byte* const Bitmap::data()const

	{
		return (byte* const)( &m_data[0] );
	}

	Bitmap::operator byte* const()const 
	{ 
		return data();
	}

	int const& Bitmap::width()const
	{ 
		return m_width; 
	}

	int const& Bitmap::height()const 
	{ 
		return m_height; 
	}

	int const& Bitmap::data_width()const 
	{ 
		return m_data_width;
	}

	int const& Bitmap::bpp()const 
	{ 
		return m_bpp; 
	}

	BMP::Colour_32bit const* Bitmap::palette( void )const
	{ 
		return m_palette; 
	}


	void Bitmap::convert_24_bgr()
	{

		int const size( m_height * m_data_width );
		int index(0);

		while(index != size)
				{
			std::swap(m_data[index],m_data[index + 2]);

			index+=3;
		}
	}
	void Bitmap::file_data(std::string const& file, std::vector<byte>& tmp_buffer)
	{

		std::ifstream in(file.c_str(), std::ios::in | std::ios::binary );

		if(!in){ throw std::runtime_error("error in opening file"); }

		in.seekg (0, std::ios::end);

		std::ifstream::pos_type size( in.tellg() );
		in.seekg (0, std::ios::beg);

		tmp_buffer.resize(size);
		in.read( reinterpret_cast<char *>(&tmp_buffer[0]), size );
	}

	bool Bitmap::load(std::string const& file_name,bool convert_24)
	{

		try
		{
			internal_load(file_name,convert_24);
		}
		catch (std::runtime_error const& )
		{

			return false;
		}
		catch(...)
		{
			return false;
		}
		return true;
	}

	void Bitmap::internal_load(std::string const& file_name,bool convert_24)
	{

		std::vector<byte> tmp_buffer;
		file_data(file_name,tmp_buffer);

		BMP::File_header* head = reinterpret_cast<BMP::File_header*>(&tmp_buffer[0]);

		if ( head->type != BMP::FILE )
		{ 
			throw std::runtime_error("image file does not contain the bitmap magic number");
		}

		BMP::Info_header* info = 
			reinterpret_cast<BMP::Info_header*>( &tmp_buffer[sizeof(BMP::File_header)] );

		if ( info->compression != BMP::UNCOMPRESSED )
		{ 
			throw std::runtime_error("image uses compression");
		}

		if( (m_bpp = info->bit_count) == 8)
		{
			byte* first = reinterpret_cast<byte*>

							( 
								&tmp_buffer[sizeof(BMP::File_header) + sizeof(BMP::Info_header)] 
							);

			std::copy(first, first + sizeof( m_palette ), reinterpret_cast<byte*>(m_palette) );
		}

		else if( m_bpp != 24 )
		{ 
			throw std::runtime_error("image uses a bits per pixel format which the loader does not handle");
		}

		//tells us if we need to flip the y order
		bool flip (false);//initialise to false

		if( (m_height = info->height) < 0 )//need to flip the image?

		{ 
			m_height = -m_height; 
			flip = true;
		}

		m_width = info->width;//set the width of the bitmap
		m_data_width = m_width * m_bpp>>3;//set the size of a row of the bitmap		

		m_data.resize(m_height * m_data_width);//buffer with the correct size
		
		read_image_data(flip,tmp_buffer,head->off_bits);

		if(m_bpp == 8)
		{ 
			if(convert_24){ convert_8_to_24_rgb(); }

			else rgb_palette();
		}
		else if(m_bpp == 24){ convert_24_rgb(); }

	}

	void Bitmap::read_image_data(bool const should_invert,std::vector<byte>& image_buffer

								,LVD::uint32 const image_start)
	{
		int const padded_width = ( m_data_width +3 ) & ~3;//round up line width to next multiple of 4

		//read in the image data, removing any padding and flipping if need be

		if( should_invert ) //its a top down dib origin upper left corner, we need to flip in the y order	
		{
			int src_row (m_height -1); //initialise the row to the last

			int dst_row (0);
			while( src_row >= 0 )//while we haven't read all the rows

			{	
				byte* first = &image_buffer[image_start + (src_row-- * padded_width)] ;

				std::copy(first
					,first + m_data_width
					,&m_data[dst_row++ * m_data_width] );
			}
		}

		else//its a bottom up dib, we don't need to flip the image
		{
			int row (0);//initialise the row to the first
			while( row < m_height )//while we haven't read all the rows

			{	
				byte* first = &image_buffer[ image_start + (row * padded_width)];

				std::copy( first
					,first + m_data_width
					,&m_data[row++ * m_data_width] );
			}
		}
	}


	//texture coordinates are different for OpenGL and DirectX 
	//so for convenience a function to invert the texture
	//if this is used for both API's.
	void Bitmap::invert()
	{
		std::vector<byte> temp(m_data_width,0);

		byte* start = &m_data[0];
		byte* end = &m_data[0] + (m_height - 1) * m_data_width;

		do 
		{
			std::copy(start, start + m_data_width, &temp[0]);

			std::copy(end, end + m_data_width, start);

			std::copy(&temp[0], &temp[0] + m_data_width, end);

			start += m_data_width; 
			end -= m_data_width ;

		} while(end - start > m_data_width);
	}

	void Bitmap::rgb_palette(void)
	{
		//swap blue and red around top give rgb instead of bgr
		for(int i =0; i<256;i++)
		{

			//gcc complains about the values being packed so use the swap trick
			//std::swap(m_palette[i].blue,m_palette[i].red);
			m_palette[i].blue^=m_palette[i].red^=m_palette[i].blue^=m_palette[i].red;	
		}
	}


	void Bitmap::convert_8_to_24_rgb()
	{
		assert(m_bpp == 8);

		m_data_width = m_width * 3;
		m_bpp = 24;

		std::vector<byte> tmp(m_height* m_data_width,0);

		int index(0);
		int i(0);
		int const size(m_height * m_width);

		//swap the colours around and insert to the data;
		while (index != size)
		{
			tmp[i  ] = m_palette[ m_data[index] ].red;

			tmp[i+1] = m_palette[ m_data[index] ].green;

			tmp[i+2] = m_palette[ m_data[index] ].blue;
			++index;

			i+=3;
		}
		m_data.swap(tmp);

	}

	void Bitmap::convert_24_rgb()
	{

		int const size( m_height * m_data_width );
		int index(0);

		while(index != size)
		{
			std::swap(m_data[index],m_data[index + 2]);

			index+=3;
		}
	}
}

//enable unsafe warnings again after disabling for std::copy
#	if( defined(_MSC_VER) && (_MSC_VER >= 1400) )
#			pragma warning (pop)
#	endif//_MSC_VER >=1400

About Me | Site Map | Valid xhtml? | ©2006 - 2009 Liam Devine