- Main Page
- Data Structures
- Files

- File List

# E:/DevIL/src-IL/src/il_bmp.c

    00001 //-----------------------------------------------------------------------------
    00002 //
    00003 // ImageLib Sources
    00004 // Copyright (C) 2000-2008 by Denton Woods
    00005 // Last modified: 01/06/2009
    00006 //
    00007 // Filename: src-IL/src/il_bmp.c
    00008 //
    00009 // Description: Reads from and writes to a bitmap (.bmp) file.
    00010 //
    00011 //-----------------------------------------------------------------------------
    00012
    00013 #define IL_BMP_C
    00014
    00015 #include "il_internal.h"
    00016 #ifndef IL_NO_BMP
    00017 #include "il_bmp.h"
    00018 #include "il_manip.h"
    00019 #include "il_endian.h"
    00020 #include <stdio.h>
    00021 void GetShiftFromMask(const ILuint Mask, ILuint * CONST_RESTRICT ShiftLeft, ILuint * CONST_RESTRICT ShiftRight);
    00022
    00024 ILboolean ilIsValidBmp(ILconst_string CONST_RESTRICT FileName)
    00025 {
    00026         ILHANDLE        BitmapFile;
    00027         ILboolean       bBitmap = IL_FALSE;
    00028
    00029         if (!iCheckExtension(FileName, IL_TEXT("bmp"))) {
    00030                 ilSetError(IL_INVALID_EXTENSION);
    00031                 return bBitmap;
    00032         }
    00033
    00034         BitmapFile = iopenr(FileName);
    00035         if (BitmapFile == NULL) {
    00036                 ilSetError(IL_COULD_NOT_OPEN_FILE);
    00037                 return bBitmap;
    00038         }
    00039
    00040         bBitmap = ilIsValidBmpF(BitmapFile);
    00041         icloser(BitmapFile);
    00042
    00043         return bBitmap;
    00044 }
    00045
    00046
    00048 ILboolean ilIsValidBmpF(ILHANDLE File)
    00049 {
    00050         ILuint          FirstPos;
    00051         ILboolean       bRet;
    00052
    00053         iSetInputFile(File);
    00054         FirstPos = itell();
    00055         bRet = iIsValidBmp();
    00056         iseek(FirstPos, IL_SEEK_SET);
    00057
    00058         return bRet;
    00059 }
    00060
    00061
    00063 ILboolean ilIsValidBmpL(const void * Lump, const ILuint Size)
    00064 {
    00065         iSetInputLump(Lump, Size);
    00066         return iIsValidBmp();
    00067 }
    00068
    00069 // Internal function used to get the .bmp header from the current file.
    00070 ILboolean iGetBmpHead(BMPHEAD * const Header)
    00071 {
    00072         Header->bfType = GetLittleUShort();
    00073         Header->bfSize = GetLittleInt();
    00074         Header->bfReserved = GetLittleUInt();
    00075         Header->bfDataOff = GetLittleInt();
    00076         Header->biSize = GetLittleInt();
    00077         Header->biWidth = GetLittleInt();
    00078         Header->biHeight = GetLittleInt();
    00079         Header->biPlanes = GetLittleShort();
    00080         Header->biBitCount = GetLittleShort();
    00081         Header->biCompression = GetLittleInt();
    00082         Header->biSizeImage = GetLittleInt();
    00083         Header->biXPelsPerMeter = GetLittleInt();
    00084         Header->biYPelsPerMeter = GetLittleInt();
    00085         Header->biClrUsed = GetLittleInt();
    00086         Header->biClrImportant = GetLittleInt();
    00087         return IL_TRUE;
    00088 }
    00089
    00090
    00091 ILboolean iGetOS2Head(OS2_HEAD * const Header)
    00092 {
    00093         if (iread(Header, sizeof(OS2_HEAD), 1) != 1)
    00094                 return IL_FALSE;
    00095
    00096         UShort(&Header->bfType);
    00097         UInt(&Header->biSize);
    00098         Short(&Header->xHotspot);
    00099         Short(&Header->yHotspot);
    00100         UInt(&Header->DataOff);
    00101         UInt(&Header->cbFix);
    00102
    00103         //2003-09-01 changed to UShort according to MSDN
    00104         UShort(&Header->cx);
    00105         UShort(&Header->cy);
    00106         UShort(&Header->cPlanes);
    00107         UShort(&Header->cBitCount);
    00108
    00109         iseek((ILint)Header->cbFix - 12, IL_SEEK_CUR);  // Skip rest of header, if any.
    00110
    00111         return IL_TRUE;
    00112 }
    00113
    00114
    00115 // Internal function to get the header and check it.
    00116 ILboolean iIsValidBmp()
    00117 {
    00118         BMPHEAD         Head;
    00119         OS2_HEAD        Os2Head;
    00120         ILboolean       IsValid;
    00121
    00122         iGetBmpHead(&Head);
    00123         iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR);  // Go ahead and restore to previous state
    00124
    00125         IsValid = iCheckBmp(&Head);
    00126         if (!IsValid) {
    00127                 iGetOS2Head(&Os2Head);
    00128                 iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR);
    00129                 IsValid = iCheckOS2(&Os2Head);
    00130         }
    00131         return IsValid;
    00132 }
    00133
    00134
    00135 // Internal function used to check if the HEADER is a valid .bmp header.
    00136 ILboolean iCheckBmp (const BMPHEAD * CONST_RESTRICT Header)
    00137 {
    00138         //if ((Header->bfType != ('B'|('M'<<8))) || ((Header->biSize != 0x28) && (Header->biSize != 0x0C)))
    00139         if ((Header->bfType != ('B'|('M'<<8))) || (Header->biSize != 0x28))
    00140                 return IL_FALSE;
    00141         if (Header->biHeight == 0 || Header->biWidth < 1)
    00142                 return IL_FALSE;
    00143         if (Header->biPlanes > 1)  // Not sure...
    00144                 return IL_FALSE;
    00145         if(Header->biCompression != 0 && Header->biCompression != 1 &&
    00146                 Header->biCompression != 2 && Header->biCompression != 3)
    00147                 return IL_FALSE;
    00148         if(Header->biCompression == 3 && Header->biBitCount != 16 && Header->biBitCount != 32)
    00149                 return IL_FALSE;
    00150         if (Header->biBitCount != 1 && Header->biBitCount != 4 && Header->biBitCount != 8 &&
    00151                 Header->biBitCount != 16 && Header->biBitCount != 24 && Header->biBitCount != 32)
    00152                 return IL_FALSE;
    00153         return IL_TRUE;
    00154 }
    00155
    00156
    00157 ILboolean iCheckOS2 (const OS2_HEAD * CONST_RESTRICT Header)
    00158 {
    00159         if ((Header->bfType != ('B'|('M'<<8))) || (Header->DataOff < 26) || (Header->cbFix < 12))
    00160                 return IL_FALSE;
    00161         if (Header->cPlanes != 1)
    00162                 return IL_FALSE;
    00163         if (Header->cx == 0 || Header->cy == 0)
    00164                 return IL_FALSE;
    00165         if (Header->cBitCount != 1 && Header->cBitCount != 4 && Header->cBitCount != 8 &&
    00166                  Header->cBitCount != 24)
    00167                  return IL_FALSE;
    00168
    00169         return IL_TRUE;
    00170 }
    00171
    00172
    00174 ILboolean ilLoadBmp(ILconst_string FileName) {
    00175         ILHANDLE        BitmapFile;
    00176         ILboolean       bBitmap = IL_FALSE;
    00177
    00178         BitmapFile = iopenr(FileName);
    00179         if (BitmapFile == NULL) {
    00180                 ilSetError(IL_COULD_NOT_OPEN_FILE);
    00181                 return bBitmap;
    00182         }
    00183
    00184         bBitmap = ilLoadBmpF(BitmapFile);
    00185         icloser(BitmapFile);
    00186
    00187         return bBitmap;
    00188 }
    00189
    00190
    00192 ILboolean ilLoadBmpF(ILHANDLE File)
    00193 {
    00194         ILuint          FirstPos;
    00195         ILboolean       bRet;
    00196
    00197         iSetInputFile(File);
    00198         FirstPos = itell();
    00199         bRet = iLoadBitmapInternal();
    00200         iseek(FirstPos, IL_SEEK_SET);
    00201
    00202         return bRet;
    00203 }
    00204
    00205
    00207 ILboolean ilLoadBmpL(const void *Lump, const ILuint Size)
    00208 {
    00209         iSetInputLump(Lump, Size);
    00210         return iLoadBitmapInternal();
    00211 }
    00212
    00213
    00214 // Internal function used to load the .bmp.
    00215 ILboolean iLoadBitmapInternal()
    00216 {
    00217         BMPHEAD         Header;
    00218         OS2_HEAD        Os2Head;
    00219         ILboolean       bBitmap;
    00220
    00221         if (iCurImage == NULL) {
    00222                 ilSetError(IL_ILLEGAL_OPERATION);
    00223                 return IL_FALSE;
    00224         }
    00225
    00226         iGetBmpHead(&Header);
    00227         if (!iCheckBmp(&Header)) {
    00228                 iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR);
    00229                 iGetOS2Head(&Os2Head);
    00230                 if (!iCheckOS2(&Os2Head)) {
    00231                         ilSetError(IL_INVALID_FILE_HEADER);
    00232                         return IL_FALSE;
    00233                 }
    00234                 else {
    00235                         return iGetOS2Bmp(&Os2Head);
    00236                 }
    00237         }
    00238
    00239         // Don't know what to do if it has more than one plane...
    00240         if (Header.biPlanes != 1) {
    00241                 ilSetError(IL_INVALID_FILE_HEADER);
    00242                 return IL_FALSE;
    00243         }
    00244
    00245         switch (Header.biCompression)
    00246         {
    00247                 case 0: // No compression
    00248                 case 3: // BITFIELDS compression is handled in 16 bit
    00249                                                 // and 32 bit code in ilReadUncompBmp()
    00250                         bBitmap = ilReadUncompBmp(&Header);
    00251                         break;
    00252                 case 1:  //     RLE 8-bit / pixel (BI_RLE8)
    00253                         bBitmap = ilReadRLE8Bmp(&Header);
    00254                         break;
    00255                 case 2:  // RLE 4-bit / pixel (BI_RLE4)
    00256                         bBitmap = ilReadRLE4Bmp(&Header);
    00257                         break;
    00258
    00259                 default:
    00260                         ilSetError(IL_INVALID_FILE_HEADER);
    00261                         return IL_FALSE;
    00262         }
    00263
    00264         if (!ilFixImage())
    00265                 return IL_FALSE;
    00266
    00267         return bBitmap;
    00268 }
    00269
    00270
    00271 // Reads an uncompressed .bmp
    00272 //      One of the absolute ugliest functions I've ever written!
    00273 ILboolean ilReadUncompBmp(BMPHEAD * Header)
    00274 {
    00275         ILuint i, j, k, c, Read;
    00276         ILubyte Bpp, ByteData, PadSize, Padding[4];
    00277         ILuint rMask, gMask, bMask; //required for bitfields packing
    00278         ILuint rShiftR, gShiftR, bShiftR; //required for bitfields packing
    00279         ILuint rShiftL, gShiftL, bShiftL; //required for bitfields packing
    00280         ILushort Read16; //used for 16bit bmp loading
    00281
    00282         if (Header->biBitCount < 8)
    00283                 Bpp = 1;  // We can't have an integral number less than one and greater than 0
    00284         else
    00285                 Bpp = (ILubyte)(Header->biBitCount >> 3);  // Convert to bytes per pixel
    00286
    00287         if(Bpp == 2 || Bpp == 4)
    00288                 Bpp = 3;
    00289
    00290         // Update the current image with the new dimensions
    00291         if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, Bpp, 0, IL_UNSIGNED_BYTE, NULL)) {
    00292                 return IL_FALSE;
    00293         }
    00294         iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00295
    00296         switch (Header->biBitCount)
    00297         {
    00298                 case 1:
    00299                         //iCurImage->Format = IL_LUMINANCE;
    00300                         iCurImage->Format = IL_COLOUR_INDEX;
    00301                         iCurImage->Pal.PalType = IL_PAL_BGR32;
    00302                         iCurImage->Pal.PalSize = 2 * 4;
    00303                         iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
    00304                         if (iCurImage->Pal.Palette == NULL) {
    00305                                 return IL_FALSE;
    00306                         }
    00307                         break;
    00308
    00309                 case 4:
    00310                 case 8:
    00311                         iCurImage->Format = IL_COLOUR_INDEX;
    00312                         iCurImage->Pal.PalType = IL_PAL_BGR32;
    00313
    00314                         // if there are 256 colors biClrUsed is 0
    00315                         iCurImage->Pal.PalSize = Header->biClrUsed ?
    00316                                         Header->biClrUsed * 4 : 256 * 4;
    00317
    00318                         if (Header->biBitCount == 4)  // biClrUsed is 0 for 4-bit bitmaps
    00319                                 iCurImage->Pal.PalSize = 16 * 4;
    00320                         iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
    00321                         if (iCurImage->Pal.Palette == NULL) {
    00322                                 return IL_FALSE;
    00323                         }
    00324                         break;
    00325
    00326                 case 16:
    00327                 case 24:
    00328                 case 32:
    00329                         iCurImage->Format = IL_BGR;
    00330                         break;
    00331
    00332                 default:
    00333                         ilSetError(IL_ILLEGAL_FILE_VALUE);
    00334                         return IL_FALSE;
    00335         }
    00336
    00337         // A height of 0 is illegal
    00338         if (Header->biHeight == 0) {
    00339                 ilSetError(IL_ILLEGAL_FILE_VALUE);
    00340                 if (iCurImage->Pal.Palette)
    00341                         ifree(iCurImage->Pal.Palette);
    00342                 return IL_FALSE;
    00343         }
    00344
    00345         // If the image height is negative, then the image is flipped
    00346         //      (Normal is IL_ORIGIN_LOWER_LEFT)
    00347         if (Header->biHeight < 0) {
    00348                 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
    00349         }
    00350         else {
    00351                 iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00352         }
    00353
    00354         // Read the palette
    00355         iseek(sizeof(BMPHEAD), IL_SEEK_SET);
    00356         if (iread(iCurImage->Pal.Palette, 1, iCurImage->Pal.PalSize) != iCurImage->Pal.PalSize)
    00357                 return IL_FALSE;
    00358
    00359         // Seek to the data from the "beginning" of the file
    00360         iseek(Header->bfDataOff, IL_SEEK_SET);
    00361
    00362         // We have to handle 1 and 4-bit images separately, because we have to expand them.
    00363         switch (Header->biBitCount)
    00364         {
    00365                 case 1:
    00366                         //changed 2003-09-01
    00367                         if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
    00368                                 iPreCache(iCurImage->Width / 8 * iCurImage->Height);
    00369
    00370                         PadSize = ((32 - (iCurImage->Width % 32)) / 8) % 4;  // Has to truncate
    00371                         for (j = 0; j < iCurImage->Height; j++) {
    00372                                 Read = 0;
    00373                                 for (i = 0; i < iCurImage->Width; ) {
    00374                                         if (iread(&ByteData, 1, 1) != 1) {
    00375                                                 iUnCache();
    00376                                                 return IL_FALSE;
    00377                                         }
    00378                                         Read++;
    00379                                         k = 128;
    00380                                         for (c = 0; c < 8; c++) {
    00381                                                 iCurImage->Data[j * iCurImage->Width + i] =
    00382                                                         (!!(ByteData & k) == 1 ? 1 : 0);
    00383                                                 k >>= 1;
    00384                                                 if (++i >= iCurImage->Width)
    00385                                                         break;
    00386                                         }
    00387                                 }
    00388                                 //iseek(PadSize, IL_SEEK_CUR);
    00389                                 iread(Padding, 1, PadSize);
    00390                         }
    00391
    00392                         iUnCache();
    00393                         break;
    00394
    00395                 case 4:
    00396                         //changed 2003-09-01
    00397                         if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
    00398                                 iPreCache(iCurImage->Width / 2 * iCurImage->Height);
    00399
    00400                         PadSize = ((8 - (iCurImage->Width % 8)) / 2) % 4;  // Has to truncate
    00401                         for (j = 0; j < iCurImage->Height; j++) {
    00402                                 for (i = 0; i < iCurImage->Width; i++) {
    00403                                         if (iread(&ByteData, 1, 1) != 1) {
    00404                                                 iUnCache();
    00405                                                 return IL_FALSE;
    00406                                         }
    00407                                         iCurImage->Data[j * iCurImage->Width + i] = ByteData >> 4;
    00408                                         if (++i == iCurImage->Width)
    00409                                                 break;
    00410                                         iCurImage->Data[j * iCurImage->Width + i] = ByteData & 0x0F;
    00411                                 }
    00412                                 iread(Padding, 1, PadSize);//iseek(PadSize, IL_SEEK_CUR);
    00413                         }
    00414
    00415                         iUnCache();
    00416                         break;
    00417
    00418                 case 16:
    00419                         //padding
    00420                         //2003-09-09: changed iCurImage->Bps to iCurImage->Width*2,
    00421                         //because iCurImage->Bps refers to the 24 bit devil image
    00422                         PadSize = (4 - (iCurImage->Width*2 % 4)) % 4;
    00423
    00424                         //check if bitfield compression is used
    00425                         rMask = 0x7C00;
    00426                         gMask = 0x03E0;
    00427                         bMask = 0x001F;
    00428                         rShiftR = 10;
    00429                         gShiftR = 5;
    00430                         bShiftR = 0;
    00431                         rShiftL = 3;
    00432                         gShiftL = 3;
    00433                         bShiftL = 3;
    00434                         if (Header->biCompression == 3) //bitfields
    00435                         {
    00436                                 iseek(Header->bfDataOff - 12, IL_SEEK_SET); //seek to bitfield data
    00437                                 iread(&rMask, 4, 1);
    00438                                 iread(&gMask, 4, 1);
    00439                                 iread(&bMask, 4, 1);
    00440                                 UInt(&rMask);
    00441                                 UInt(&gMask);
    00442                                 UInt(&bMask);
    00443                                 GetShiftFromMask(rMask, &rShiftL, &rShiftR);
    00444                                 GetShiftFromMask(gMask, &gShiftL, &gShiftR);
    00445                                 GetShiftFromMask(bMask, &bShiftL, &bShiftR);
    00446                         }
    00447
    00448                         k = 0;
    00449
    00450                         //changed 2003-09-01
    00451                         if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
    00452                                 iPreCache(iCurImage->Width * iCurImage->Height);
    00453
    00454                         //@TODO: This may not be safe for Big Endian.
    00455                         for (j = 0; j < iCurImage->Height; j++) {
    00456                                 for(i = 0; i < iCurImage->Width; i++, k += 3) {
    00457                                         if (iread(&Read16, 2, 1) != 1) {
    00458                                                 iUnCache();
    00459                                                 return IL_FALSE;
    00460                                         }
    00461                                         iCurImage->Data[k] = ((Read16 & bMask) >> bShiftR) << bShiftL;
    00462                                         iCurImage->Data[k + 1] = ((Read16 & gMask) >> gShiftR)  << gShiftL;
    00463                                         iCurImage->Data[k + 2] = ((Read16 & rMask) >> rShiftR) << rShiftL;
    00464                                 }
    00465                                 iread(Padding, 1, PadSize);
    00466                         }
    00467
    00468                         iUnCache();
    00469                         break;
    00470
    00471                 case 8:
    00472                 case 24:
    00473             // For 8 and 24 bit, Bps is equal to the bmps bps
    00474                         PadSize = (4 - (iCurImage->Bps % 4)) % 4;
    00475                         if (PadSize == 0) {
    00476                                 if (iread(iCurImage->Data, 1, iCurImage->SizeOfPlane) != iCurImage->SizeOfPlane)
    00477                                         return IL_FALSE;
    00478                         }
    00479                         else {  // Microsoft requires lines to be padded if the widths aren't multiples of 4.
    00480                                 //changed 2003-09-01
    00481                                 if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
    00482                                         iPreCache(iCurImage->Width * iCurImage->Height);
    00483
    00484                                 PadSize = (4 - (iCurImage->Bps % 4));
    00485                                 for (i = 0; i < iCurImage->SizeOfPlane; i += iCurImage->Bps) {
    00486                                         if (iread(iCurImage->Data + i, 1, iCurImage->Bps) != iCurImage->Bps) {
    00487                                                 iUnCache();
    00488                                                 return IL_FALSE;
    00489                                         }
    00490                                         //iseek(PadSize, IL_SEEK_CUR);
    00491                                         iread(Padding, 1, PadSize);
    00492                                 }
    00493
    00494                                 iUnCache();
    00495                         }
    00496                         break;
    00497
    00498                 case 32:
    00499                         //32bit files are always padded to 4 byte...
    00500                         //check if bitfield compression is used
    00501                         rMask = 0xFF0000;
    00502                         gMask = 0x00FF00;
    00503                         bMask = 0x0000FF;
    00504                         rShiftR = 16;
    00505                         gShiftR = 8;
    00506                         bShiftR = 0;
    00507                         rShiftL = 0;
    00508                         gShiftL = 0;
    00509                         bShiftL = 0;
    00510                         if (Header->biCompression == 3) //bitfields
    00511                         {
    00512                                 iseek(Header->bfDataOff - 12, IL_SEEK_SET); //seek to bitfield data
    00513                                 iread(&rMask, 4, 1);
    00514                                 iread(&gMask, 4, 1);
    00515                                 iread(&bMask, 4, 1);
    00516                                 UInt(&rMask);
    00517                                 UInt(&gMask);
    00518                                 UInt(&bMask);
    00519                                 GetShiftFromMask(rMask, &rShiftL, &rShiftR);
    00520                                 GetShiftFromMask(gMask, &gShiftL, &gShiftR);
    00521                                 GetShiftFromMask(bMask, &bShiftL, &bShiftR);
    00522                         }
    00523
    00524                         //TODO: win98 supports per-pixel alpha, so
    00525                         //load to rgba????
    00526
    00527                         //changed 2003-09-01
    00528                         if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
    00529                                 iPreCache(iCurImage->Width * iCurImage->Height);
    00530
    00531                         for(i = 0; i < iCurImage->SizeOfData; i += 3) {
    00532                                 if (iread(&Read, 4, 1) != 1) {
    00533                                         iUnCache();
    00534                                         return IL_FALSE;
    00535                                 }
    00536
    00537                                 iCurImage->Data[i] = ((Read & bMask) >> bShiftR) << bShiftL;
    00538                                 iCurImage->Data[i + 1] = ((Read & gMask) >> gShiftR) << gShiftL;
    00539                                 iCurImage->Data[i + 2] = ((Read & rMask) >> rShiftR) << rShiftL;
    00540                         }
    00541
    00542                         iUnCache();
    00543                         break;
    00544
    00545                 default:
    00546                         return IL_FALSE; //shouldn't happen, we checked that before
    00547         }
    00548
    00549         return IL_TRUE;
    00550 }
    00551
    00552
    00553 ILboolean ilReadRLE8Bmp(BMPHEAD *Header)
    00554 {
    00555         ILubyte Bytes[2];
    00556         size_t  offset = 0, count, endOfLine = Header->biWidth;
    00557
    00558         // Update the current image with the new dimensions
    00559         if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, 1, 0, IL_UNSIGNED_BYTE, NULL))
    00560                 return IL_FALSE;
    00561
    00562         iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00563
    00564         // A height of 0 is illegal
    00565         if (Header->biHeight == 0)
    00566                 return IL_FALSE;
    00567
    00568         iCurImage->Format = IL_COLOUR_INDEX;
    00569         iCurImage->Pal.PalType = IL_PAL_BGR32;
    00570         iCurImage->Pal.PalSize = Header->biClrUsed * 4;  // 256 * 4 for most images
    00571         if (iCurImage->Pal.PalSize == 0)
    00572                 iCurImage->Pal.PalSize = 256 * 4;
    00573         iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
    00574         if (iCurImage->Pal.Palette == NULL)
    00575                 return IL_FALSE;
    00576
    00577         // If the image height is negative, then the image is flipped
    00578         //      (Normal is IL_ORIGIN_LOWER_LEFT)
    00579         iCurImage->Origin = Header->biHeight < 0 ?
    00580                  IL_ORIGIN_UPPER_LEFT : IL_ORIGIN_LOWER_LEFT;
    00581
    00582         // Read the palette
    00583         iseek(sizeof(BMPHEAD), IL_SEEK_SET);
    00584         if (iread(iCurImage->Pal.Palette, iCurImage->Pal.PalSize, 1) != 1)
    00585                 return IL_FALSE;
    00586
    00587         // Seek to the data from the "beginning" of the file
    00588         iseek(Header->bfDataOff, IL_SEEK_SET);
    00589
    00590     while (offset < iCurImage->SizeOfData) {
    00591                 if (iread(Bytes, sizeof(Bytes), 1) != 1)
    00592                         return IL_FALSE;
    00593                 if (Bytes[0] == 0x00) {  // Escape sequence
    00594                         switch (Bytes[1])
    00595                         {
    00596                                 case 0x00:  // End of line
    00597                                         offset = endOfLine;
    00598                                         endOfLine += iCurImage->Width;
    00599                                         break;
    00600                                 case 0x01:  // End of bitmap
    00601                                         offset = iCurImage->SizeOfData;
    00602                                         break;
    00603                                 case 0x2:
    00604                                         if (iread(Bytes, sizeof(Bytes), 1) != 1)
    00605                                                 return IL_FALSE;
    00606                                         offset += Bytes[0] + Bytes[1] * iCurImage->Width;
    00607                                         endOfLine += Bytes[1] * iCurImage->Width;
    00608                                         break;
    00609                                 default:
    00610                                         count = IL_MIN(Bytes[1], iCurImage->SizeOfData-offset);
    00611                                         if (iread(iCurImage->Data + offset, (ILuint)count, 1) != 1)
    00612                                                 return IL_FALSE;
    00613                                         offset += count;
    00614                                         if ((count & 1) == 1)  // Must be on a word boundary
    00615                                                 if (iread(Bytes, 1, 1) != 1)
    00616                                                         return IL_FALSE;
    00617                                         break;
    00618                         }
    00619                 } else {
    00620                         count = IL_MIN (Bytes[0], iCurImage->SizeOfData-offset);
    00621                         memset(iCurImage->Data + offset, Bytes[1], count);
    00622                         offset += count;
    00623                 }
    00624         }
    00625         return IL_TRUE;
    00626 }
    00627
    00628
    00629 //changed 2003-09-01
    00630 //deleted ilReadRLE8Bmp() USE_POINTER version
    00631
    00632 ILboolean ilReadRLE4Bmp(BMPHEAD *Header)
    00633 {
    00634         ILubyte Bytes[2];
    00635         ILuint  i;
    00636     size_t      offset = 0, count, endOfLine = Header->biWidth;
    00637
    00638         // Update the current image with the new dimensions
    00639         if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, 1, 0, IL_UNSIGNED_BYTE, NULL))
    00640                 return IL_FALSE;
    00641         iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00642
    00643         // A height of 0 is illegal
    00644         if (Header->biHeight == 0) {
    00645                 ilSetError(IL_ILLEGAL_FILE_VALUE);
    00646                 return IL_FALSE;
    00647         }
    00648
    00649         iCurImage->Format = IL_COLOUR_INDEX;
    00650         iCurImage->Pal.PalType = IL_PAL_BGR32;
    00651         iCurImage->Pal.PalSize = 16 * 4; //Header->biClrUsed * 4;  // 16 * 4 for most images
    00652         iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
    00653         if (iCurImage->Pal.Palette == NULL)
    00654                 return IL_FALSE;
    00655
    00656         // If the image height is negative, then the image is flipped
    00657         //      (Normal is IL_ORIGIN_LOWER_LEFT)
    00658         iCurImage->Origin = Header->biHeight < 0 ?
    00659                  IL_ORIGIN_UPPER_LEFT : IL_ORIGIN_LOWER_LEFT;
    00660
    00661         // Read the palette
    00662         iseek(sizeof(BMPHEAD), IL_SEEK_SET);
    00663
    00664         if (iread(iCurImage->Pal.Palette, iCurImage->Pal.PalSize, 1) != 1)
    00665                 return IL_FALSE;
    00666
    00667         // Seek to the data from the "beginning" of the file
    00668         iseek(Header->bfDataOff, IL_SEEK_SET);
    00669
    00670         while (offset < iCurImage->SizeOfData) {
    00671       int align;
    00672                 if (iread(&Bytes[0], sizeof(Bytes), 1) != 1)
    00673                         return IL_FALSE;
    00674                 if (Bytes[0] == 0x0) {                          // Escape sequence
    00675          switch (Bytes[1]) {
    00676          case 0x0:      // End of line
    00677             offset = endOfLine;
    00678             endOfLine += iCurImage->Width;
    00679             break;
    00680          case 0x1:      // End of bitmap
    00681             offset = iCurImage->SizeOfData;
    00682             break;
    00683          case 0x2:
    00684                                 if (iread(&Bytes[0], sizeof(Bytes), 1) != 1)
    00685                                         return IL_FALSE;
    00686             offset += Bytes[0] + Bytes[1] * iCurImage->Width;
    00687             endOfLine += Bytes[1] * iCurImage->Width;
    00688             break;
    00689          default:         // Run of pixels
    00690             count = IL_MIN (Bytes[1], iCurImage->SizeOfData-offset);
    00691
    00692                                 for (i = 0; i < count; i++) {
    00693                                         int byte;
    00694
    00695                                         if ((i & 0x01) == 0) {
    00696                                                 if (iread(&Bytes[0], sizeof(Bytes[0]), 1) != 1)
    00697                                                         return IL_FALSE;
    00698                                                 byte = (Bytes[0] >> 4);
    00699                                         }
    00700                                         else
    00701                                                 byte = (Bytes[0] & 0x0F);
    00702                                         iCurImage->Data[offset++] = byte;
    00703                                 }
    00704
    00705                                 align = Bytes[1] % 4;
    00706
    00707                                 if (align == 1 || align == 2)   // Must be on a word boundary
    00708                                         if (iread(&Bytes[0], sizeof(Bytes[0]), 1) != 1)
    00709                                                 return IL_FALSE;
    00710                         }
    00711                 } else {
    00712          count = IL_MIN (Bytes[0], iCurImage->SizeOfData-offset);
    00713          Bytes[0] = (Bytes[1] >> 4);
    00714                         Bytes[1] &= 0x0F;
    00715                         for (i = 0; i < count; i++)
    00716                                 iCurImage->Data[offset++] = Bytes [i & 1];
    00717                 }
    00718         }
    00719
    00720    return IL_TRUE;
    00721 }
    00722
    00723
    00724 //changed 2003-09-01
    00725 //deleted ilReadRLE4Bmp() USE_POINTER version
    00726
    00727 ILboolean iGetOS2Bmp(OS2_HEAD *Header)
    00728 {
    00729         ILuint  PadSize, Read, i, j, k, c;
    00730         ILubyte ByteData;
    00731
    00732         if (Header->cBitCount == 1) {
    00733                 if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) {
    00734                         return IL_FALSE;
    00735                 }
    00736                 iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00737
    00738                 iCurImage->Pal.Palette = (ILubyte*)ialloc(2 * 3);
    00739                 if (iCurImage->Pal.Palette == NULL) {
    00740                         return IL_FALSE;
    00741                 }
    00742                 iCurImage->Pal.PalSize = 2 * 3;
    00743                 iCurImage->Pal.PalType = IL_PAL_BGR24;
    00744
    00745                 if (iread(iCurImage->Pal.Palette, 1, 2 * 3) != 6)
    00746                         return IL_FALSE;
    00747
    00748                 PadSize = ((32 - (iCurImage->Width % 32)) / 8) % 4;  // Has to truncate.
    00749                 iseek(Header->DataOff, IL_SEEK_SET);
    00750
    00751                 for (j = 0; j < iCurImage->Height; j++) {
    00752                         Read = 0;
    00753                         for (i = 0; i < iCurImage->Width; ) {
    00754                                 if (iread(&ByteData, 1, 1) != 1)
    00755                                         return IL_FALSE;
    00756                                 Read++;
    00757                                 k = 128;
    00758                                 for (c = 0; c < 8; c++) {
    00759                                         iCurImage->Data[j * iCurImage->Width + i] =
    00760                                                 (!!(ByteData & k) == 1 ? 1 : 0);
    00761                                         k >>= 1;
    00762                                         if (++i >= iCurImage->Width)
    00763                                                 break;
    00764                                 }
    00765                         }
    00766                         iseek(PadSize, IL_SEEK_CUR);
    00767                 }
    00768                 return IL_TRUE;
    00769         }
    00770
    00771         if (Header->cBitCount == 4) {
    00772                 if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) {
    00773                         return IL_FALSE;
    00774                 }
    00775                 iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00776
    00777                 iCurImage->Pal.Palette = (ILubyte*)ialloc(16 * 3);
    00778                 if (iCurImage->Pal.Palette == NULL) {
    00779                         return IL_FALSE;
    00780                 }
    00781                 iCurImage->Pal.PalSize = 16 * 3;
    00782                 iCurImage->Pal.PalType = IL_PAL_BGR24;
    00783
    00784                 if (iread(iCurImage->Pal.Palette, 1, 16 * 3) != 16*3)
    00785                         return IL_FALSE;
    00786
    00787                 PadSize = ((8 - (iCurImage->Width % 8)) / 2) % 4;  // Has to truncate
    00788                 iseek(Header->DataOff, IL_SEEK_SET);
    00789
    00790                 for (j = 0; j < iCurImage->Height; j++) {
    00791                         for (i = 0; i < iCurImage->Width; i++) {
    00792                                 if (iread(&ByteData, 1, 1) != 1)
    00793                                         return IL_FALSE;
    00794                                 iCurImage->Data[j * iCurImage->Width + i] = ByteData >> 4;
    00795                                 if (++i == iCurImage->Width)
    00796                                         break;
    00797                                 iCurImage->Data[j * iCurImage->Width + i] = ByteData & 0x0F;
    00798                         }
    00799                         iseek(PadSize, IL_SEEK_CUR);
    00800                 }
    00801
    00802                 return IL_TRUE;
    00803         }
    00804
    00805         if (Header->cBitCount == 8) {
    00806                 //added this line 2003-09-01...strange no-one noticed before...
    00807                 if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
    00808                         return IL_FALSE;
    00809
    00810                 iCurImage->Pal.Palette = (ILubyte*)ialloc(256 * 3);
    00811                 if (iCurImage->Pal.Palette == NULL) {
    00812                         return IL_FALSE;
    00813                 }
    00814                 iCurImage->Pal.PalSize = 256 * 3;
    00815                 iCurImage->Pal.PalType = IL_PAL_BGR24;
    00816
    00817                 if (iread(iCurImage->Pal.Palette, 1, 256 * 3) != 256*3)
    00818                         return IL_FALSE;
    00819         }
    00820         else { //has to be 24 bpp
    00821                 if (!ilTexImage(Header->cx, Header->cy, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, NULL))
    00822                         return IL_FALSE;
    00823         }
    00824         iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
    00825
    00826         iseek(Header->DataOff, IL_SEEK_SET);
    00827
    00828         PadSize = (4 - (iCurImage->Bps % 4)) % 4;
    00829         if (PadSize == 0) {
    00830                 if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
    00831                         return IL_FALSE;
    00832         }
    00833         else {
    00834                 for (i = 0; i < iCurImage->Height; i++) {
    00835                         if (iread(iCurImage->Data + i * iCurImage->Bps, 1, iCurImage->Bps) != iCurImage->Bps)
    00836                                 return IL_FALSE;
    00837                         iseek(PadSize, IL_SEEK_CUR);
    00838                 }
    00839         }
    00840
    00841         return IL_TRUE;
    00842 }
    00843
    00844
    00846 ILboolean ilSaveBmp(const ILstring FileName)
    00847 {
    00848         ILHANDLE        BitmapFile;
    00849         ILuint          BitmapSize;
    00850
    00851         if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
    00852                 if (iFileExists(FileName)) {
    00853                         ilSetError(IL_FILE_ALREADY_EXISTS);
    00854                         return IL_FALSE;
    00855                 }
    00856         }
    00857
    00858         BitmapFile = iopenw(FileName);
    00859         if (BitmapFile == NULL) {
    00860                 ilSetError(IL_COULD_NOT_OPEN_FILE);
    00861                 return IL_FALSE;
    00862         }
    00863
    00864         BitmapSize = ilSaveBmpF(BitmapFile);
    00865         iclosew(BitmapFile);
    00866
    00867         if (BitmapSize == 0)
    00868                 return IL_FALSE;
    00869         return IL_TRUE;
    00870 }
    00871
    00872
    00874 ILuint ilSaveBmpF(ILHANDLE File)
    00875 {
    00876         ILuint Pos;
    00877         iSetOutputFile(File);
    00878         Pos = itellw();
    00879         if (iSaveBitmapInternal() == IL_FALSE)
    00880                 return 0;  // Error occurred
    00881         return itellw() - Pos;  // Return the number of bytes written.
    00882 }
    00883
    00884
    00886 ILuint ilSaveBmpL(void *Lump, ILuint Size)
    00887 {
    00888         ILuint Pos = itellw();
    00889         iSetOutputLump(Lump, Size);
    00890         if (iSaveBitmapInternal() == IL_FALSE)
    00891                 return 0;  // Error occurred
    00892         return itellw() - Pos;  // Return the number of bytes written.
    00893 }
    00894
    00895
    00896 // Internal function used to save the .bmp.
    00897 ILboolean iSaveBitmapInternal()
    00898 {
    00899         //int compress_rle8 = ilGetInteger(IL_BMP_RLE);
    00900         int compress_rle8 = IL_FALSE; // disabled BMP RLE compression. broken
    00901         ILuint  FileSize, i, PadSize, Padding = 0;
    00902         ILimage *TempImage = NULL;
    00903         ILpal   *TempPal;
    00904         ILubyte *TempData;
    00905
    00906         if (iCurImage == NULL) {
    00907                 ilSetError(IL_ILLEGAL_OPERATION);
    00908                 return IL_FALSE;
    00909         }
    00910
    00911         iputc('B');  // Comprises the
    00912         iputc('M');  //  "signature"
    00913
    00914         SaveLittleUInt(0);  // Will come back and change later in this function (filesize)
    00915         SaveLittleUInt(0);  // Reserved
    00916
    00917         if (compress_rle8 == IL_TRUE)
    00918         {
    00919                 TempImage = iConvertImage(iCurImage, IL_COLOR_INDEX, IL_UNSIGNED_BYTE);
    00920                 if (TempImage == NULL)
    00921                         return IL_FALSE;
    00922                 TempPal = iConvertPal(&TempImage->Pal, IL_PAL_BGR32);
    00923                 if (TempPal == NULL)
    00924                 {
    00925                         ilCloseImage(TempImage);
    00926                         return IL_FALSE;
    00927                 }
    00928         }
    00929
    00930         // If the current image has a palette, take care of it
    00931         TempPal = &iCurImage->Pal;
    00932         if( iCurImage->Pal.PalSize && iCurImage->Pal.Palette && iCurImage->Pal.PalType != IL_PAL_NONE ) {
    00933                 // If the palette in .bmp format, write it directly
    00934                 if (iCurImage->Pal.PalType == IL_PAL_BGR32) {
    00935                         TempPal = &iCurImage->Pal;
    00936                 } else {
    00937                         TempPal = iConvertPal(&iCurImage->Pal, IL_PAL_BGR32);
    00938                         if (TempPal == NULL) {
    00939                                 return IL_FALSE;
    00940                         }
    00941                 }
    00942         }
    00943
    00944         SaveLittleUInt(54 + TempPal->PalSize);  // Offset of the data
    00945
    00946         //Changed 20040923: moved this block above writing of
    00947         //BITMAPINFOHEADER, so that the written header refers to
    00948         //TempImage instead of the original image
    00949
    00950         // @TODO LUMINANCE converted to BGR insteaf of beign saved to luminance
    00951         if (iCurImage->Format != IL_BGR && iCurImage->Format != IL_BGRA && iCurImage->Format != IL_COLOUR_INDEX) {
    00952                 if (iCurImage->Format == IL_RGBA) {
    00953                         TempImage = iConvertImage(iCurImage, IL_BGRA, IL_UNSIGNED_BYTE);
    00954                 } else {
    00955                         TempImage = iConvertImage(iCurImage, IL_BGR, IL_UNSIGNED_BYTE);
    00956                 }
    00957                 if (TempImage == NULL)
    00958                         return IL_FALSE;
    00959         } else if (iCurImage->Bpc > 1) {
    00960                 TempImage = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_BYTE);
    00961                 if (TempImage == NULL)
    00962                         return IL_FALSE;
    00963         } else {
    00964                 TempImage = iCurImage;
    00965         }
    00966
    00967         if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT) {
    00968                 TempData = iGetFlipped(TempImage);
    00969                 if (TempData == NULL) {
    00970                         ilCloseImage(TempImage);
    00971                         return IL_FALSE;
    00972                 }
    00973         } else {
    00974                 TempData = TempImage->Data;
    00975         }
    00976
    00977         SaveLittleUInt(0x28);  // Header size
    00978         SaveLittleUInt(iCurImage->Width);
    00979
    00980         // Removed because some image viewers don't like the negative height.
    00981         // even if it is standard. @TODO should be enabled or disabled
    00982         // usually enabled.
    00983         /*if (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT)
    00984                 SaveLittleInt(-(ILint)iCurImage->Height);
    00985         else*/
    00986                 SaveLittleInt(TempImage->Height);
    00987
    00988         SaveLittleUShort(1);  // Number of planes
    00989         SaveLittleUShort((ILushort)((ILushort)TempImage->Bpp << 3));  // Bpp
    00990         if( compress_rle8 == IL_TRUE ) {
    00991                 SaveLittleInt(1); // rle8 compression
    00992         } else {
    00993                 SaveLittleInt(0);
    00994         }
    00995         SaveLittleInt(0);  // Size of image (Obsolete)
    00996         SaveLittleInt(0);  // (Obsolete)
    00997         SaveLittleInt(0);  // (Obsolete)
    00998
    00999         if (TempImage->Pal.PalType != IL_PAL_NONE) {
    01000                 SaveLittleInt(ilGetInteger(IL_PALETTE_NUM_COLS));  // Num colours used
    01001         } else {
    01002                 SaveLittleInt(0);
    01003         }
    01004         SaveLittleInt(0);  // Important colour (none)
    01005
    01006         iwrite(TempPal->Palette, 1, TempPal->PalSize);
    01007
    01008
    01009         if( compress_rle8 == IL_TRUE ) {
    01010                 //@TODO compress and save
    01011                 ILubyte *Dest = (ILubyte*)ialloc((long)((double)TempImage->SizeOfPlane*130/127));
    01012                 FileSize = ilRleCompress(TempImage->Data,TempImage->Width,TempImage->Height,
    01013                                                 TempImage->Depth,TempImage->Bpp,Dest,IL_BMPCOMP,NULL);
    01014                 iwrite(Dest,1,FileSize);
    01015         } else {
    01016                 PadSize = (4 - (TempImage->Bps % 4)) % 4;
    01017                 // No padding, so write data directly.
    01018                 if (PadSize == 0) {
    01019                         iwrite(TempData, 1, TempImage->SizeOfPlane);
    01020                 } else {  // Odd width, so we must pad each line.
    01021                         for (i = 0; i < TempImage->SizeOfPlane; i += TempImage->Bps) {
    01022                                 iwrite(TempData + i, 1, TempImage->Bps); // Write data
    01023                                 iwrite(&Padding, 1, PadSize); // Write pad byte(s)
    01024                         }
    01025                 }
    01026         }
    01027
    01028         // Write the filesize
    01029         FileSize = itellw();
    01030         iseekw(2, IL_SEEK_SET);
    01031         SaveLittleUInt(FileSize);
    01032
    01033         if (TempPal != &iCurImage->Pal) {
    01034                 ifree(TempPal->Palette);
    01035                 ifree(TempPal);
    01036         }
    01037         if (TempData != TempImage->Data)
    01038                 ifree(TempData);
    01039         if (TempImage != iCurImage)
    01040                 ilCloseImage(TempImage);
    01041
    01042         iseekw(FileSize, IL_SEEK_SET);
    01043
    01044         return IL_TRUE;
    01045 }
    01046
    01047
    01048 #endif//IL_NO_BMP