diff options
Diffstat (limited to 'DevIL/src-IL/src/il_bmp.cpp')
-rw-r--r-- | DevIL/src-IL/src/il_bmp.cpp | 1048 |
1 files changed, 1048 insertions, 0 deletions
diff --git a/DevIL/src-IL/src/il_bmp.cpp b/DevIL/src-IL/src/il_bmp.cpp new file mode 100644 index 00000000..e4719e1c --- /dev/null +++ b/DevIL/src-IL/src/il_bmp.cpp @@ -0,0 +1,1048 @@ +//----------------------------------------------------------------------------- +// +// ImageLib Sources +// Copyright (C) 2000-2008 by Denton Woods +// Last modified: 02/09/2009 +// +// Filename: src-IL/src/il_bmp.c +// +// Description: Reads from and writes to a bitmap (.bmp) file. +// +//----------------------------------------------------------------------------- + +#define IL_BMP_C + +#include "il_internal.h" +#ifndef IL_NO_BMP +#include "il_bmp.h" +#include "il_endian.h" +#include <stdio.h> +void GetShiftFromMask(const ILuint Mask, ILuint * CONST_RESTRICT ShiftLeft, ILuint * CONST_RESTRICT ShiftRight); + +//! Checks if the file specified in FileName is a valid .bmp file. +ILboolean ilIsValidBmp(ILconst_string CONST_RESTRICT FileName) +{ + ILHANDLE BitmapFile; + ILboolean bBitmap = IL_FALSE; + + if (!iCheckExtension(FileName, IL_TEXT("bmp"))) { + ilSetError(IL_INVALID_EXTENSION); + return bBitmap; + } + + BitmapFile = iopenr(FileName); + if (BitmapFile == NULL) { + ilSetError(IL_COULD_NOT_OPEN_FILE); + return bBitmap; + } + + bBitmap = ilIsValidBmpF(BitmapFile); + icloser(BitmapFile); + + return bBitmap; +} + + +//! Checks if the ILHANDLE contains a valid .bmp file at the current position. +ILboolean ilIsValidBmpF(ILHANDLE File) +{ + ILuint FirstPos; + ILboolean bRet; + + iSetInputFile(File); + FirstPos = itell(); + bRet = iIsValidBmp(); + iseek(FirstPos, IL_SEEK_SET); + + return bRet; +} + + +//! Checks if Lump is a valid .bmp lump. +ILboolean ilIsValidBmpL(const void * Lump, ILuint Size) +{ + iSetInputLump(Lump, Size); + return iIsValidBmp(); +} + +// Internal function used to get the .bmp header from the current file. +ILboolean iGetBmpHead(BMPHEAD * const Header) +{ + Header->bfType = GetLittleUShort(); + Header->bfSize = GetLittleInt(); + Header->bfReserved = GetLittleUInt(); + Header->bfDataOff = GetLittleInt(); + Header->biSize = GetLittleInt(); + Header->biWidth = GetLittleInt(); + Header->biHeight = GetLittleInt(); + Header->biPlanes = GetLittleShort(); + Header->biBitCount = GetLittleShort(); + Header->biCompression = GetLittleInt(); + Header->biSizeImage = GetLittleInt(); + Header->biXPelsPerMeter = GetLittleInt(); + Header->biYPelsPerMeter = GetLittleInt(); + Header->biClrUsed = GetLittleInt(); + Header->biClrImportant = GetLittleInt(); + return IL_TRUE; +} + + +ILboolean iGetOS2Head(OS2_HEAD * const Header) +{ + if (iread(Header, sizeof(OS2_HEAD), 1) != 1) + return IL_FALSE; + + UShort(&Header->bfType); + UInt(&Header->biSize); + Short(&Header->xHotspot); + Short(&Header->yHotspot); + UInt(&Header->DataOff); + UInt(&Header->cbFix); + + //2003-09-01 changed to UShort according to MSDN + UShort(&Header->cx); + UShort(&Header->cy); + UShort(&Header->cPlanes); + UShort(&Header->cBitCount); + + iseek((ILint)Header->cbFix - 12, IL_SEEK_CUR); // Skip rest of header, if any. + + return IL_TRUE; +} + + +// Internal function to get the header and check it. +ILboolean iIsValidBmp() +{ + BMPHEAD Head; + OS2_HEAD Os2Head; + ILboolean IsValid; + + iGetBmpHead(&Head); + iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR); // Go ahead and restore to previous state + + IsValid = iCheckBmp(&Head); + if (!IsValid) { + iGetOS2Head(&Os2Head); + iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR); + IsValid = iCheckOS2(&Os2Head); + } + return IsValid; +} + + +// Internal function used to check if the HEADER is a valid .bmp header. +ILboolean iCheckBmp (const BMPHEAD * CONST_RESTRICT Header) +{ + //if ((Header->bfType != ('B'|('M'<<8))) || ((Header->biSize != 0x28) && (Header->biSize != 0x0C))) + if ((Header->bfType != ('B'|('M'<<8))) || (Header->biSize != 0x28)) + return IL_FALSE; + if (Header->biHeight == 0 || Header->biWidth < 1) + return IL_FALSE; + if (Header->biPlanes > 1) // Not sure... + return IL_FALSE; + if(Header->biCompression != 0 && Header->biCompression != 1 && + Header->biCompression != 2 && Header->biCompression != 3) + return IL_FALSE; + if(Header->biCompression == 3 && Header->biBitCount != 16 && Header->biBitCount != 32) + return IL_FALSE; + if (Header->biBitCount != 1 && Header->biBitCount != 4 && Header->biBitCount != 8 && + Header->biBitCount != 16 && Header->biBitCount != 24 && Header->biBitCount != 32) + return IL_FALSE; + return IL_TRUE; +} + + +ILboolean iCheckOS2 (const OS2_HEAD * CONST_RESTRICT Header) +{ + if ((Header->bfType != ('B'|('M'<<8))) || (Header->DataOff < 26) || (Header->cbFix < 12)) + return IL_FALSE; + if (Header->cPlanes != 1) + return IL_FALSE; + if (Header->cx == 0 || Header->cy == 0) + return IL_FALSE; + if (Header->cBitCount != 1 && Header->cBitCount != 4 && Header->cBitCount != 8 && + Header->cBitCount != 24) + return IL_FALSE; + + return IL_TRUE; +} + + +//! Reads a .bmp file +ILboolean ilLoadBmp(ILconst_string FileName) { + ILHANDLE BitmapFile; + ILboolean bBitmap = IL_FALSE; + + BitmapFile = iopenr(FileName); + if (BitmapFile == NULL) { + ilSetError(IL_COULD_NOT_OPEN_FILE); + return bBitmap; + } + + bBitmap = ilLoadBmpF(BitmapFile); + icloser(BitmapFile); + + return bBitmap; +} + + +//! Reads an already-opened .bmp file +ILboolean ilLoadBmpF(ILHANDLE File) +{ + ILuint FirstPos; + ILboolean bRet; + + iSetInputFile(File); + FirstPos = itell(); + bRet = iLoadBitmapInternal(); + iseek(FirstPos, IL_SEEK_SET); + + return bRet; +} + + +//! Reads from a memory "lump" that contains a .bmp +ILboolean ilLoadBmpL(const void *Lump, ILuint Size) +{ + iSetInputLump(Lump, Size); + return iLoadBitmapInternal(); +} + + +// Internal function used to load the .bmp. +ILboolean iLoadBitmapInternal() +{ + BMPHEAD Header; + OS2_HEAD Os2Head; + ILboolean bBitmap; + + if (iCurImage == NULL) { + ilSetError(IL_ILLEGAL_OPERATION); + return IL_FALSE; + } + + iGetBmpHead(&Header); + if (!iCheckBmp(&Header)) { + iseek(-(ILint)sizeof(BMPHEAD), IL_SEEK_CUR); + iGetOS2Head(&Os2Head); + if (!iCheckOS2(&Os2Head)) { + ilSetError(IL_INVALID_FILE_HEADER); + return IL_FALSE; + } + else { + return iGetOS2Bmp(&Os2Head); + } + } + + // Don't know what to do if it has more than one plane... + if (Header.biPlanes != 1) { + ilSetError(IL_INVALID_FILE_HEADER); + return IL_FALSE; + } + + switch (Header.biCompression) + { + case 0: // No compression + case 3: // BITFIELDS compression is handled in 16 bit + // and 32 bit code in ilReadUncompBmp() + bBitmap = ilReadUncompBmp(&Header); + break; + case 1: // RLE 8-bit / pixel (BI_RLE8) + bBitmap = ilReadRLE8Bmp(&Header); + break; + case 2: // RLE 4-bit / pixel (BI_RLE4) + bBitmap = ilReadRLE4Bmp(&Header); + break; + + default: + ilSetError(IL_INVALID_FILE_HEADER); + return IL_FALSE; + } + + if (!ilFixImage()) + return IL_FALSE; + + return bBitmap; +} + + +// Reads an uncompressed .bmp +// One of the absolute ugliest functions I've ever written! +ILboolean ilReadUncompBmp(BMPHEAD * Header) +{ + ILuint i, j, k, c, Read; + ILubyte Bpp, ByteData, PadSize, Padding[4]; + ILuint rMask, gMask, bMask; //required for bitfields packing + ILuint rShiftR, gShiftR, bShiftR; //required for bitfields packing + ILuint rShiftL, gShiftL, bShiftL; //required for bitfields packing + ILushort Read16; //used for 16bit bmp loading + + if (Header->biBitCount < 8) + Bpp = 1; // We can't have an integral number less than one and greater than 0 + else + Bpp = (ILubyte)(Header->biBitCount >> 3); // Convert to bytes per pixel + + if(Bpp == 2 || Bpp == 4) + Bpp = 3; + + // Update the current image with the new dimensions + if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, Bpp, 0, IL_UNSIGNED_BYTE, NULL)) { + return IL_FALSE; + } + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + + switch (Header->biBitCount) + { + case 1: + //iCurImage->Format = IL_LUMINANCE; + iCurImage->Format = IL_COLOUR_INDEX; + iCurImage->Pal.PalType = IL_PAL_BGR32; + iCurImage->Pal.PalSize = 2 * 4; + iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize); + if (iCurImage->Pal.Palette == NULL) { + return IL_FALSE; + } + break; + + case 4: + case 8: + iCurImage->Format = IL_COLOUR_INDEX; + iCurImage->Pal.PalType = IL_PAL_BGR32; + + // if there are 256 colors biClrUsed is 0 + iCurImage->Pal.PalSize = Header->biClrUsed ? + Header->biClrUsed * 4 : 256 * 4; + + if (Header->biBitCount == 4) // biClrUsed is 0 for 4-bit bitmaps + iCurImage->Pal.PalSize = 16 * 4; + iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize); + if (iCurImage->Pal.Palette == NULL) { + return IL_FALSE; + } + break; + + case 16: + case 24: + case 32: + iCurImage->Format = IL_BGR; + break; + + default: + ilSetError(IL_ILLEGAL_FILE_VALUE); + return IL_FALSE; + } + + // A height of 0 is illegal + if (Header->biHeight == 0) { + ilSetError(IL_ILLEGAL_FILE_VALUE); + if (iCurImage->Pal.Palette) + ifree(iCurImage->Pal.Palette); + return IL_FALSE; + } + + // If the image height is negative, then the image is flipped + // (Normal is IL_ORIGIN_LOWER_LEFT) + if (Header->biHeight < 0) { + iCurImage->Origin = IL_ORIGIN_UPPER_LEFT; + } + else { + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + } + + // Read the palette + iseek(sizeof(BMPHEAD), IL_SEEK_SET); + if (iread(iCurImage->Pal.Palette, 1, iCurImage->Pal.PalSize) != iCurImage->Pal.PalSize) + return IL_FALSE; + + // Seek to the data from the "beginning" of the file + iseek(Header->bfDataOff, IL_SEEK_SET); + + // We have to handle 1 and 4-bit images separately, because we have to expand them. + switch (Header->biBitCount) + { + case 1: + //changed 2003-09-01 + if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) + iPreCache(iCurImage->Width / 8 * iCurImage->Height); + + PadSize = ((32 - (iCurImage->Width % 32)) / 8) % 4; // Has to truncate + for (j = 0; j < iCurImage->Height; j++) { + Read = 0; + for (i = 0; i < iCurImage->Width; ) { + if (iread(&ByteData, 1, 1) != 1) { + iUnCache(); + return IL_FALSE; + } + Read++; + k = 128; + for (c = 0; c < 8; c++) { + iCurImage->Data[j * iCurImage->Width + i] = + (!!(ByteData & k) == 1 ? 1 : 0); + k >>= 1; + if (++i >= iCurImage->Width) + break; + } + } + //iseek(PadSize, IL_SEEK_CUR); + iread(Padding, 1, PadSize); + } + + iUnCache(); + break; + + case 4: + //changed 2003-09-01 + if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) + iPreCache(iCurImage->Width / 2 * iCurImage->Height); + + PadSize = ((8 - (iCurImage->Width % 8)) / 2) % 4; // Has to truncate + for (j = 0; j < iCurImage->Height; j++) { + for (i = 0; i < iCurImage->Width; i++) { + if (iread(&ByteData, 1, 1) != 1) { + iUnCache(); + return IL_FALSE; + } + iCurImage->Data[j * iCurImage->Width + i] = ByteData >> 4; + if (++i == iCurImage->Width) + break; + iCurImage->Data[j * iCurImage->Width + i] = ByteData & 0x0F; + } + iread(Padding, 1, PadSize);//iseek(PadSize, IL_SEEK_CUR); + } + + iUnCache(); + break; + + case 16: + //padding + //2003-09-09: changed iCurImage->Bps to iCurImage->Width*2, + //because iCurImage->Bps refers to the 24 bit devil image + PadSize = (4 - (iCurImage->Width*2 % 4)) % 4; + + //check if bitfield compression is used + rMask = 0x7C00; + gMask = 0x03E0; + bMask = 0x001F; + rShiftR = 10; + gShiftR = 5; + bShiftR = 0; + rShiftL = 3; + gShiftL = 3; + bShiftL = 3; + if (Header->biCompression == 3) //bitfields + { + iseek(Header->bfDataOff - 12, IL_SEEK_SET); //seek to bitfield data + iread(&rMask, 4, 1); + iread(&gMask, 4, 1); + iread(&bMask, 4, 1); + UInt(&rMask); + UInt(&gMask); + UInt(&bMask); + GetShiftFromMask(rMask, &rShiftL, &rShiftR); + GetShiftFromMask(gMask, &gShiftL, &gShiftR); + GetShiftFromMask(bMask, &bShiftL, &bShiftR); + } + + k = 0; + + //changed 2003-09-01 + if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) + iPreCache(iCurImage->Width * iCurImage->Height); + + //@TODO: This may not be safe for Big Endian. + for (j = 0; j < iCurImage->Height; j++) { + for(i = 0; i < iCurImage->Width; i++, k += 3) { + if (iread(&Read16, 2, 1) != 1) { + iUnCache(); + return IL_FALSE; + } + iCurImage->Data[k] = ((Read16 & bMask) >> bShiftR) << bShiftL; + iCurImage->Data[k + 1] = ((Read16 & gMask) >> gShiftR) << gShiftL; + iCurImage->Data[k + 2] = ((Read16 & rMask) >> rShiftR) << rShiftL; + } + iread(Padding, 1, PadSize); + } + + iUnCache(); + break; + + case 8: + case 24: + // For 8 and 24 bit, Bps is equal to the bmps bps + PadSize = (4 - (iCurImage->Bps % 4)) % 4; + if (PadSize == 0) { + if (iread(iCurImage->Data, 1, iCurImage->SizeOfPlane) != iCurImage->SizeOfPlane) + return IL_FALSE; + } + else { // Microsoft requires lines to be padded if the widths aren't multiples of 4. + //changed 2003-09-01 + if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) + iPreCache(iCurImage->Width * iCurImage->Height); + + PadSize = (4 - (iCurImage->Bps % 4)); + for (i = 0; i < iCurImage->SizeOfPlane; i += iCurImage->Bps) { + if (iread(iCurImage->Data + i, 1, iCurImage->Bps) != iCurImage->Bps) { + iUnCache(); + return IL_FALSE; + } + //iseek(PadSize, IL_SEEK_CUR); + iread(Padding, 1, PadSize); + } + + iUnCache(); + } + break; + + case 32: + //32bit files are always padded to 4 byte... + //check if bitfield compression is used + rMask = 0xFF0000; + gMask = 0x00FF00; + bMask = 0x0000FF; + rShiftR = 16; + gShiftR = 8; + bShiftR = 0; + rShiftL = 0; + gShiftL = 0; + bShiftL = 0; + if (Header->biCompression == 3) //bitfields + { + iseek(Header->bfDataOff - 12, IL_SEEK_SET); //seek to bitfield data + iread(&rMask, 4, 1); + iread(&gMask, 4, 1); + iread(&bMask, 4, 1); + UInt(&rMask); + UInt(&gMask); + UInt(&bMask); + GetShiftFromMask(rMask, &rShiftL, &rShiftR); + GetShiftFromMask(gMask, &gShiftL, &gShiftR); + GetShiftFromMask(bMask, &bShiftL, &bShiftR); + } + + //TODO: win98 supports per-pixel alpha, so + //load to rgba???? + + //changed 2003-09-01 + if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) + iPreCache(iCurImage->Width * iCurImage->Height); + + for(i = 0; i < iCurImage->SizeOfData; i += 3) { + if (iread(&Read, 4, 1) != 1) { + iUnCache(); + return IL_FALSE; + } + + iCurImage->Data[i] = ((Read & bMask) >> bShiftR) << bShiftL; + iCurImage->Data[i + 1] = ((Read & gMask) >> gShiftR) << gShiftL; + iCurImage->Data[i + 2] = ((Read & rMask) >> rShiftR) << rShiftL; + } + + iUnCache(); + break; + + default: + return IL_FALSE; //shouldn't happen, we checked that before + } + + return IL_TRUE; +} + + +ILboolean ilReadRLE8Bmp(BMPHEAD *Header) +{ + ILubyte Bytes[2]; + size_t offset = 0, count, endOfLine = Header->biWidth; + + // Update the current image with the new dimensions + if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, 1, 0, IL_UNSIGNED_BYTE, NULL)) + return IL_FALSE; + + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + + // A height of 0 is illegal + if (Header->biHeight == 0) + return IL_FALSE; + + iCurImage->Format = IL_COLOUR_INDEX; + iCurImage->Pal.PalType = IL_PAL_BGR32; + iCurImage->Pal.PalSize = Header->biClrUsed * 4; // 256 * 4 for most images + if (iCurImage->Pal.PalSize == 0) + iCurImage->Pal.PalSize = 256 * 4; + iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize); + if (iCurImage->Pal.Palette == NULL) + return IL_FALSE; + + // If the image height is negative, then the image is flipped + // (Normal is IL_ORIGIN_LOWER_LEFT) + iCurImage->Origin = Header->biHeight < 0 ? + IL_ORIGIN_UPPER_LEFT : IL_ORIGIN_LOWER_LEFT; + + // Read the palette + iseek(sizeof(BMPHEAD), IL_SEEK_SET); + if (iread(iCurImage->Pal.Palette, iCurImage->Pal.PalSize, 1) != 1) + return IL_FALSE; + + // Seek to the data from the "beginning" of the file + iseek(Header->bfDataOff, IL_SEEK_SET); + + while (offset < iCurImage->SizeOfData) { + if (iread(Bytes, sizeof(Bytes), 1) != 1) + return IL_FALSE; + if (Bytes[0] == 0x00) { // Escape sequence + switch (Bytes[1]) + { + case 0x00: // End of line + offset = endOfLine; + endOfLine += iCurImage->Width; + break; + case 0x01: // End of bitmap + offset = iCurImage->SizeOfData; + break; + case 0x2: + if (iread(Bytes, sizeof(Bytes), 1) != 1) + return IL_FALSE; + offset += Bytes[0] + Bytes[1] * iCurImage->Width; + endOfLine += Bytes[1] * iCurImage->Width; + break; + default: + count = IL_MIN(Bytes[1], iCurImage->SizeOfData-offset); + if (iread(iCurImage->Data + offset, (ILuint)count, 1) != 1) + return IL_FALSE; + offset += count; + if ((count & 1) == 1) // Must be on a word boundary + if (iread(Bytes, 1, 1) != 1) + return IL_FALSE; + break; + } + } else { + count = IL_MIN (Bytes[0], iCurImage->SizeOfData-offset); + memset(iCurImage->Data + offset, Bytes[1], count); + offset += count; + } + } + return IL_TRUE; +} + + +//changed 2003-09-01 +//deleted ilReadRLE8Bmp() USE_POINTER version + +ILboolean ilReadRLE4Bmp(BMPHEAD *Header) +{ + ILubyte Bytes[2]; + ILuint i; + size_t offset = 0, count, endOfLine = Header->biWidth; + + // Update the current image with the new dimensions + if (!ilTexImage(Header->biWidth, abs(Header->biHeight), 1, 1, 0, IL_UNSIGNED_BYTE, NULL)) + return IL_FALSE; + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + + // A height of 0 is illegal + if (Header->biHeight == 0) { + ilSetError(IL_ILLEGAL_FILE_VALUE); + return IL_FALSE; + } + + iCurImage->Format = IL_COLOUR_INDEX; + iCurImage->Pal.PalType = IL_PAL_BGR32; + iCurImage->Pal.PalSize = 16 * 4; //Header->biClrUsed * 4; // 16 * 4 for most images + iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize); + if (iCurImage->Pal.Palette == NULL) + return IL_FALSE; + + // If the image height is negative, then the image is flipped + // (Normal is IL_ORIGIN_LOWER_LEFT) + iCurImage->Origin = Header->biHeight < 0 ? + IL_ORIGIN_UPPER_LEFT : IL_ORIGIN_LOWER_LEFT; + + // Read the palette + iseek(sizeof(BMPHEAD), IL_SEEK_SET); + + if (iread(iCurImage->Pal.Palette, iCurImage->Pal.PalSize, 1) != 1) + return IL_FALSE; + + // Seek to the data from the "beginning" of the file + iseek(Header->bfDataOff, IL_SEEK_SET); + + while (offset < iCurImage->SizeOfData) { + int align; + if (iread(&Bytes[0], sizeof(Bytes), 1) != 1) + return IL_FALSE; + if (Bytes[0] == 0x0) { // Escape sequence + switch (Bytes[1]) { + case 0x0: // End of line + offset = endOfLine; + endOfLine += iCurImage->Width; + break; + case 0x1: // End of bitmap + offset = iCurImage->SizeOfData; + break; + case 0x2: + if (iread(&Bytes[0], sizeof(Bytes), 1) != 1) + return IL_FALSE; + offset += Bytes[0] + Bytes[1] * iCurImage->Width; + endOfLine += Bytes[1] * iCurImage->Width; + break; + default: // Run of pixels + count = IL_MIN (Bytes[1], iCurImage->SizeOfData-offset); + + for (i = 0; i < count; i++) { + int byte; + + if ((i & 0x01) == 0) { + if (iread(&Bytes[0], sizeof(Bytes[0]), 1) != 1) + return IL_FALSE; + byte = (Bytes[0] >> 4); + } + else + byte = (Bytes[0] & 0x0F); + iCurImage->Data[offset++] = byte; + } + + align = Bytes[1] % 4; + + if (align == 1 || align == 2) // Must be on a word boundary + if (iread(&Bytes[0], sizeof(Bytes[0]), 1) != 1) + return IL_FALSE; + } + } else { + count = IL_MIN (Bytes[0], iCurImage->SizeOfData-offset); + Bytes[0] = (Bytes[1] >> 4); + Bytes[1] &= 0x0F; + for (i = 0; i < count; i++) + iCurImage->Data[offset++] = Bytes [i & 1]; + } + } + + return IL_TRUE; +} + + +//changed 2003-09-01 +//deleted ilReadRLE4Bmp() USE_POINTER version + +ILboolean iGetOS2Bmp(OS2_HEAD *Header) +{ + ILuint PadSize, Read, i, j, k, c; + ILubyte ByteData; + + if (Header->cBitCount == 1) { + if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) { + return IL_FALSE; + } + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + + iCurImage->Pal.Palette = (ILubyte*)ialloc(2 * 3); + if (iCurImage->Pal.Palette == NULL) { + return IL_FALSE; + } + iCurImage->Pal.PalSize = 2 * 3; + iCurImage->Pal.PalType = IL_PAL_BGR24; + + if (iread(iCurImage->Pal.Palette, 1, 2 * 3) != 6) + return IL_FALSE; + + PadSize = ((32 - (iCurImage->Width % 32)) / 8) % 4; // Has to truncate. + iseek(Header->DataOff, IL_SEEK_SET); + + for (j = 0; j < iCurImage->Height; j++) { + Read = 0; + for (i = 0; i < iCurImage->Width; ) { + if (iread(&ByteData, 1, 1) != 1) + return IL_FALSE; + Read++; + k = 128; + for (c = 0; c < 8; c++) { + iCurImage->Data[j * iCurImage->Width + i] = + (!!(ByteData & k) == 1 ? 1 : 0); + k >>= 1; + if (++i >= iCurImage->Width) + break; + } + } + iseek(PadSize, IL_SEEK_CUR); + } + return IL_TRUE; + } + + if (Header->cBitCount == 4) { + if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) { + return IL_FALSE; + } + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + + iCurImage->Pal.Palette = (ILubyte*)ialloc(16 * 3); + if (iCurImage->Pal.Palette == NULL) { + return IL_FALSE; + } + iCurImage->Pal.PalSize = 16 * 3; + iCurImage->Pal.PalType = IL_PAL_BGR24; + + if (iread(iCurImage->Pal.Palette, 1, 16 * 3) != 16*3) + return IL_FALSE; + + PadSize = ((8 - (iCurImage->Width % 8)) / 2) % 4; // Has to truncate + iseek(Header->DataOff, IL_SEEK_SET); + + for (j = 0; j < iCurImage->Height; j++) { + for (i = 0; i < iCurImage->Width; i++) { + if (iread(&ByteData, 1, 1) != 1) + return IL_FALSE; + iCurImage->Data[j * iCurImage->Width + i] = ByteData >> 4; + if (++i == iCurImage->Width) + break; + iCurImage->Data[j * iCurImage->Width + i] = ByteData & 0x0F; + } + iseek(PadSize, IL_SEEK_CUR); + } + + return IL_TRUE; + } + + if (Header->cBitCount == 8) { + //added this line 2003-09-01...strange no-one noticed before... + if (!ilTexImage(Header->cx, Header->cy, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) + return IL_FALSE; + + iCurImage->Pal.Palette = (ILubyte*)ialloc(256 * 3); + if (iCurImage->Pal.Palette == NULL) { + return IL_FALSE; + } + iCurImage->Pal.PalSize = 256 * 3; + iCurImage->Pal.PalType = IL_PAL_BGR24; + + if (iread(iCurImage->Pal.Palette, 1, 256 * 3) != 256*3) + return IL_FALSE; + } + else { //has to be 24 bpp + if (!ilTexImage(Header->cx, Header->cy, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, NULL)) + return IL_FALSE; + } + iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; + + iseek(Header->DataOff, IL_SEEK_SET); + + PadSize = (4 - (iCurImage->Bps % 4)) % 4; + if (PadSize == 0) { + if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData) + return IL_FALSE; + } + else { + for (i = 0; i < iCurImage->Height; i++) { + if (iread(iCurImage->Data + i * iCurImage->Bps, 1, iCurImage->Bps) != iCurImage->Bps) + return IL_FALSE; + iseek(PadSize, IL_SEEK_CUR); + } + } + + return IL_TRUE; +} + + +//! Writes a Bmp file +ILboolean ilSaveBmp(const ILstring FileName) +{ + ILHANDLE BitmapFile; + ILuint BitmapSize; + + if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) { + if (iFileExists(FileName)) { + ilSetError(IL_FILE_ALREADY_EXISTS); + return IL_FALSE; + } + } + + BitmapFile = iopenw(FileName); + if (BitmapFile == NULL) { + ilSetError(IL_COULD_NOT_OPEN_FILE); + return IL_FALSE; + } + + BitmapSize = ilSaveBmpF(BitmapFile); + iclosew(BitmapFile); + + if (BitmapSize == 0) + return IL_FALSE; + return IL_TRUE; +} + + +//! Writes a Bmp to an already-opened file +ILuint ilSaveBmpF(ILHANDLE File) +{ + ILuint Pos; + iSetOutputFile(File); + Pos = itellw(); + if (iSaveBitmapInternal() == IL_FALSE) + return 0; // Error occurred + return itellw() - Pos; // Return the number of bytes written. +} + + +//! Writes a Bmp to a memory "lump" +ILuint ilSaveBmpL(void *Lump, ILuint Size) +{ + ILuint Pos; + iSetOutputLump(Lump, Size); + Pos = itellw(); + if (iSaveBitmapInternal() == IL_FALSE) + return 0; // Error occurred + return itellw() - Pos; // Return the number of bytes written. +} + + +// Internal function used to save the .bmp. +ILboolean iSaveBitmapInternal() +{ + //int compress_rle8 = ilGetInteger(IL_BMP_RLE); + int compress_rle8 = IL_FALSE; // disabled BMP RLE compression. broken + ILuint FileSize, i, PadSize, Padding = 0; + ILimage *TempImage = NULL; + ILpal *TempPal; + ILubyte *TempData; + + if (iCurImage == NULL) { + ilSetError(IL_ILLEGAL_OPERATION); + return IL_FALSE; + } + + iputc('B'); // Comprises the + iputc('M'); // "signature" + + SaveLittleUInt(0); // Will come back and change later in this function (filesize) + SaveLittleUInt(0); // Reserved + + if (compress_rle8 == IL_TRUE) + { + TempImage = iConvertImage(iCurImage, IL_COLOR_INDEX, IL_UNSIGNED_BYTE); + if (TempImage == NULL) + return IL_FALSE; + TempPal = iConvertPal(&TempImage->Pal, IL_PAL_BGR32); + if (TempPal == NULL) + { + ilCloseImage(TempImage); + return IL_FALSE; + } + } + + // If the current image has a palette, take care of it + TempPal = &iCurImage->Pal; + if( iCurImage->Pal.PalSize && iCurImage->Pal.Palette && iCurImage->Pal.PalType != IL_PAL_NONE ) { + // If the palette in .bmp format, write it directly + if (iCurImage->Pal.PalType == IL_PAL_BGR32) { + TempPal = &iCurImage->Pal; + } else { + TempPal = iConvertPal(&iCurImage->Pal, IL_PAL_BGR32); + if (TempPal == NULL) { + return IL_FALSE; + } + } + } + + SaveLittleUInt(54 + TempPal->PalSize); // Offset of the data + + //Changed 20040923: moved this block above writing of + //BITMAPINFOHEADER, so that the written header refers to + //TempImage instead of the original image + + // @TODO LUMINANCE converted to BGR insteaf of beign saved to luminance + if (iCurImage->Format != IL_BGR && iCurImage->Format != IL_BGRA && iCurImage->Format != IL_COLOUR_INDEX) { + if (iCurImage->Format == IL_RGBA) { + TempImage = iConvertImage(iCurImage, IL_BGRA, IL_UNSIGNED_BYTE); + } else { + TempImage = iConvertImage(iCurImage, IL_BGR, IL_UNSIGNED_BYTE); + } + if (TempImage == NULL) + return IL_FALSE; + } else if (iCurImage->Bpc > 1) { + TempImage = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_BYTE); + if (TempImage == NULL) + return IL_FALSE; + } else { + TempImage = iCurImage; + } + + if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT) { + TempData = iGetFlipped(TempImage); + if (TempData == NULL) { + ilCloseImage(TempImage); + return IL_FALSE; + } + } else { + TempData = TempImage->Data; + } + + SaveLittleUInt(0x28); // Header size + SaveLittleUInt(iCurImage->Width); + + // Removed because some image viewers don't like the negative height. + // even if it is standard. @TODO should be enabled or disabled + // usually enabled. + /*if (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT) + SaveLittleInt(-(ILint)iCurImage->Height); + else*/ + SaveLittleInt(TempImage->Height); + + SaveLittleUShort(1); // Number of planes + SaveLittleUShort((ILushort)((ILushort)TempImage->Bpp << 3)); // Bpp + if( compress_rle8 == IL_TRUE ) { + SaveLittleInt(1); // rle8 compression + } else { + SaveLittleInt(0); + } + SaveLittleInt(0); // Size of image (Obsolete) + SaveLittleInt(0); // (Obsolete) + SaveLittleInt(0); // (Obsolete) + + if (TempImage->Pal.PalType != IL_PAL_NONE) { + SaveLittleInt(ilGetInteger(IL_PALETTE_NUM_COLS)); // Num colours used + } else { + SaveLittleInt(0); + } + SaveLittleInt(0); // Important colour (none) + + iwrite(TempPal->Palette, 1, TempPal->PalSize); + + + if( compress_rle8 == IL_TRUE ) { + //@TODO compress and save + ILubyte *Dest = (ILubyte*)ialloc((long)((double)TempImage->SizeOfPlane*130/127)); + FileSize = ilRleCompress(TempImage->Data,TempImage->Width,TempImage->Height, + TempImage->Depth,TempImage->Bpp,Dest,IL_BMPCOMP,NULL); + iwrite(Dest,1,FileSize); + } else { + PadSize = (4 - (TempImage->Bps % 4)) % 4; + // No padding, so write data directly. + if (PadSize == 0) { + iwrite(TempData, 1, TempImage->SizeOfPlane); + } else { // Odd width, so we must pad each line. + for (i = 0; i < TempImage->SizeOfPlane; i += TempImage->Bps) { + iwrite(TempData + i, 1, TempImage->Bps); // Write data + iwrite(&Padding, 1, PadSize); // Write pad byte(s) + } + } + } + + // Write the filesize + FileSize = itellw(); + iseekw(2, IL_SEEK_SET); + SaveLittleUInt(FileSize); + + if (TempPal != &iCurImage->Pal) { + ifree(TempPal->Palette); + ifree(TempPal); + } + if (TempData != TempImage->Data) + ifree(TempData); + if (TempImage != iCurImage) + ilCloseImage(TempImage); + + iseekw(FileSize, IL_SEEK_SET); + + return IL_TRUE; +} + + +#endif//IL_NO_BMP |