372 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			372 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (C) 2002-2012 Nikolaus Gebhardt
 | |
| // This file is part of the "Irrlicht Engine".
 | |
| // For conditions of distribution and use, see copyright notice in irrlicht.h
 | |
| 
 | |
| #include "CImageLoaderBMP.h"
 | |
| 
 | |
| #include "IReadFile.h"
 | |
| #include "SColor.h"
 | |
| #include "CColorConverter.h"
 | |
| #include "CImage.h"
 | |
| #include "os.h"
 | |
| #include "irrString.h"
 | |
| 
 | |
| namespace irr
 | |
| {
 | |
| namespace video
 | |
| {
 | |
| 
 | |
| 
 | |
| //! constructor
 | |
| CImageLoaderBMP::CImageLoaderBMP()
 | |
| {
 | |
| 	#ifdef _DEBUG
 | |
| 	setDebugName("CImageLoaderBMP");
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| 
 | |
| //! returns true if the file maybe is able to be loaded by this class
 | |
| //! based on the file extension (e.g. ".tga")
 | |
| bool CImageLoaderBMP::isALoadableFileExtension(const io::path& filename) const
 | |
| {
 | |
| 	return core::hasFileExtension ( filename, "bmp" );
 | |
| }
 | |
| 
 | |
| 
 | |
| //! returns true if the file maybe is able to be loaded by this class
 | |
| bool CImageLoaderBMP::isALoadableFileFormat(io::IReadFile* file) const
 | |
| {
 | |
| 	u16 headerID;
 | |
| 	file->read(&headerID, sizeof(u16));
 | |
| #ifdef __BIG_ENDIAN__
 | |
| 	headerID = os::Byteswap::byteswap(headerID);
 | |
| #endif
 | |
| 	return headerID == 0x4d42;
 | |
| }
 | |
| 
 | |
| 
 | |
| void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
 | |
| {
 | |
| 	u8* p = bmpData;
 | |
| 	u8* newBmp = new u8[(width+pitch)*height];
 | |
| 	u8* d = newBmp;
 | |
| 	u8* destEnd = newBmp + (width+pitch)*height;
 | |
| 	s32 line = 0;
 | |
| 
 | |
| 	while (bmpData - p < size && d < destEnd)
 | |
| 	{
 | |
| 		if (*p == 0)
 | |
| 		{
 | |
| 			++p;
 | |
| 
 | |
| 			switch(*p)
 | |
| 			{
 | |
| 			case 0: // end of line
 | |
| 				++p;
 | |
| 				++line;
 | |
| 				d = newBmp + (line*(width+pitch));
 | |
| 				break;
 | |
| 			case 1: // end of bmp
 | |
| 				delete [] bmpData;
 | |
| 				bmpData = newBmp;
 | |
| 				return;
 | |
| 			case 2:
 | |
| 				++p; d +=(u8)*p;  // delta
 | |
| 				++p; d += ((u8)*p)*(width+pitch);
 | |
| 				++p;
 | |
| 				break;
 | |
| 			default:
 | |
| 				{
 | |
| 					// absolute mode
 | |
| 					s32 count = (u8)*p; ++p;
 | |
| 					s32 readAdditional = ((2-(count%2))%2);
 | |
| 					s32 i;
 | |
| 
 | |
| 					for (i=0; i<count; ++i)
 | |
| 					{
 | |
| 						*d = *p;
 | |
| 						++p;
 | |
| 						++d;
 | |
| 					}
 | |
| 
 | |
| 					for (i=0; i<readAdditional; ++i)
 | |
| 						++p;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			s32 count = (u8)*p; ++p;
 | |
| 			u8 color = *p; ++p;
 | |
| 			for (s32 i=0; i<count; ++i)
 | |
| 			{
 | |
| 				*d = color;
 | |
| 				++d;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	delete [] bmpData;
 | |
| 	bmpData = newBmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
 | |
| {
 | |
| 	s32 lineWidth = (width+1)/2+pitch;
 | |
| 	u8* p = bmpData;
 | |
| 	u8* newBmp = new u8[lineWidth*height];
 | |
| 	u8* d = newBmp;
 | |
| 	u8* destEnd = newBmp + lineWidth*height;
 | |
| 	s32 line = 0;
 | |
| 	s32 shift = 4;
 | |
| 
 | |
| 	while (bmpData - p < size && d < destEnd)
 | |
| 	{
 | |
| 		if (*p == 0)
 | |
| 		{
 | |
| 			++p;
 | |
| 
 | |
| 			switch(*p)
 | |
| 			{
 | |
| 			case 0: // end of line
 | |
| 				++p;
 | |
| 				++line;
 | |
| 				d = newBmp + (line*lineWidth);
 | |
| 				shift = 4;
 | |
| 				break;
 | |
| 			case 1: // end of bmp
 | |
| 				delete [] bmpData;
 | |
| 				bmpData = newBmp;
 | |
| 				return;
 | |
| 			case 2:
 | |
| 				{
 | |
| 					++p;
 | |
| 					s32 x = (u8)*p; ++p;
 | |
| 					s32 y = (u8)*p; ++p;
 | |
| 					d += x/2 + y*lineWidth;
 | |
| 					shift = x%2==0 ? 4 : 0;
 | |
| 				}
 | |
| 				break;
 | |
| 			default:
 | |
| 				{
 | |
| 					// absolute mode
 | |
| 					s32 count = (u8)*p; ++p;
 | |
| 					s32 readAdditional = ((2-((count)%2))%2);
 | |
| 					s32 readShift = 4;
 | |
| 					s32 i;
 | |
| 
 | |
| 					for (i=0; i<count; ++i)
 | |
| 					{
 | |
| 						s32 color = (((u8)*p) >> readShift) & 0x0f;
 | |
| 						readShift -= 4;
 | |
| 						if (readShift < 0)
 | |
| 						{
 | |
| 							++*p;
 | |
| 							readShift = 4;
 | |
| 						}
 | |
| 
 | |
| 						u8 mask = 0x0f << shift;
 | |
| 						*d = (*d & (~mask)) | ((color << shift) & mask);
 | |
| 
 | |
| 						shift -= 4;
 | |
| 						if (shift < 0)
 | |
| 						{
 | |
| 							shift = 4;
 | |
| 							++d;
 | |
| 						}
 | |
| 
 | |
| 					}
 | |
| 
 | |
| 					for (i=0; i<readAdditional; ++i)
 | |
| 						++p;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			s32 count = (u8)*p; ++p;
 | |
| 			s32 color1 = (u8)*p; color1 = color1 & 0x0f;
 | |
| 			s32 color2 = (u8)*p; color2 = (color2 >> 4) & 0x0f;
 | |
| 			++p;
 | |
| 
 | |
| 			for (s32 i=0; i<count; ++i)
 | |
| 			{
 | |
| 				u8 mask = 0x0f << shift;
 | |
| 				u8 toSet = (shift==0 ? color1 : color2) << shift;
 | |
| 				*d = (*d & (~mask)) | (toSet & mask);
 | |
| 
 | |
| 				shift -= 4;
 | |
| 				if (shift < 0)
 | |
| 				{
 | |
| 					shift = 4;
 | |
| 					++d;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	delete [] bmpData;
 | |
| 	bmpData = newBmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| //! creates a surface from the file
 | |
| IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
 | |
| {
 | |
| 	SBMPHeader header;
 | |
| 
 | |
| 	file->read(&header, sizeof(header));
 | |
| 
 | |
| #ifdef __BIG_ENDIAN__
 | |
| 	header.Id = os::Byteswap::byteswap(header.Id);
 | |
| 	header.FileSize = os::Byteswap::byteswap(header.FileSize);
 | |
| 	header.BitmapDataOffset = os::Byteswap::byteswap(header.BitmapDataOffset);
 | |
| 	header.BitmapHeaderSize = os::Byteswap::byteswap(header.BitmapHeaderSize);
 | |
| 	header.Width = os::Byteswap::byteswap(header.Width);
 | |
| 	header.Height = os::Byteswap::byteswap(header.Height);
 | |
| 	header.Planes = os::Byteswap::byteswap(header.Planes);
 | |
| 	header.BPP = os::Byteswap::byteswap(header.BPP);
 | |
| 	header.Compression = os::Byteswap::byteswap(header.Compression);
 | |
| 	header.BitmapDataSize = os::Byteswap::byteswap(header.BitmapDataSize);
 | |
| 	header.PixelPerMeterX = os::Byteswap::byteswap(header.PixelPerMeterX);
 | |
| 	header.PixelPerMeterY = os::Byteswap::byteswap(header.PixelPerMeterY);
 | |
| 	header.Colors = os::Byteswap::byteswap(header.Colors);
 | |
| 	header.ImportantColors = os::Byteswap::byteswap(header.ImportantColors);
 | |
| #endif
 | |
| 
 | |
| 	s32 pitch = 0;
 | |
| 
 | |
| 	//! return if the header is false
 | |
| 
 | |
| 	if (header.Id != 0x4d42)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (header.Compression > 2) // we'll only handle RLE-Compression
 | |
| 	{
 | |
| 		os::Printer::log("Compression mode not supported.", ELL_ERROR);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (header.BPP > 32 || !checkImageDimensions(header.Width, header.Height))
 | |
| 	{
 | |
| 		os::Printer::log("Rejecting BMP with unreasonable size or BPP.", ELL_ERROR);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	// adjust bitmap data size to dword boundary
 | |
| 	header.BitmapDataSize += (4-(header.BitmapDataSize%4))%4;
 | |
| 
 | |
| 	// read palette
 | |
| 
 | |
| 	long pos = file->getPos();
 | |
| 	s32 paletteSize = (header.BitmapDataOffset - pos) / 4;
 | |
| 
 | |
| 	s32* paletteData = 0;
 | |
| 	if (paletteSize)
 | |
| 	{
 | |
| 		paletteData = new s32[paletteSize];
 | |
| 		file->read(paletteData, paletteSize * sizeof(s32));
 | |
| #ifdef __BIG_ENDIAN__
 | |
| 		for (s32 i=0; i<paletteSize; ++i)
 | |
| 			paletteData[i] = os::Byteswap::byteswap(paletteData[i]);
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	// read image data
 | |
| 
 | |
| 	if (!header.BitmapDataSize)
 | |
| 	{
 | |
| 		// okay, lets guess the size
 | |
| 		// some tools simply don't set it
 | |
| 		header.BitmapDataSize = static_cast<u32>(file->getSize()) - header.BitmapDataOffset;
 | |
| 	}
 | |
| 
 | |
| 	file->seek(header.BitmapDataOffset);
 | |
| 
 | |
| 	f32 t = (header.Width) * (header.BPP / 8.0f);
 | |
| 	s32 widthInBytes = (s32)t;
 | |
| 	t -= widthInBytes;
 | |
| 	if (t!=0.0f)
 | |
| 		++widthInBytes;
 | |
| 
 | |
| 	s32 lineData = widthInBytes + ((4-(widthInBytes%4)))%4;
 | |
| 	pitch = lineData - widthInBytes;
 | |
| 
 | |
| 	u8* bmpData = new u8[header.BitmapDataSize];
 | |
| 	file->read(bmpData, header.BitmapDataSize);
 | |
| 
 | |
| 	// decompress data if needed
 | |
| 	switch(header.Compression)
 | |
| 	{
 | |
| 	case 1: // 8 bit rle
 | |
| 		decompress8BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
 | |
| 		break;
 | |
| 	case 2: // 4 bit rle
 | |
| 		decompress4BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	// create surface
 | |
| 
 | |
| 	// no default constructor from packed area! ARM problem!
 | |
| 	core::dimension2d<u32> dim;
 | |
| 	dim.Width = header.Width;
 | |
| 	dim.Height = header.Height;
 | |
| 
 | |
| 	IImage* image = 0;
 | |
| 	switch(header.BPP)
 | |
| 	{
 | |
| 	case 1:
 | |
| 		image = new CImage(ECF_A1R5G5B5, dim);
 | |
| 		if (image)
 | |
| 			CColorConverter::convert1BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);
 | |
| 		break;
 | |
| 	case 4:
 | |
| 		image = new CImage(ECF_A1R5G5B5, dim);
 | |
| 		if (image)
 | |
| 			CColorConverter::convert4BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);
 | |
| 		break;
 | |
| 	case 8:
 | |
| 		image = new CImage(ECF_A1R5G5B5, dim);
 | |
| 		if (image)
 | |
| 			CColorConverter::convert8BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);
 | |
| 		break;
 | |
| 	case 16:
 | |
| 		image = new CImage(ECF_A1R5G5B5, dim);
 | |
| 		if (image)
 | |
| 			CColorConverter::convert16BitTo16Bit((s16*)bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);
 | |
| 		break;
 | |
| 	case 24:
 | |
| 		image = new CImage(ECF_R8G8B8, dim);
 | |
| 		if (image)
 | |
| 			CColorConverter::convert24BitTo24Bit(bmpData, (u8*)image->getData(), header.Width, header.Height, pitch, true, true);
 | |
| 		break;
 | |
| 	case 32: // thx to Reinhard Ostermeier
 | |
| 		image = new CImage(ECF_A8R8G8B8, dim);
 | |
| 		if (image)
 | |
| 			CColorConverter::convert32BitTo32Bit((s32*)bmpData, (s32*)image->getData(), header.Width, header.Height, pitch, true);
 | |
| 		break;
 | |
| 	};
 | |
| 
 | |
| 	// clean up
 | |
| 
 | |
| 	delete [] paletteData;
 | |
| 	delete [] bmpData;
 | |
| 
 | |
| 	return image;
 | |
| }
 | |
| 
 | |
| 
 | |
| //! creates a loader which is able to load windows bitmaps
 | |
| IImageLoader* createImageLoaderBMP()
 | |
| {
 | |
| 	return new CImageLoaderBMP;
 | |
| }
 | |
| 
 | |
| 
 | |
| } // end namespace video
 | |
| } // end namespace irr
 |