]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CImageLoaderBMP.cpp
Use swap_control from MESA and EXT before SGI
[irrlicht.git] / source / Irrlicht / CImageLoaderBMP.cpp
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
4 \r
5 #include "CImageLoaderBMP.h"\r
6 \r
7 #ifdef _IRR_COMPILE_WITH_BMP_LOADER_\r
8 \r
9 #include "IReadFile.h"\r
10 #include "SColor.h"\r
11 #include "CColorConverter.h"\r
12 #include "CImage.h"\r
13 #include "os.h"\r
14 #include "irrString.h"\r
15 \r
16 namespace irr\r
17 {\r
18 namespace video\r
19 {\r
20 \r
21 \r
22 //! constructor\r
23 CImageLoaderBMP::CImageLoaderBMP()\r
24 {\r
25         #ifdef _DEBUG\r
26         setDebugName("CImageLoaderBMP");\r
27         #endif\r
28 }\r
29 \r
30 \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
34 {\r
35         return core::hasFileExtension ( filename, "bmp" );\r
36 }\r
37 \r
38 \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
41 {\r
42         u16 headerID;\r
43         file->read(&headerID, sizeof(u16));\r
44 #ifdef __BIG_ENDIAN__\r
45         headerID = os::Byteswap::byteswap(headerID);\r
46 #endif\r
47         return headerID == 0x4d42;\r
48 }\r
49 \r
50 \r
51 void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const\r
52 {\r
53         u8* p = bmpData;\r
54         u8* newBmp = new u8[(width+pitch)*height];\r
55         u8* d = newBmp;\r
56         u8* destEnd = newBmp + (width+pitch)*height;\r
57         s32 line = 0;\r
58 \r
59         while (bmpData - p < size && d < destEnd)\r
60         {\r
61                 if (*p == 0)\r
62                 {\r
63                         ++p;\r
64 \r
65                         switch(*p)\r
66                         {\r
67                         case 0: // end of line\r
68                                 ++p;\r
69                                 ++line;\r
70                                 d = newBmp + (line*(width+pitch));\r
71                                 break;\r
72                         case 1: // end of bmp\r
73                                 delete [] bmpData;\r
74                                 bmpData = newBmp;\r
75                                 return;\r
76                         case 2:\r
77                                 ++p; d +=(u8)*p;  // delta\r
78                                 ++p; d += ((u8)*p)*(width+pitch);\r
79                                 ++p;\r
80                                 break;\r
81                         default:\r
82                                 {\r
83                                         // absolute mode\r
84                                         s32 count = (u8)*p; ++p;\r
85                                         s32 readAdditional = ((2-(count%2))%2);\r
86                                         s32 i;\r
87 \r
88                                         for (i=0; i<count; ++i)\r
89                                         {\r
90                                                 *d = *p;\r
91                                                 ++p;\r
92                                                 ++d;\r
93                                         }\r
94 \r
95                                         for (i=0; i<readAdditional; ++i)\r
96                                                 ++p;\r
97                                 }\r
98                         }\r
99                 }\r
100                 else\r
101                 {\r
102                         s32 count = (u8)*p; ++p;\r
103                         u8 color = *p; ++p;\r
104                         for (s32 i=0; i<count; ++i)\r
105                         {\r
106                                 *d = color;\r
107                                 ++d;\r
108                         }\r
109                 }\r
110         }\r
111 \r
112         delete [] bmpData;\r
113         bmpData = newBmp;\r
114 }\r
115 \r
116 \r
117 void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const\r
118 {\r
119         s32 lineWidth = (width+1)/2+pitch;\r
120         u8* p = bmpData;\r
121         u8* newBmp = new u8[lineWidth*height];\r
122         u8* d = newBmp;\r
123         u8* destEnd = newBmp + lineWidth*height;\r
124         s32 line = 0;\r
125         s32 shift = 4;\r
126 \r
127         while (bmpData - p < size && d < destEnd)\r
128         {\r
129                 if (*p == 0)\r
130                 {\r
131                         ++p;\r
132 \r
133                         switch(*p)\r
134                         {\r
135                         case 0: // end of line\r
136                                 ++p;\r
137                                 ++line;\r
138                                 d = newBmp + (line*lineWidth);\r
139                                 shift = 4;\r
140                                 break;\r
141                         case 1: // end of bmp\r
142                                 delete [] bmpData;\r
143                                 bmpData = newBmp;\r
144                                 return;\r
145                         case 2:\r
146                                 {\r
147                                         ++p;\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
152                                 }\r
153                                 break;\r
154                         default:\r
155                                 {\r
156                                         // absolute mode\r
157                                         s32 count = (u8)*p; ++p;\r
158                                         s32 readAdditional = ((2-((count)%2))%2);\r
159                                         s32 readShift = 4;\r
160                                         s32 i;\r
161 \r
162                                         for (i=0; i<count; ++i)\r
163                                         {\r
164                                                 s32 color = (((u8)*p) >> readShift) & 0x0f;\r
165                                                 readShift -= 4;\r
166                                                 if (readShift < 0)\r
167                                                 {\r
168                                                         ++*p;\r
169                                                         readShift = 4;\r
170                                                 }\r
171 \r
172                                                 u8 mask = 0x0f << shift;\r
173                                                 *d = (*d & (~mask)) | ((color << shift) & mask);\r
174 \r
175                                                 shift -= 4;\r
176                                                 if (shift < 0)\r
177                                                 {\r
178                                                         shift = 4;\r
179                                                         ++d;\r
180                                                 }\r
181 \r
182                                         }\r
183 \r
184                                         for (i=0; i<readAdditional; ++i)\r
185                                                 ++p;\r
186                                 }\r
187                         }\r
188                 }\r
189                 else\r
190                 {\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
194                         ++p;\r
195 \r
196                         for (s32 i=0; i<count; ++i)\r
197                         {\r
198                                 u8 mask = 0x0f << shift;\r
199                                 u8 toSet = (shift==0 ? color1 : color2) << shift;\r
200                                 *d = (*d & (~mask)) | (toSet & mask);\r
201 \r
202                                 shift -= 4;\r
203                                 if (shift < 0)\r
204                                 {\r
205                                         shift = 4;\r
206                                         ++d;\r
207                                 }\r
208                         }\r
209                 }\r
210         }\r
211 \r
212         delete [] bmpData;\r
213         bmpData = newBmp;\r
214 }\r
215 \r
216 \r
217 \r
218 //! creates a surface from the file\r
219 IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const\r
220 {\r
221         SBMPHeader header;\r
222 \r
223         file->read(&header, sizeof(header));\r
224 \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
240 #endif\r
241 \r
242         s32 pitch = 0;\r
243 \r
244         //! return if the header is false\r
245 \r
246         if (header.Id != 0x4d42)\r
247                 return 0;\r
248 \r
249         if (header.Compression > 2) // we'll only handle RLE-Compression\r
250         {\r
251                 os::Printer::log("Compression mode not supported.", ELL_ERROR);\r
252                 return 0;\r
253         }\r
254 \r
255         if (header.BPP > 32 || !checkImageDimensions(header.Width, header.Height))\r
256         {\r
257                 os::Printer::log("Rejecting BMP with unreasonable size or BPP.", ELL_ERROR);\r
258                 return 0;\r
259         }\r
260 \r
261         // adjust bitmap data size to dword boundary\r
262         header.BitmapDataSize += (4-(header.BitmapDataSize%4))%4;\r
263 \r
264         // read palette\r
265 \r
266         long pos = file->getPos();\r
267         s32 paletteSize = (header.BitmapDataOffset - pos) / 4;\r
268 \r
269         s32* paletteData = 0;\r
270         if (paletteSize)\r
271         {\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
277 #endif\r
278         }\r
279 \r
280         // read image data\r
281 \r
282         if (!header.BitmapDataSize)\r
283         {\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
287         }\r
288 \r
289         file->seek(header.BitmapDataOffset);\r
290 \r
291         f32 t = (header.Width) * (header.BPP / 8.0f);\r
292         s32 widthInBytes = (s32)t;\r
293         t -= widthInBytes;\r
294         if (t!=0.0f)\r
295                 ++widthInBytes;\r
296 \r
297         s32 lineData = widthInBytes + ((4-(widthInBytes%4)))%4;\r
298         pitch = lineData - widthInBytes;\r
299 \r
300         u8* bmpData = new u8[header.BitmapDataSize];\r
301         file->read(bmpData, header.BitmapDataSize);\r
302 \r
303         // decompress data if needed\r
304         switch(header.Compression)\r
305         {\r
306         case 1: // 8 bit rle\r
307                 decompress8BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);\r
308                 break;\r
309         case 2: // 4 bit rle\r
310                 decompress4BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);\r
311                 break;\r
312         }\r
313 \r
314         // create surface\r
315 \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
320 \r
321         IImage* image = 0;\r
322         switch(header.BPP)\r
323         {\r
324         case 1:\r
325                 image = new CImage(ECF_A1R5G5B5, dim);\r
326                 if (image)\r
327                         CColorConverter::convert1BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);\r
328                 break;\r
329         case 4:\r
330                 image = new CImage(ECF_A1R5G5B5, dim);\r
331                 if (image)\r
332                         CColorConverter::convert4BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);\r
333                 break;\r
334         case 8:\r
335                 image = new CImage(ECF_A1R5G5B5, dim);\r
336                 if (image)\r
337                         CColorConverter::convert8BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true);\r
338                 break;\r
339         case 16:\r
340                 image = new CImage(ECF_A1R5G5B5, dim);\r
341                 if (image)\r
342                         CColorConverter::convert16BitTo16Bit((s16*)bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true);\r
343                 break;\r
344         case 24:\r
345                 image = new CImage(ECF_R8G8B8, dim);\r
346                 if (image)\r
347                         CColorConverter::convert24BitTo24Bit(bmpData, (u8*)image->getData(), header.Width, header.Height, pitch, true, true);\r
348                 break;\r
349         case 32: // thx to Reinhard Ostermeier\r
350                 image = new CImage(ECF_A8R8G8B8, dim);\r
351                 if (image)\r
352                         CColorConverter::convert32BitTo32Bit((s32*)bmpData, (s32*)image->getData(), header.Width, header.Height, pitch, true);\r
353                 break;\r
354         };\r
355 \r
356         // clean up\r
357 \r
358         delete [] paletteData;\r
359         delete [] bmpData;\r
360 \r
361         return image;\r
362 }\r
363 \r
364 \r
365 //! creates a loader which is able to load windows bitmaps\r
366 IImageLoader* createImageLoaderBMP()\r
367 {\r
368         return new CImageLoaderBMP;\r
369 }\r
370 \r
371 \r
372 } // end namespace video\r
373 } // end namespace irr\r
374 \r
375 #endif\r
376 \r