1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
5 #include "CImageLoaderBMP.h"
\r
7 #include "IReadFile.h"
\r
9 #include "CColorConverter.h"
\r
12 #include "irrString.h"
\r
21 CImageLoaderBMP::CImageLoaderBMP()
\r
24 setDebugName("CImageLoaderBMP");
\r
29 //! returns true if the file maybe is able to be loaded by this class
\r
30 //! based on the file extension (e.g. ".tga")
\r
31 bool CImageLoaderBMP::isALoadableFileExtension(const io::path& filename) const
\r
33 return core::hasFileExtension ( filename, "bmp" );
\r
37 //! returns true if the file maybe is able to be loaded by this class
\r
38 bool CImageLoaderBMP::isALoadableFileFormat(io::IReadFile* file) const
\r
41 file->read(&headerID, sizeof(u16));
\r
42 #ifdef __BIG_ENDIAN__
\r
43 headerID = os::Byteswap::byteswap(headerID);
\r
45 return headerID == 0x4d42;
\r
49 void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
\r
52 u8* newBmp = new u8[(width+pitch)*height];
\r
54 u8* destEnd = newBmp + (width+pitch)*height;
\r
57 while (bmpData - p < size && d < destEnd)
\r
65 case 0: // end of line
\r
68 d = newBmp + (line*(width+pitch));
\r
70 case 1: // end of bmp
\r
75 ++p; d +=(u8)*p; // delta
\r
76 ++p; d += ((u8)*p)*(width+pitch);
\r
82 s32 count = (u8)*p; ++p;
\r
83 s32 readAdditional = ((2-(count%2))%2);
\r
86 for (i=0; i<count; ++i)
\r
93 for (i=0; i<readAdditional; ++i)
\r
100 s32 count = (u8)*p; ++p;
\r
101 u8 color = *p; ++p;
\r
102 for (s32 i=0; i<count; ++i)
\r
115 void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
\r
117 s32 lineWidth = (width+1)/2+pitch;
\r
119 u8* newBmp = new u8[lineWidth*height];
\r
121 u8* destEnd = newBmp + lineWidth*height;
\r
125 while (bmpData - p < size && d < destEnd)
\r
133 case 0: // end of line
\r
136 d = newBmp + (line*lineWidth);
\r
139 case 1: // end of bmp
\r
146 s32 x = (u8)*p; ++p;
\r
147 s32 y = (u8)*p; ++p;
\r
148 d += x/2 + y*lineWidth;
\r
149 shift = x%2==0 ? 4 : 0;
\r
155 s32 count = (u8)*p; ++p;
\r
156 s32 readAdditional = ((2-((count)%2))%2);
\r
160 for (i=0; i<count; ++i)
\r
162 s32 color = (((u8)*p) >> readShift) & 0x0f;
\r
170 u8 mask = 0x0f << shift;
\r
171 *d = (*d & (~mask)) | ((color << shift) & mask);
\r
182 for (i=0; i<readAdditional; ++i)
\r
189 s32 count = (u8)*p; ++p;
\r
190 s32 color1 = (u8)*p; color1 = color1 & 0x0f;
\r
191 s32 color2 = (u8)*p; color2 = (color2 >> 4) & 0x0f;
\r
194 for (s32 i=0; i<count; ++i)
\r
196 u8 mask = 0x0f << shift;
\r
197 u8 toSet = (shift==0 ? color1 : color2) << shift;
\r
198 *d = (*d & (~mask)) | (toSet & mask);
\r
216 //! creates a surface from the file
\r
217 IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
\r
221 file->read(&header, sizeof(header));
\r
223 #ifdef __BIG_ENDIAN__
\r
224 header.Id = os::Byteswap::byteswap(header.Id);
\r
225 header.FileSize = os::Byteswap::byteswap(header.FileSize);
\r
226 header.BitmapDataOffset = os::Byteswap::byteswap(header.BitmapDataOffset);
\r
227 header.BitmapHeaderSize = os::Byteswap::byteswap(header.BitmapHeaderSize);
\r
228 header.Width = os::Byteswap::byteswap(header.Width);
\r
229 header.Height = os::Byteswap::byteswap(header.Height);
\r
230 header.Planes = os::Byteswap::byteswap(header.Planes);
\r
231 header.BPP = os::Byteswap::byteswap(header.BPP);
\r
232 header.Compression = os::Byteswap::byteswap(header.Compression);
\r
233 header.BitmapDataSize = os::Byteswap::byteswap(header.BitmapDataSize);
\r
234 header.PixelPerMeterX = os::Byteswap::byteswap(header.PixelPerMeterX);
\r
235 header.PixelPerMeterY = os::Byteswap::byteswap(header.PixelPerMeterY);
\r
236 header.Colors = os::Byteswap::byteswap(header.Colors);
\r
237 header.ImportantColors = os::Byteswap::byteswap(header.ImportantColors);
\r
242 //! return if the header is false
\r
244 if (header.Id != 0x4d42)
\r
247 if (header.Compression > 2) // we'll only handle RLE-Compression
\r
249 os::Printer::log("Compression mode not supported.", ELL_ERROR);
\r
253 if (header.BPP > 32 || !checkImageDimensions(header.Width, header.Height))
\r
255 os::Printer::log("Rejecting BMP with unreasonable size or BPP.", ELL_ERROR);
\r
259 // adjust bitmap data size to dword boundary
\r
260 header.BitmapDataSize += (4-(header.BitmapDataSize%4))%4;
\r
264 long pos = file->getPos();
\r
265 s32 paletteSize = (header.BitmapDataOffset - pos) / 4;
\r
267 s32* paletteData = 0;
\r
270 paletteData = new s32[paletteSize];
\r
271 file->read(paletteData, paletteSize * sizeof(s32));
\r
272 #ifdef __BIG_ENDIAN__
\r
273 for (s32 i=0; i<paletteSize; ++i)
\r
274 paletteData[i] = os::Byteswap::byteswap(paletteData[i]);
\r
280 if (!header.BitmapDataSize)
\r
282 // okay, lets guess the size
\r
283 // some tools simply don't set it
\r
284 header.BitmapDataSize = static_cast<u32>(file->getSize()) - header.BitmapDataOffset;
\r
287 file->seek(header.BitmapDataOffset);
\r
289 f32 t = (header.Width) * (header.BPP / 8.0f);
\r
290 s32 widthInBytes = (s32)t;
\r
295 s32 lineData = widthInBytes + ((4-(widthInBytes%4)))%4;
\r
296 pitch = lineData - widthInBytes;
\r
298 u8* bmpData = new u8[header.BitmapDataSize];
\r
299 file->read(bmpData, header.BitmapDataSize);
\r
301 // decompress data if needed
\r
302 switch(header.Compression)
\r
304 case 1: // 8 bit rle
\r
305 decompress8BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
\r
307 case 2: // 4 bit rle
\r
308 decompress4BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
\r
314 // no default constructor from packed area! ARM problem!
\r
315 core::dimension2d<u32> dim;
\r
316 dim.Width = header.Width;
\r
317 dim.Height = header.Height;
\r
323 image = new CImage(ECF_A1R5G5B5, dim);
\r
325 CColorConverter::convert1BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);
\r
328 image = new CImage(ECF_A1R5G5B5, dim);
\r
330 CColorConverter::convert4BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);
\r
333 image = new CImage(ECF_A1R5G5B5, dim);
\r
335 CColorConverter::convert8BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);
\r
338 image = new CImage(ECF_A1R5G5B5, dim);
\r
340 CColorConverter::convert16BitTo16Bit((s16*)bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);
\r
343 image = new CImage(ECF_R8G8B8, dim);
\r
345 CColorConverter::convert24BitTo24Bit(bmpData, (u8*)image->getData(), header.Width, header.Height, pitch, true, true);
\r
347 case 32: // thx to Reinhard Ostermeier
\r
348 image = new CImage(ECF_A8R8G8B8, dim);
\r
350 CColorConverter::convert32BitTo32Bit((s32*)bmpData, (s32*)image->getData(), header.Width, header.Height, pitch, true);
\r
356 delete [] paletteData;
\r
363 //! creates a loader which is able to load windows bitmaps
\r
364 IImageLoader* createImageLoaderBMP()
\r
366 return new CImageLoaderBMP;
\r
370 } // end namespace video
\r
371 } // end namespace irr
\r