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