1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2008 by Denton Woods
5 // Last modified: 02/09/2009
6 //
7 // Filename: src-IL/src/il_bmp.c
8 //
9 // Description: Reads from and writes to a bitmap (.bmp) file.
10 //
11 //-----------------------------------------------------------------------------
12 
13 #define IL_BMP_C
14 
15 #include "il_internal.h"
16 #ifndef IL_NO_BMP
17 #include "il_bmp.h"
18 #include "il_manip.h"
19 #include "il_endian.h"
20 #include <stdio.h>
21 void GetShiftFromMask(const ILuint Mask, ILuint * CONST_RESTRICT ShiftLeft, ILuint * CONST_RESTRICT ShiftRight);
22 
23 //! Checks if the file specified in FileName is a valid .bmp file.
ilIsValidBmp(ILconst_string CONST_RESTRICT FileName)24 ILboolean ilIsValidBmp(ILconst_string CONST_RESTRICT FileName)
25 {
26 	ILHANDLE	BitmapFile;
27 	ILboolean	bBitmap = IL_FALSE;
28 
29 	if (!iCheckExtension(FileName, IL_TEXT("bmp"))) {
30 		ilSetError(IL_INVALID_EXTENSION);
31 		return bBitmap;
32 	}
33 
34 	BitmapFile = iopenr(FileName);
35 	if (BitmapFile == NULL) {
36 		ilSetError(IL_COULD_NOT_OPEN_FILE);
37 		return bBitmap;
38 	}
39 
40 	bBitmap = ilIsValidBmpF(BitmapFile);
41 	icloser(BitmapFile);
42 
43 	return bBitmap;
44 }
45 
46 
47 //! Checks if the ILHANDLE contains a valid .bmp file at the current position.
ilIsValidBmpF(ILHANDLE File)48 ILboolean ilIsValidBmpF(ILHANDLE File)
49 {
50 	ILuint		FirstPos;
51 	ILboolean	bRet;
52 
53 	iSetInputFile(File);
54 	FirstPos = itell();
55 	bRet = iIsValidBmp();
56 	iseek(FirstPos, IL_SEEK_SET);
57 
58 	return bRet;
59 }
60 
61 
62 //! Checks if Lump is a valid .bmp lump.
ilIsValidBmpL(const void * Lump,ILuint Size)63 ILboolean ilIsValidBmpL(const void * Lump, ILuint Size)
64 {
65 	iSetInputLump(Lump, Size);
66 	return iIsValidBmp();
67 }
68 
69 // Internal function used to get the .bmp header from the current file.
iGetBmpHead(BMPHEAD * const Header)70 ILboolean iGetBmpHead(BMPHEAD * const Header)
71 {
72 	Header->bfType = GetLittleUShort();
73 	Header->bfSize = GetLittleInt();
74 	Header->bfReserved = GetLittleUInt();
75 	Header->bfDataOff = GetLittleInt();
76 	Header->biSize = GetLittleInt();
77 	Header->biWidth = GetLittleInt();
78 	Header->biHeight = GetLittleInt();
79 	Header->biPlanes = GetLittleShort();
80 	Header->biBitCount = GetLittleShort();
81 	Header->biCompression = GetLittleInt();
82 	Header->biSizeImage = GetLittleInt();
83 	Header->biXPelsPerMeter = GetLittleInt();
84 	Header->biYPelsPerMeter = GetLittleInt();
85 	Header->biClrUsed = GetLittleInt();
86 	Header->biClrImportant = GetLittleInt();
87 	return IL_TRUE;
88 }
89 
90 
iGetOS2Head(OS2_HEAD * const Header)91 ILboolean iGetOS2Head(OS2_HEAD * const Header)
92 {
93 	if (iread(Header, sizeof(OS2_HEAD), 1) != 1)
94 		return IL_FALSE;
95 
96 	UShort(&Header->bfType);
97 	UInt(&Header->biSize);
98 	Short(&Header->xHotspot);
99 	Short(&Header->yHotspot);
100 	UInt(&Header->DataOff);
101 	UInt(&Header->cbFix);
102 
103 	//2003-09-01 changed to UShort according to MSDN
104 	UShort(&Header->cx);
105 	UShort(&Header->cy);
106 	UShort(&Header->cPlanes);
107 	UShort(&Header->cBitCount);
108 
109 	iseek((ILint)Header->cbFix - 12, IL_SEEK_CUR);  // Skip rest of header, if any.
110 
111 	return IL_TRUE;
112 }
113 
114 
115 // Internal function to get the header and check it.
iIsValidBmp()116 ILboolean iIsValidBmp()
117 {
118 	BMPHEAD		Head;
119 	OS2_HEAD	Os2Head;
120 	ILboolean	IsValid;
121 
122 	iGetBmpHead(&Head);
123 	iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR);  // Go ahead and restore to previous state
124 
125 	IsValid = iCheckBmp(&Head);
126 	if (!IsValid) {
127 		iGetOS2Head(&Os2Head);
128 		iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR);
129 		IsValid = iCheckOS2(&Os2Head);
130 	}
131 	return IsValid;
132 }
133 
134 
135 // Internal function used to check if the HEADER is a valid .bmp header.
iCheckBmp(const BMPHEAD * CONST_RESTRICT Header)136 ILboolean iCheckBmp (const BMPHEAD * CONST_RESTRICT Header)
137 {
138 	//if ((Header->bfType != ('B'|('M'<<8))) || ((Header->biSize != 0x28) && (Header->biSize != 0x0C)))
139 	if ((Header->bfType != ('B'|('M'<<8))) || (Header->biSize != 0x28))
140 		return IL_FALSE;
141 	if (Header->biHeight == 0 || Header->biWidth < 1)
142 		return IL_FALSE;
143 	if (Header->biPlanes > 1)  // Not sure...
144 		return IL_FALSE;
145 	if(Header->biCompression != 0 && Header->biCompression != 1 &&
146 		Header->biCompression != 2 && Header->biCompression != 3)
147 		return IL_FALSE;
148 	if(Header->biCompression == 3 && Header->biBitCount != 16 && Header->biBitCount != 32)
149 		return IL_FALSE;
150 	if (Header->biBitCount != 1 && Header->biBitCount != 4 && Header->biBitCount != 8 &&
151 		Header->biBitCount != 16 && Header->biBitCount != 24 && Header->biBitCount != 32)
152 		return IL_FALSE;
153 	return IL_TRUE;
154 }
155 
156 
iCheckOS2(const OS2_HEAD * CONST_RESTRICT Header)157 ILboolean iCheckOS2 (const OS2_HEAD * CONST_RESTRICT Header)
158 {
159 	if ((Header->bfType != ('B'|('M'<<8))) || (Header->DataOff < 26) || (Header->cbFix < 12))
160 		return IL_FALSE;
161 	if (Header->cPlanes != 1)
162 		return IL_FALSE;
163 	if (Header->cx == 0 || Header->cy == 0)
164 		return IL_FALSE;
165 	if (Header->cBitCount != 1 && Header->cBitCount != 4 && Header->cBitCount != 8 &&
166 		 Header->cBitCount != 24)
167 		 return IL_FALSE;
168 
169 	return IL_TRUE;
170 }
171 
172 
173 //! Reads a .bmp file
ilLoadBmp(ILconst_string FileName)174 ILboolean ilLoadBmp(ILconst_string FileName) {
175 	ILHANDLE	BitmapFile;
176 	ILboolean	bBitmap = IL_FALSE;
177 
178 	BitmapFile = iopenr(FileName);
179 	if (BitmapFile == NULL) {
180 		ilSetError(IL_COULD_NOT_OPEN_FILE);
181 		return bBitmap;
182 	}
183 
184 	bBitmap = ilLoadBmpF(BitmapFile);
185 	icloser(BitmapFile);
186 
187 	return bBitmap;
188 }
189 
190 
191 //! Reads an already-opened .bmp file
ilLoadBmpF(ILHANDLE File)192 ILboolean ilLoadBmpF(ILHANDLE File)
193 {
194 	ILuint		FirstPos;
195 	ILboolean	bRet;
196 
197 	iSetInputFile(File);
198 	FirstPos = itell();
199 	bRet = iLoadBitmapInternal();
200 	iseek(FirstPos, IL_SEEK_SET);
201 
202 	return bRet;
203 }
204 
205 
206 //! Reads from a memory "lump" that contains a .bmp
ilLoadBmpL(const void * Lump,ILuint Size)207 ILboolean ilLoadBmpL(const void *Lump, ILuint Size)
208 {
209 	iSetInputLump(Lump, Size);
210 	return iLoadBitmapInternal();
211 }
212 
213 
214 // Internal function used to load the .bmp.
iLoadBitmapInternal()215 ILboolean iLoadBitmapInternal()
216 {
217 	BMPHEAD		Header;
218 	OS2_HEAD	Os2Head;
219 	ILboolean	bBitmap;
220 
221 	if (iCurImage == NULL) {
222 		ilSetError(IL_ILLEGAL_OPERATION);
223 		return IL_FALSE;
224 	}
225 
226 	iGetBmpHead(&Header);
227 	if (!iCheckBmp(&Header)) {
228 		iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR);
229 		iGetOS2Head(&Os2Head);
230 		if (!iCheckOS2(&Os2Head)) {
231 			ilSetError(IL_INVALID_FILE_HEADER);
232 			return IL_FALSE;
233 		}
234 		else {
235 			return iGetOS2Bmp(&Os2Head);
236 		}
237 	}
238 
239 	// Don't know what to do if it has more than one plane...
240 	if (Header.biPlanes != 1) {
241 		ilSetError(IL_INVALID_FILE_HEADER);
242 		return IL_FALSE;
243 	}
244 
245 	switch (Header.biCompression)
246 	{
247 		case 0:	// No compression
248 		case 3:	// BITFIELDS compression is handled in 16 bit
249 						// and 32 bit code in ilReadUncompBmp()
250 			bBitmap = ilReadUncompBmp(&Header);
251 			break;
252 		case 1:  //	RLE 8-bit / pixel (BI_RLE8)
253 			bBitmap = ilReadRLE8Bmp(&Header);
254 			break;
255 		case 2:  // RLE 4-bit / pixel (BI_RLE4)
256 			bBitmap = ilReadRLE4Bmp(&Header);
257 			break;
258 
259 		default:
260 			ilSetError(IL_INVALID_FILE_HEADER);
261 			return IL_FALSE;
262 	}
263 
264 	if (!ilFixImage())
265 		return IL_FALSE;
266 
267 	return bBitmap;
268 }
269 
270 
271 // Reads an uncompressed .bmp
272 //	One of the absolute ugliest functions I've ever written!
ilReadUncompBmp(BMPHEAD * Header)273 ILboolean ilReadUncompBmp(BMPHEAD * Header)
274 {
275 	ILuint i, j, k, c, Read;
276 	ILubyte Bpp, ByteData, PadSize, Padding[4];
277 	ILuint rMask, gMask, bMask; //required for bitfields packing
278 	ILuint rShiftR, gShiftR, bShiftR; //required for bitfields packing
279 	ILuint rShiftL, gShiftL, bShiftL; //required for bitfields packing
280 	ILushort Read16; //used for 16bit bmp loading
281 
282 	if (Header->biBitCount < 8)
283 		Bpp = 1;  // We can't have an integral number less than one and greater than 0
284 	else
285 		Bpp = (ILubyte)(Header->biBitCount >> 3);  // Convert to bytes per pixel
286 
287 	if(Bpp == 2 || Bpp == 4)
288 		Bpp = 3;
289 
290 	// Update the current image with the new dimensions
291 	if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, Bpp, 0, IL_UNSIGNED_BYTE, NULL)) {
292 		return IL_FALSE;
293 	}
294 	iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
295 
296 	switch (Header->biBitCount)
297 	{
298 		case 1:
299 			//iCurImage->Format = IL_LUMINANCE;
300 			iCurImage->Format = IL_COLOUR_INDEX;
301 			iCurImage->Pal.PalType = IL_PAL_BGR32;
302 			iCurImage->Pal.PalSize = 2 * 4;
303 			iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
304 			if (iCurImage->Pal.Palette == NULL) {
305 				return IL_FALSE;
306 			}
307 			break;
308 
309 		case 4:
310 		case 8:
311 			iCurImage->Format = IL_COLOUR_INDEX;
312 			iCurImage->Pal.PalType = IL_PAL_BGR32;
313 
314 			// if there are 256 colors biClrUsed is 0
315 			iCurImage->Pal.PalSize = Header->biClrUsed ?
316 					Header->biClrUsed * 4 : 256 * 4;
317 
318 			if (Header->biBitCount == 4)  // biClrUsed is 0 for 4-bit bitmaps
319 				iCurImage->Pal.PalSize = 16 * 4;
320 			iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
321 			if (iCurImage->Pal.Palette == NULL) {
322 				return IL_FALSE;
323 			}
324 			break;
325 
326 		case 16:
327 		case 24:
328 		case 32:
329 			iCurImage->Format = IL_BGR;
330 			break;
331 
332 		default:
333 			ilSetError(IL_ILLEGAL_FILE_VALUE);
334 			return IL_FALSE;
335 	}
336 
337 	// A height of 0 is illegal
338 	if (Header->biHeight == 0) {
339 		ilSetError(IL_ILLEGAL_FILE_VALUE);
340 		if (iCurImage->Pal.Palette)
341 			ifree(iCurImage->Pal.Palette);
342 		return IL_FALSE;
343 	}
344 
345 	// If the image height is negative, then the image is flipped
346 	//	(Normal is IL_ORIGIN_LOWER_LEFT)
347 	if (Header->biHeight < 0) {
348 		iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
349 	}
350 	else {
351 		iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
352 	}
353 
354 	// Read the palette
355 	iseek(sizeof(BMPHEAD), IL_SEEK_SET);
356 	if (iread(iCurImage->Pal.Palette, 1, iCurImage->Pal.PalSize) != iCurImage->Pal.PalSize)
357 		return IL_FALSE;
358 
359 	// Seek to the data from the "beginning" of the file
360 	iseek(Header->bfDataOff, IL_SEEK_SET);
361 
362 	// We have to handle 1 and 4-bit images separately, because we have to expand them.
363 	switch (Header->biBitCount)
364 	{
365 		case 1:
366 			//changed 2003-09-01
367 			if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
368 				iPreCache(iCurImage->Width / 8 * iCurImage->Height);
369 
370 			PadSize = ((32 - (iCurImage->Width % 32)) / 8) % 4;  // Has to truncate
371 			for (j = 0; j < iCurImage->Height; j++) {
372 				Read = 0;
373 				for (i = 0; i < iCurImage->Width; ) {
374 					if (iread(&ByteData, 1, 1) != 1) {
375 						iUnCache();
376 						return IL_FALSE;
377 					}
378 					Read++;
379 					k = 128;
380 					for (c = 0; c < 8; c++) {
381 						iCurImage->Data[j * iCurImage->Width + i] =
382 							(!!(ByteData & k) == 1 ? 1 : 0);
383 						k >>= 1;
384 						if (++i >= iCurImage->Width)
385 							break;
386 					}
387 				}
388 				//iseek(PadSize, IL_SEEK_CUR);
389 				iread(Padding, 1, PadSize);
390 			}
391 
392 			iUnCache();
393 			break;
394 
395 		case 4:
396 			//changed 2003-09-01
397 			if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
398 				iPreCache(iCurImage->Width / 2 * iCurImage->Height);
399 
400 			PadSize = ((8 - (iCurImage->Width % 8)) / 2) % 4;  // Has to truncate
401 			for (j = 0; j < iCurImage->Height; j++) {
402 				for (i = 0; i < iCurImage->Width; i++) {
403 					if (iread(&ByteData, 1, 1) != 1) {
404 						iUnCache();
405 						return IL_FALSE;
406 					}
407 					iCurImage->Data[j * iCurImage->Width + i] = ByteData >> 4;
408 					if (++i == iCurImage->Width)
409 						break;
410 					iCurImage->Data[j * iCurImage->Width + i] = ByteData & 0x0F;
411 				}
412 				iread(Padding, 1, PadSize);//iseek(PadSize, IL_SEEK_CUR);
413 			}
414 
415 			iUnCache();
416 			break;
417 
418 		case 16:
419 			//padding
420 			//2003-09-09: changed iCurImage->Bps to iCurImage->Width*2,
421 			//because iCurImage->Bps refers to the 24 bit devil image
422 			PadSize = (4 - (iCurImage->Width*2 % 4)) % 4;
423 
424 			//check if bitfield compression is used
425 			rMask = 0x7C00;
426 			gMask = 0x03E0;
427 			bMask = 0x001F;
428 			rShiftR = 10;
429 			gShiftR = 5;
430 			bShiftR = 0;
431 			rShiftL = 3;
432 			gShiftL = 3;
433 			bShiftL = 3;
434 			if (Header->biCompression == 3) //bitfields
435 			{
436 				iseek(Header->bfDataOff - 12, IL_SEEK_SET); //seek to bitfield data
437 				iread(&rMask, 4, 1);
438 				iread(&gMask, 4, 1);
439 				iread(&bMask, 4, 1);
440 				UInt(&rMask);
441 				UInt(&gMask);
442 				UInt(&bMask);
443 				GetShiftFromMask(rMask, &rShiftL, &rShiftR);
444 				GetShiftFromMask(gMask, &gShiftL, &gShiftR);
445 				GetShiftFromMask(bMask, &bShiftL, &bShiftR);
446 			}
447 
448 			k = 0;
449 
450 			//changed 2003-09-01
451 			if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
452 				iPreCache(iCurImage->Width * iCurImage->Height);
453 
454 			//@TODO: This may not be safe for Big Endian.
455 			for (j = 0; j < iCurImage->Height; j++) {
456 				for(i = 0; i < iCurImage->Width; i++, k += 3) {
457 					if (iread(&Read16, 2, 1) != 1) {
458 						iUnCache();
459 						return IL_FALSE;
460 					}
461 					iCurImage->Data[k] = ((Read16 & bMask) >> bShiftR) << bShiftL;
462 					iCurImage->Data[k + 1] = ((Read16 & gMask) >> gShiftR)  << gShiftL;
463 					iCurImage->Data[k + 2] = ((Read16 & rMask) >> rShiftR) << rShiftL;
464 				}
465 				iread(Padding, 1, PadSize);
466 			}
467 
468 			iUnCache();
469 			break;
470 
471 		case 8:
472 		case 24:
473             // For 8 and 24 bit, Bps is equal to the bmps bps
474 			PadSize = (4 - (iCurImage->Bps % 4)) % 4;
475 			if (PadSize == 0) {
476 				if (iread(iCurImage->Data, 1, iCurImage->SizeOfPlane) != iCurImage->SizeOfPlane)
477 					return IL_FALSE;
478 			}
479 			else {	// Microsoft requires lines to be padded if the widths aren't multiples of 4.
480 				//changed 2003-09-01
481 				if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
482 					iPreCache(iCurImage->Width * iCurImage->Height);
483 
484 				PadSize = (4 - (iCurImage->Bps % 4));
485 				for (i = 0; i < iCurImage->SizeOfPlane; i += iCurImage->Bps) {
486 					if (iread(iCurImage->Data + i, 1, iCurImage->Bps) != iCurImage->Bps) {
487 						iUnCache();
488 						return IL_FALSE;
489 					}
490 					//iseek(PadSize, IL_SEEK_CUR);
491 					iread(Padding, 1, PadSize);
492 				}
493 
494 				iUnCache();
495 			}
496 			break;
497 
498 		case 32:
499 			//32bit files are always padded to 4 byte...
500 			//check if bitfield compression is used
501 			rMask = 0xFF0000;
502 			gMask = 0x00FF00;
503 			bMask = 0x0000FF;
504 			rShiftR = 16;
505 			gShiftR = 8;
506 			bShiftR = 0;
507 			rShiftL = 0;
508 			gShiftL = 0;
509 			bShiftL = 0;
510 			if (Header->biCompression == 3) //bitfields
511 			{
512 				iseek(Header->bfDataOff - 12, IL_SEEK_SET); //seek to bitfield data
513 				iread(&rMask, 4, 1);
514 				iread(&gMask, 4, 1);
515 				iread(&bMask, 4, 1);
516 				UInt(&rMask);
517 				UInt(&gMask);
518 				UInt(&bMask);
519 				GetShiftFromMask(rMask, &rShiftL, &rShiftR);
520 				GetShiftFromMask(gMask, &gShiftL, &gShiftR);
521 				GetShiftFromMask(bMask, &bShiftL, &bShiftR);
522 			}
523 
524 			//TODO: win98 supports per-pixel alpha, so
525 			//load to rgba????
526 
527 			//changed 2003-09-01
528 			if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
529 				iPreCache(iCurImage->Width * iCurImage->Height);
530 
531 			for(i = 0; i < iCurImage->SizeOfData; i += 3) {
532 				if (iread(&Read, 4, 1) != 1) {
533 					iUnCache();
534 					return IL_FALSE;
535 				}
536 
537 				iCurImage->Data[i] = ((Read & bMask) >> bShiftR) << bShiftL;
538 				iCurImage->Data[i + 1] = ((Read & gMask) >> gShiftR) << gShiftL;
539 				iCurImage->Data[i + 2] = ((Read & rMask) >> rShiftR) << rShiftL;
540 			}
541 
542 			iUnCache();
543 			break;
544 
545 		default:
546 			return IL_FALSE; //shouldn't happen, we checked that before
547 	}
548 
549 	return IL_TRUE;
550 }
551 
552 
ilReadRLE8Bmp(BMPHEAD * Header)553 ILboolean ilReadRLE8Bmp(BMPHEAD *Header)
554 {
555 	ILubyte	Bytes[2];
556 	size_t	offset = 0, count, endOfLine = Header->biWidth;
557 
558 	// Update the current image with the new dimensions
559 	if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, 1, 0, IL_UNSIGNED_BYTE, NULL))
560 		return IL_FALSE;
561 
562 	iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
563 
564 	// A height of 0 is illegal
565 	if (Header->biHeight == 0)
566 		return IL_FALSE;
567 
568 	iCurImage->Format = IL_COLOUR_INDEX;
569 	iCurImage->Pal.PalType = IL_PAL_BGR32;
570 	iCurImage->Pal.PalSize = Header->biClrUsed * 4;  // 256 * 4 for most images
571 	if (iCurImage->Pal.PalSize == 0)
572 		iCurImage->Pal.PalSize = 256 * 4;
573 	iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
574 	if (iCurImage->Pal.Palette == NULL)
575 		return IL_FALSE;
576 
577 	// If the image height is negative, then the image is flipped
578 	//	(Normal is IL_ORIGIN_LOWER_LEFT)
579 	iCurImage->Origin = Header->biHeight < 0 ?
580 		 IL_ORIGIN_UPPER_LEFT : IL_ORIGIN_LOWER_LEFT;
581 
582 	// Read the palette
583 	iseek(sizeof(BMPHEAD), IL_SEEK_SET);
584 	if (iread(iCurImage->Pal.Palette, iCurImage->Pal.PalSize, 1) != 1)
585 		return IL_FALSE;
586 
587 	// Seek to the data from the "beginning" of the file
588 	iseek(Header->bfDataOff, IL_SEEK_SET);
589 
590     while (offset < iCurImage->SizeOfData) {
591 		if (iread(Bytes, sizeof(Bytes), 1) != 1)
592 			return IL_FALSE;
593 		if (Bytes[0] == 0x00) {  // Escape sequence
594 			switch (Bytes[1])
595 			{
596 				case 0x00:  // End of line
597 					offset = endOfLine;
598 					endOfLine += iCurImage->Width;
599 					break;
600 				case 0x01:  // End of bitmap
601 					offset = iCurImage->SizeOfData;
602 					break;
603 				case 0x2:
604 					if (iread(Bytes, sizeof(Bytes), 1) != 1)
605 						return IL_FALSE;
606 					offset += Bytes[0] + Bytes[1] * iCurImage->Width;
607 					endOfLine += Bytes[1] * iCurImage->Width;
608 					break;
609 				default:
610 					count = IL_MIN(Bytes[1], iCurImage->SizeOfData-offset);
611 					if (iread(iCurImage->Data + offset, (ILuint)count, 1) != 1)
612 						return IL_FALSE;
613 					offset += count;
614 					if ((count & 1) == 1)  // Must be on a word boundary
615 						if (iread(Bytes, 1, 1) != 1)
616 							return IL_FALSE;
617 					break;
618 			}
619 		} else {
620 			count = IL_MIN (Bytes[0], iCurImage->SizeOfData-offset);
621 			memset(iCurImage->Data + offset, Bytes[1], count);
622 			offset += count;
623 		}
624 	}
625 	return IL_TRUE;
626 }
627 
628 
629 //changed 2003-09-01
630 //deleted ilReadRLE8Bmp() USE_POINTER version
631 
ilReadRLE4Bmp(BMPHEAD * Header)632 ILboolean ilReadRLE4Bmp(BMPHEAD *Header)
633 {
634 	ILubyte	Bytes[2];
635 	ILuint	i;
636     size_t	offset = 0, count, endOfLine = Header->biWidth;
637 
638 	// Update the current image with the new dimensions
639 	if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, 1, 0, IL_UNSIGNED_BYTE, NULL))
640 		return IL_FALSE;
641 	iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
642 
643 	// A height of 0 is illegal
644 	if (Header->biHeight == 0) {
645 		ilSetError(IL_ILLEGAL_FILE_VALUE);
646 		return IL_FALSE;
647 	}
648 
649 	iCurImage->Format = IL_COLOUR_INDEX;
650 	iCurImage->Pal.PalType = IL_PAL_BGR32;
651 	iCurImage->Pal.PalSize = 16 * 4; //Header->biClrUsed * 4;  // 16 * 4 for most images
652 	iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
653 	if (iCurImage->Pal.Palette == NULL)
654 		return IL_FALSE;
655 
656 	// If the image height is negative, then the image is flipped
657 	//	(Normal is IL_ORIGIN_LOWER_LEFT)
658 	iCurImage->Origin = Header->biHeight < 0 ?
659 		 IL_ORIGIN_UPPER_LEFT : IL_ORIGIN_LOWER_LEFT;
660 
661 	// Read the palette
662 	iseek(sizeof(BMPHEAD), IL_SEEK_SET);
663 
664 	if (iread(iCurImage->Pal.Palette, iCurImage->Pal.PalSize, 1) != 1)
665 		return IL_FALSE;
666 
667 	// Seek to the data from the "beginning" of the file
668 	iseek(Header->bfDataOff, IL_SEEK_SET);
669 
670 	while (offset < iCurImage->SizeOfData) {
671       int align;
672 		if (iread(&Bytes[0], sizeof(Bytes), 1) != 1)
673 			return IL_FALSE;
674 		if (Bytes[0] == 0x0) {				// Escape sequence
675          switch (Bytes[1]) {
676          case 0x0:	// End of line
677             offset = endOfLine;
678             endOfLine += iCurImage->Width;
679             break;
680          case 0x1:	// End of bitmap
681             offset = iCurImage->SizeOfData;
682             break;
683          case 0x2:
684 				if (iread(&Bytes[0], sizeof(Bytes), 1) != 1)
685 					return IL_FALSE;
686             offset += Bytes[0] + Bytes[1] * iCurImage->Width;
687             endOfLine += Bytes[1] * iCurImage->Width;
688             break;
689          default:	  // Run of pixels
690             count = IL_MIN (Bytes[1], iCurImage->SizeOfData-offset);
691 
692 				for (i = 0; i < count; i++) {
693 					int byte;
694 
695 					if ((i & 0x01) == 0) {
696 						if (iread(&Bytes[0], sizeof(Bytes[0]), 1) != 1)
697 							return IL_FALSE;
698 						byte = (Bytes[0] >> 4);
699 					}
700 					else
701 						byte = (Bytes[0] & 0x0F);
702 					iCurImage->Data[offset++] = byte;
703 				}
704 
705 				align = Bytes[1] % 4;
706 
707 				if (align == 1 || align == 2)	// Must be on a word boundary
708 					if (iread(&Bytes[0], sizeof(Bytes[0]), 1) != 1)
709 						return IL_FALSE;
710 			}
711 		} else {
712          count = IL_MIN (Bytes[0], iCurImage->SizeOfData-offset);
713          Bytes[0] = (Bytes[1] >> 4);
714 			Bytes[1] &= 0x0F;
715 			for (i = 0; i < count; i++)
716 				iCurImage->Data[offset++] = Bytes [i & 1];
717 		}
718 	}
719 
720    return IL_TRUE;
721 }
722 
723 
724 //changed 2003-09-01
725 //deleted ilReadRLE4Bmp() USE_POINTER version
726 
iGetOS2Bmp(OS2_HEAD * Header)727 ILboolean iGetOS2Bmp(OS2_HEAD *Header)
728 {
729 	ILuint	PadSize, Read, i, j, k, c;
730 	ILubyte	ByteData;
731 
732 	if (Header->cBitCount == 1) {
733 		if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) {
734 			return IL_FALSE;
735 		}
736 		iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
737 
738 		iCurImage->Pal.Palette = (ILubyte*)ialloc(2 * 3);
739 		if (iCurImage->Pal.Palette == NULL) {
740 			return IL_FALSE;
741 		}
742 		iCurImage->Pal.PalSize = 2 * 3;
743 		iCurImage->Pal.PalType = IL_PAL_BGR24;
744 
745 		if (iread(iCurImage->Pal.Palette, 1, 2 * 3) != 6)
746 			return IL_FALSE;
747 
748 		PadSize = ((32 - (iCurImage->Width % 32)) / 8) % 4;  // Has to truncate.
749 		iseek(Header->DataOff, IL_SEEK_SET);
750 
751 		for (j = 0; j < iCurImage->Height; j++) {
752 			Read = 0;
753 			for (i = 0; i < iCurImage->Width; ) {
754 				if (iread(&ByteData, 1, 1) != 1)
755 					return IL_FALSE;
756 				Read++;
757 				k = 128;
758 				for (c = 0; c < 8; c++) {
759 					iCurImage->Data[j * iCurImage->Width + i] =
760 						(!!(ByteData & k) == 1 ? 1 : 0);
761 					k >>= 1;
762 					if (++i >= iCurImage->Width)
763 						break;
764 				}
765 			}
766 			iseek(PadSize, IL_SEEK_CUR);
767 		}
768 		return IL_TRUE;
769 	}
770 
771 	if (Header->cBitCount == 4) {
772 		if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) {
773 			return IL_FALSE;
774 		}
775 		iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
776 
777 		iCurImage->Pal.Palette = (ILubyte*)ialloc(16 * 3);
778 		if (iCurImage->Pal.Palette == NULL) {
779 			return IL_FALSE;
780 		}
781 		iCurImage->Pal.PalSize = 16 * 3;
782 		iCurImage->Pal.PalType = IL_PAL_BGR24;
783 
784 		if (iread(iCurImage->Pal.Palette, 1, 16 * 3) != 16*3)
785 			return IL_FALSE;
786 
787 		PadSize = ((8 - (iCurImage->Width % 8)) / 2) % 4;  // Has to truncate
788 		iseek(Header->DataOff, IL_SEEK_SET);
789 
790 		for (j = 0; j < iCurImage->Height; j++) {
791 			for (i = 0; i < iCurImage->Width; i++) {
792 				if (iread(&ByteData, 1, 1) != 1)
793 					return IL_FALSE;
794 				iCurImage->Data[j * iCurImage->Width + i] = ByteData >> 4;
795 				if (++i == iCurImage->Width)
796 					break;
797 				iCurImage->Data[j * iCurImage->Width + i] = ByteData & 0x0F;
798 			}
799 			iseek(PadSize, IL_SEEK_CUR);
800 		}
801 
802 		return IL_TRUE;
803 	}
804 
805 	if (Header->cBitCount == 8) {
806 		//added this line 2003-09-01...strange no-one noticed before...
807 		if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
808 			return IL_FALSE;
809 
810 		iCurImage->Pal.Palette = (ILubyte*)ialloc(256 * 3);
811 		if (iCurImage->Pal.Palette == NULL) {
812 			return IL_FALSE;
813 		}
814 		iCurImage->Pal.PalSize = 256 * 3;
815 		iCurImage->Pal.PalType = IL_PAL_BGR24;
816 
817 		if (iread(iCurImage->Pal.Palette, 1, 256 * 3) != 256*3)
818 			return IL_FALSE;
819 	}
820 	else { //has to be 24 bpp
821 		if (!ilTexImage(Header->cx, Header->cy, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, NULL))
822 			return IL_FALSE;
823 	}
824 	iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
825 
826 	iseek(Header->DataOff, IL_SEEK_SET);
827 
828 	PadSize = (4 - (iCurImage->Bps % 4)) % 4;
829 	if (PadSize == 0) {
830 		if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
831 			return IL_FALSE;
832 	}
833 	else {
834 		for (i = 0; i < iCurImage->Height; i++) {
835 			if (iread(iCurImage->Data + i * iCurImage->Bps, 1, iCurImage->Bps) != iCurImage->Bps)
836 				return IL_FALSE;
837 			iseek(PadSize, IL_SEEK_CUR);
838 		}
839 	}
840 
841 	return IL_TRUE;
842 }
843 
844 
845 //! Writes a Bmp file
ilSaveBmp(const ILstring FileName)846 ILboolean ilSaveBmp(const ILstring FileName)
847 {
848 	ILHANDLE	BitmapFile;
849 	ILuint		BitmapSize;
850 
851 	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
852 		if (iFileExists(FileName)) {
853 			ilSetError(IL_FILE_ALREADY_EXISTS);
854 			return IL_FALSE;
855 		}
856 	}
857 
858 	BitmapFile = iopenw(FileName);
859 	if (BitmapFile == NULL) {
860 		ilSetError(IL_COULD_NOT_OPEN_FILE);
861 		return IL_FALSE;
862 	}
863 
864 	BitmapSize = ilSaveBmpF(BitmapFile);
865 	iclosew(BitmapFile);
866 
867 	if (BitmapSize == 0)
868 		return IL_FALSE;
869 	return IL_TRUE;
870 }
871 
872 
873 //! Writes a Bmp to an already-opened file
ilSaveBmpF(ILHANDLE File)874 ILuint ilSaveBmpF(ILHANDLE File)
875 {
876 	ILuint Pos;
877 	iSetOutputFile(File);
878 	Pos = itellw();
879 	if (iSaveBitmapInternal() == IL_FALSE)
880 		return 0;  // Error occurred
881 	return itellw() - Pos;  // Return the number of bytes written.
882 }
883 
884 
885 //! Writes a Bmp to a memory "lump"
ilSaveBmpL(void * Lump,ILuint Size)886 ILuint ilSaveBmpL(void *Lump, ILuint Size)
887 {
888 	ILuint Pos;
889 	iSetOutputLump(Lump, Size);
890 	Pos = itellw();
891 	if (iSaveBitmapInternal() == IL_FALSE)
892 		return 0;  // Error occurred
893 	return itellw() - Pos;  // Return the number of bytes written.
894 }
895 
896 
897 // Internal function used to save the .bmp.
iSaveBitmapInternal()898 ILboolean iSaveBitmapInternal()
899 {
900 	//int compress_rle8 = ilGetInteger(IL_BMP_RLE);
901 	int compress_rle8 = IL_FALSE; // disabled BMP RLE compression. broken
902 	ILuint	FileSize, i, PadSize, Padding = 0;
903 	ILimage	*TempImage = NULL;
904 	ILpal	*TempPal;
905 	ILubyte	*TempData;
906 
907 	if (iCurImage == NULL) {
908 		ilSetError(IL_ILLEGAL_OPERATION);
909 		return IL_FALSE;
910 	}
911 
912 	iputc('B');  // Comprises the
913 	iputc('M');  //  "signature"
914 
915 	SaveLittleUInt(0);  // Will come back and change later in this function (filesize)
916 	SaveLittleUInt(0);  // Reserved
917 
918 	if (compress_rle8 == IL_TRUE)
919 	{
920 		TempImage = iConvertImage(iCurImage, IL_COLOR_INDEX, IL_UNSIGNED_BYTE);
921 		if (TempImage == NULL)
922 			return IL_FALSE;
923 		TempPal = iConvertPal(&TempImage->Pal, IL_PAL_BGR32);
924 		if (TempPal == NULL)
925 		{
926 			ilCloseImage(TempImage);
927 			return IL_FALSE;
928 		}
929 	}
930 
931 	if((iCurImage->Format == IL_LUMINANCE) && (iCurImage->Pal.Palette == NULL))
932 	{
933 		// For luminance images it is necessary to generate a grayscale BGR32
934 		//  color palette.  Could call iConvertImage(..., IL_COLOR_INDEX, ...)
935 		//  to generate an RGB24 palette, followed by iConvertPal(..., IL_PAL_BGR32),
936 		//  to convert the palette to BGR32, but it seemed faster to just
937 		//  explicitely generate the correct palette.
938 
939 		iCurImage->Pal.PalSize = 256*4;
940 		iCurImage->Pal.PalType = IL_PAL_BGR32;
941 		iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
942 
943 		// Generate grayscale palette
944 		for (i = 0; i < 256; i++)
945 		{
946 			iCurImage->Pal.Palette[i * 4] = i;
947 			iCurImage->Pal.Palette[i * 4 + 1] = i;
948 			iCurImage->Pal.Palette[i * 4 + 2] = i;
949 			iCurImage->Pal.Palette[i * 4 + 3] = 0;
950 		}
951 	}
952 
953 	// If the current image has a palette, take care of it
954 	TempPal = &iCurImage->Pal;
955 	if( iCurImage->Pal.PalSize && iCurImage->Pal.Palette && iCurImage->Pal.PalType != IL_PAL_NONE ) {
956 		// If the palette in .bmp format, write it directly
957 		if (iCurImage->Pal.PalType == IL_PAL_BGR32) {
958 			TempPal = &iCurImage->Pal;
959 		} else {
960 			TempPal = iConvertPal(&iCurImage->Pal, IL_PAL_BGR32);
961 			if (TempPal == NULL) {
962 				return IL_FALSE;
963 			}
964 		}
965 	}
966 
967 	SaveLittleUInt(54 + TempPal->PalSize);  // Offset of the data
968 
969 	//Changed 20040923: moved this block above writing of
970 	//BITMAPINFOHEADER, so that the written header refers to
971 	//TempImage instead of the original image
972 
973 	if ((iCurImage->Format != IL_BGR) && (iCurImage->Format != IL_BGRA) &&
974 			(iCurImage->Format != IL_COLOUR_INDEX) && (iCurImage->Format != IL_LUMINANCE)) {
975 		if (iCurImage->Format == IL_RGBA) {
976 			TempImage = iConvertImage(iCurImage, IL_BGRA, IL_UNSIGNED_BYTE);
977 		} else {
978 			TempImage = iConvertImage(iCurImage, IL_BGR, IL_UNSIGNED_BYTE);
979 		}
980 		if (TempImage == NULL)
981 			return IL_FALSE;
982 	} else if (iCurImage->Bpc > 1) {
983 		TempImage = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_BYTE);
984 		if (TempImage == NULL)
985 			return IL_FALSE;
986 	} else {
987 		TempImage = iCurImage;
988 	}
989 
990 	if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT) {
991 		TempData = iGetFlipped(TempImage);
992 		if (TempData == NULL) {
993 			ilCloseImage(TempImage);
994 			return IL_FALSE;
995 		}
996 	} else {
997 		TempData = TempImage->Data;
998 	}
999 
1000 	SaveLittleUInt(0x28);  // Header size
1001 	SaveLittleUInt(iCurImage->Width);
1002 
1003 	// Removed because some image viewers don't like the negative height.
1004 	// even if it is standard. @TODO should be enabled or disabled
1005 	// usually enabled.
1006 	/*if (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT)
1007 		SaveLittleInt(-(ILint)iCurImage->Height);
1008 	else*/
1009 		SaveLittleInt(TempImage->Height);
1010 
1011 	SaveLittleUShort(1);  // Number of planes
1012 	SaveLittleUShort((ILushort)((ILushort)TempImage->Bpp << 3));  // Bpp
1013 	if( compress_rle8 == IL_TRUE ) {
1014 		SaveLittleInt(1); // rle8 compression
1015 	} else {
1016 		SaveLittleInt(0);
1017 	}
1018 	SaveLittleInt(0);  // Size of image (Obsolete)
1019 	SaveLittleInt(0);  // (Obsolete)
1020 	SaveLittleInt(0);  // (Obsolete)
1021 
1022 	if (TempImage->Pal.PalType != IL_PAL_NONE) {
1023 		SaveLittleInt(ilGetInteger(IL_PALETTE_NUM_COLS));  // Num colours used
1024 	} else {
1025 		SaveLittleInt(0);
1026 	}
1027 	SaveLittleInt(0);  // Important colour (none)
1028 
1029 	iwrite(TempPal->Palette, 1, TempPal->PalSize);
1030 
1031 
1032 	if( compress_rle8 == IL_TRUE ) {
1033 		//@TODO compress and save
1034 		ILubyte *Dest = (ILubyte*)ialloc((long)((double)TempImage->SizeOfPlane*130/127));
1035 		FileSize = ilRleCompress(TempImage->Data,TempImage->Width,TempImage->Height,
1036 						TempImage->Depth,TempImage->Bpp,Dest,IL_BMPCOMP,NULL);
1037 		iwrite(Dest,1,FileSize);
1038 	} else {
1039 		PadSize = (4 - (TempImage->Bps % 4)) % 4;
1040 		// No padding, so write data directly.
1041 		if (PadSize == 0) {
1042 			iwrite(TempData, 1, TempImage->SizeOfPlane);
1043 		} else {  // Odd width, so we must pad each line.
1044 			for (i = 0; i < TempImage->SizeOfPlane; i += TempImage->Bps) {
1045 				iwrite(TempData + i, 1, TempImage->Bps); // Write data
1046 				iwrite(&Padding, 1, PadSize); // Write pad byte(s)
1047 			}
1048 		}
1049 	}
1050 
1051 	// Write the filesize
1052 	FileSize = itellw();
1053 	iseekw(2, IL_SEEK_SET);
1054 	SaveLittleUInt(FileSize);
1055 
1056 	if (TempPal != &iCurImage->Pal) {
1057 		ifree(TempPal->Palette);
1058 		ifree(TempPal);
1059 	}
1060 	if (TempData != TempImage->Data)
1061 		ifree(TempData);
1062 	if (TempImage != iCurImage)
1063 		ilCloseImage(TempImage);
1064 
1065 	iseekw(FileSize, IL_SEEK_SET);
1066 
1067 	return IL_TRUE;
1068 }
1069 
1070 
1071 #endif//IL_NO_BMP
1072