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 #ifdef _IRR_COMPILE_WITH_BMP_LOADER_
\r
9 #include "IReadFile.h"
\r
11 #include "CColorConverter.h"
\r
14 #include "irrString.h"
\r
23 CImageLoaderBMP::CImageLoaderBMP()
\r
26 setDebugName("CImageLoaderBMP");
\r
31 //! returns true if the file maybe is able to be loaded by this class
\r
32 //! based on the file extension (e.g. ".tga")
\r
33 bool CImageLoaderBMP::isALoadableFileExtension(const io::path& filename) const
\r
35 return core::hasFileExtension ( filename, "bmp" );
\r
39 //! returns true if the file maybe is able to be loaded by this class
\r
40 bool CImageLoaderBMP::isALoadableFileFormat(io::IReadFile* file) const
\r
43 file->read(&headerID, sizeof(u16));
\r
44 #ifdef __BIG_ENDIAN__
\r
45 headerID = os::Byteswap::byteswap(headerID);
\r
47 return headerID == 0x4d42;
\r
51 void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
\r
54 u8* newBmp = new u8[(width+pitch)*height];
\r
56 u8* destEnd = newBmp + (width+pitch)*height;
\r
59 while (bmpData - p < size && d < destEnd)
\r
67 case 0: // end of line
\r
70 d = newBmp + (line*(width+pitch));
\r
72 case 1: // end of bmp
\r
77 ++p; d +=(u8)*p; // delta
\r
78 ++p; d += ((u8)*p)*(width+pitch);
\r
84 s32 count = (u8)*p; ++p;
\r
85 s32 readAdditional = ((2-(count%2))%2);
\r
88 for (i=0; i<count; ++i)
\r
95 for (i=0; i<readAdditional; ++i)
\r
102 s32 count = (u8)*p; ++p;
\r
103 u8 color = *p; ++p;
\r
104 for (s32 i=0; i<count; ++i)
\r
117 void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
\r
119 s32 lineWidth = (width+1)/2+pitch;
\r
121 u8* newBmp = new u8[lineWidth*height];
\r
123 u8* destEnd = newBmp + lineWidth*height;
\r
127 while (bmpData - p < size && d < destEnd)
\r
135 case 0: // end of line
\r
138 d = newBmp + (line*lineWidth);
\r
141 case 1: // end of bmp
\r
148 s32 x = (u8)*p; ++p;
\r
149 s32 y = (u8)*p; ++p;
\r
150 d += x/2 + y*lineWidth;
\r
151 shift = x%2==0 ? 4 : 0;
\r
157 s32 count = (u8)*p; ++p;
\r
158 s32 readAdditional = ((2-((count)%2))%2);
\r
162 for (i=0; i<count; ++i)
\r
164 s32 color = (((u8)*p) >> readShift) & 0x0f;
\r
172 u8 mask = 0x0f << shift;
\r
173 *d = (*d & (~mask)) | ((color << shift) & mask);
\r
184 for (i=0; i<readAdditional; ++i)
\r
191 s32 count = (u8)*p; ++p;
\r
192 s32 color1 = (u8)*p; color1 = color1 & 0x0f;
\r
193 s32 color2 = (u8)*p; color2 = (color2 >> 4) & 0x0f;
\r
196 for (s32 i=0; i<count; ++i)
\r
198 u8 mask = 0x0f << shift;
\r
199 u8 toSet = (shift==0 ? color1 : color2) << shift;
\r
200 *d = (*d & (~mask)) | (toSet & mask);
\r
218 //! creates a surface from the file
\r
219 IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
\r
223 file->read(&header, sizeof(header));
\r
225 #ifdef __BIG_ENDIAN__
\r
226 header.Id = os::Byteswap::byteswap(header.Id);
\r
227 header.FileSize = os::Byteswap::byteswap(header.FileSize);
\r
228 header.BitmapDataOffset = os::Byteswap::byteswap(header.BitmapDataOffset);
\r
229 header.BitmapHeaderSize = os::Byteswap::byteswap(header.BitmapHeaderSize);
\r
230 header.Width = os::Byteswap::byteswap(header.Width);
\r
231 header.Height = os::Byteswap::byteswap(header.Height);
\r
232 header.Planes = os::Byteswap::byteswap(header.Planes);
\r
233 header.BPP = os::Byteswap::byteswap(header.BPP);
\r
234 header.Compression = os::Byteswap::byteswap(header.Compression);
\r
235 header.BitmapDataSize = os::Byteswap::byteswap(header.BitmapDataSize);
\r
236 header.PixelPerMeterX = os::Byteswap::byteswap(header.PixelPerMeterX);
\r
237 header.PixelPerMeterY = os::Byteswap::byteswap(header.PixelPerMeterY);
\r
238 header.Colors = os::Byteswap::byteswap(header.Colors);
\r
239 header.ImportantColors = os::Byteswap::byteswap(header.ImportantColors);
\r
244 //! return if the header is false
\r
246 if (header.Id != 0x4d42)
\r
249 if (header.Compression > 2) // we'll only handle RLE-Compression
\r
251 os::Printer::log("Compression mode not supported.", ELL_ERROR);
\r
255 if (header.BPP > 32 || !checkImageDimensions(header.Width, header.Height))
\r
257 os::Printer::log("Rejecting BMP with unreasonable size or BPP.", ELL_ERROR);
\r
261 // adjust bitmap data size to dword boundary
\r
262 header.BitmapDataSize += (4-(header.BitmapDataSize%4))%4;
\r
266 long pos = file->getPos();
\r
267 s32 paletteSize = (header.BitmapDataOffset - pos) / 4;
\r
269 s32* paletteData = 0;
\r
272 paletteData = new s32[paletteSize];
\r
273 file->read(paletteData, paletteSize * sizeof(s32));
\r
274 #ifdef __BIG_ENDIAN__
\r
275 for (s32 i=0; i<paletteSize; ++i)
\r
276 paletteData[i] = os::Byteswap::byteswap(paletteData[i]);
\r
282 if (!header.BitmapDataSize)
\r
284 // okay, lets guess the size
\r
285 // some tools simply don't set it
\r
286 header.BitmapDataSize = static_cast<u32>(file->getSize()) - header.BitmapDataOffset;
\r
289 file->seek(header.BitmapDataOffset);
\r
291 f32 t = (header.Width) * (header.BPP / 8.0f);
\r
292 s32 widthInBytes = (s32)t;
\r
297 s32 lineData = widthInBytes + ((4-(widthInBytes%4)))%4;
\r
298 pitch = lineData - widthInBytes;
\r
300 u8* bmpData = new u8[header.BitmapDataSize];
\r
301 file->read(bmpData, header.BitmapDataSize);
\r
303 // decompress data if needed
\r
304 switch(header.Compression)
\r
306 case 1: // 8 bit rle
\r
307 decompress8BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
\r
309 case 2: // 4 bit rle
\r
310 decompress4BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
\r
316 // no default constructor from packed area! ARM problem!
\r
317 core::dimension2d<u32> dim;
\r
318 dim.Width = header.Width;
\r
319 dim.Height = header.Height;
\r
325 image = new CImage(ECF_A1R5G5B5, dim);
\r
327 CColorConverter::convert1BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);
\r
330 image = new CImage(ECF_A1R5G5B5, dim);
\r
332 CColorConverter::convert4BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);
\r
335 image = new CImage(ECF_A1R5G5B5, dim);
\r
337 CColorConverter::convert8BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);
\r
340 image = new CImage(ECF_A1R5G5B5, dim);
\r
342 CColorConverter::convert16BitTo16Bit((s16*)bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);
\r
345 image = new CImage(ECF_R8G8B8, dim);
\r
347 CColorConverter::convert24BitTo24Bit(bmpData, (u8*)image->getData(), header.Width, header.Height, pitch, true, true);
\r
349 case 32: // thx to Reinhard Ostermeier
\r
350 image = new CImage(ECF_A8R8G8B8, dim);
\r
352 CColorConverter::convert32BitTo32Bit((s32*)bmpData, (s32*)image->getData(), header.Width, header.Height, pitch, true);
\r
358 delete [] paletteData;
\r
365 //! creates a loader which is able to load windows bitmaps
\r
366 IImageLoader* createImageLoaderBMP()
\r
368 return new CImageLoaderBMP;
\r
372 } // end namespace video
\r
373 } // end namespace irr
\r