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