summaryrefslogtreecommitdiff
path: root/src/im_format_bmp.cpp
diff options
context:
space:
mode:
authorscuri <scuri>2008-10-17 06:10:15 +0000
committerscuri <scuri>2008-10-17 06:10:15 +0000
commit5a422aba704c375a307a902bafe658342e209906 (patch)
tree5005011e086bb863d8fb587ad3319bbec59b2447 /src/im_format_bmp.cpp
First commit - moving from LuaForge to SourceForge
Diffstat (limited to 'src/im_format_bmp.cpp')
-rw-r--r--src/im_format_bmp.cpp939
1 files changed, 939 insertions, 0 deletions
diff --git a/src/im_format_bmp.cpp b/src/im_format_bmp.cpp
new file mode 100644
index 0000000..809169b
--- /dev/null
+++ b/src/im_format_bmp.cpp
@@ -0,0 +1,939 @@
+/** \file
+ * \brief BMP - Windows Device Independent Bitmap
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_format_bmp.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $
+ */
+
+#include "im_format.h"
+#include "im_format_all.h"
+#include "im_util.h"
+#include "im_counter.h"
+
+#include "im_binfile.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+
+#define BMP_ID 0x4d42 /* BMP "magic" number */
+
+#define BMP_COMPRESS_RGB 0L /* No compression */
+#define BMP_COMPRESS_RLE8 1L /* 8 bits per pixel compression */
+#define BMP_COMPRESS_RLE4 2L /* 4 bits per pixel compression */
+#define BMP_BITFIELDS 3L /* no compression, palette is mask for 16 and 32 bits images */
+
+/* State-machine definitions */
+#define BMP_READING 0 /* General READING mode */
+#define BMP_ENCODING 1 /* Encoding same-color pixel runs */
+#define BMP_ABSMODE 2 /* Absolute-mode encoding */
+#define BMP_SINGLE 3 /* Encoding short absolute-mode runs */
+#define BMP_ENDOFLINE 4 /* End of scan line detected */
+
+#define BMP_LSN(value) (unsigned char)((value) & 0x0f) /* Least-significant nibble */
+#define BMP_MSN(value) (unsigned char)(((value) & 0xf0) >> 4) /* Most-significant nibble */
+
+
+/* File Header Structure.
+ * 2 Type; File Type Identifier
+ * 4 FileSize; Size of File
+ * 2 Reserved1; Reserved (should be 0)
+ * 2 Reserved2; Reserved (should be 0)
+ * 4 Offset; Offset to bitmap data
+ * 14 TOTAL */
+
+/* Information Header Structure.
+ * 4 Size; Size of Remaining Header
+ * 4 Width; Width of Bitmap in Pixels
+ * 4 Height; Height of Bitmap in Pixels
+ * 2 Planes; Number of Planes
+ * 2 BitCount; Bits Per Pixel
+ * 4 Compression; Compression Scheme
+ * 4 SizeImage; Size of bitmap
+ * 4 XPelsPerMeter; Horz. Resolution in Pixels/Meter
+ * 4 YPelsPerMeter; Vert. Resolution in Pixels/Meter
+ * 4 ClrUsed; Number of Colors in Color Table
+ * 4 ClrImportant; Number of Important Colors
+ * 40 TOTAL V3
+ * 4 RedMask;
+ * 4 GreenMask;
+ * 4 BlueMask;
+ * 4 AlphaMask;
+ * 4 CSType;
+ * 12 ciexyzRed(x, y, z); [3*FXPT2DOT30]
+ * 12 ciexyzGreen(x, y, z); "
+ * 12 ciexyzBlue(x, y, z); "
+ * 4 GammaRed;
+ * 4 GammaGreen;
+ * 4 GammaBlue;
+ * 108 TOTAL V4 (not supported here)
+ * 4 Intent;
+ * 4 ProfileData;
+ * 4 ProfileSize;
+ * 4 Reserved;
+ * 120 TOTAL V5 (not supported here)
+ */
+
+/* RGB Color Quadruple Structure. */
+/* 1 rgbBlue; Blue Intensity Value */
+/* 1 rgbGreen; Green Intensity Value */
+/* 1 rgbRed; Red Intensity Value */
+/* 1 rgbReserved; Reserved (should be 0) */
+/* 4 */
+
+static int iBMPDecodeScanLine(imBinFile* handle, unsigned char* DecodedBuffer, int Width)
+{
+ unsigned char runCount; /* Number of pixels in the run */
+ unsigned char runValue; /* Value of pixels in the run */
+ int Index = 0; /* The index of DecodedBuffer */
+ int cont = 1, remain;
+
+ while (cont)
+ {
+ imBinFileRead(handle, &runCount, 1, 1); /* Number of pixels in the run */
+ imBinFileRead(handle, &runValue, 1, 1); /* Value of pixels in the run */
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (runCount)
+ {
+ while (runCount-- && Index < Width)
+ DecodedBuffer[Index++] = runValue;
+ }
+ else /* Abssolute Mode or Escape Code */
+ {
+ switch(runValue)
+ {
+ case 0: /* End of Scan Line Escape Code */
+ case 1: /* End of Bitmap Escape Code */
+ cont = 0;
+ break;
+ case 2: /* Delta Escape Code (ignored) */
+ imBinFileRead(handle, &runCount, 1, 1);
+ imBinFileRead(handle, &runCount, 1, 1);
+ break;
+ default: /* Abssolute Mode */
+ remain = runValue % 2;
+ runValue = (unsigned char)(Index + runValue < (Width + 1)? runValue: (Width - 1) - Index);
+ imBinFileRead(handle, DecodedBuffer + Index, runValue, 1);
+ if (remain)
+ imBinFileSeekOffset(handle, 1);
+ Index += runValue;
+ }
+ }
+
+ if (imBinFileError(handle) || Index > Width)
+ return IM_ERR_ACCESS;
+ }
+
+ return IM_ERR_NONE;
+}
+
+static int iBMPEncodeScanLine(unsigned char* EncodedBuffer, unsigned char* sl, int np)
+{
+ int slx = 0; /* Scan line index */
+ int state = BMP_READING; /* State machine control variable */
+ int count = 0; /* Used by various states */
+ unsigned char pixel; /* Holds single pixels from sl */
+ int done = 0; /* Ends while loop when true */
+ int oldcount, oldslx; /* Copies of count and slx */
+ int BufSize = 0;
+
+ while (!done)
+ {
+ switch (state)
+ {
+ case BMP_READING:
+ /* Input: */
+ /* np == number of pixels in scan line */
+ /* sl == scan line */
+ /* sl[slx] == next pixel to process */
+
+ if (slx >= np) /* No pixels left */
+ state = BMP_ENDOFLINE;
+ else if (slx == np - 1) /* One pixel left */
+ {
+ count = 1;
+ state = BMP_SINGLE;
+ }
+ else if (sl[slx] == sl[slx + 1]) /* Next 2 pixels equal */
+ state = BMP_ENCODING;
+ else /* Next 2 pixels differ */
+ state = BMP_ABSMODE;
+
+ break;
+ case BMP_ENCODING:
+ /* Input: */
+ /* slx <= np - 2 (at least 2 pixels in run) */
+ /* sl[slx] == first pixel of run */
+ /* sl[slx] == sl[slx + 1] */
+
+ count = 2;
+ pixel = sl[slx];
+ slx += 2;
+
+ while ((slx < np) && (pixel == sl[slx]) && (count < 255))
+ {
+ count++;
+ slx++;
+ }
+
+ *EncodedBuffer++ = (unsigned char)count;
+ BufSize++;
+ *EncodedBuffer++ = pixel;
+ BufSize++;
+ state = BMP_READING;
+
+ break;
+ case BMP_ABSMODE:
+ /* Input: */
+ /* slx <= np - 2 (at least 2 pixels in run) */
+ /* sl[slx] == first pixel of run */
+ /* sl[slx] != sl[slx + 1] */
+
+ oldslx = slx;
+ count = 2;
+ slx += 2;
+
+ /* Compute number of bytes in run */
+ while ((slx < np) && (sl[slx] != sl[slx - 1]) && (count < 255))
+ {
+ count++;
+ slx++;
+ }
+
+ /* If same-color run found, back up one byte */
+ if ((slx < np) && (sl[slx] == sl[slx - 1]))
+ if (count > 1)
+ count--;
+
+ slx = oldslx; /* Restore scan-line index */
+
+ /* Output short absolute runs of less than 3 pixels */
+ if (count < 3 )
+ state = BMP_SINGLE;
+ else
+ {
+ /* Output absolute-mode run */
+ *EncodedBuffer++ = 0;
+ BufSize++;
+ *EncodedBuffer++ = (unsigned char)count;
+ BufSize++;
+ oldcount = count;
+
+ while (count > 0)
+ {
+ *EncodedBuffer++ = sl[slx];
+ BufSize++;
+ slx++;
+ count--;
+ }
+
+ if (oldcount % 2)
+ {
+ *EncodedBuffer++ = 0;
+ BufSize++;
+ }
+
+ state = BMP_READING;
+ }
+ break;
+
+ case BMP_SINGLE:
+ /* Input: */
+ /* count == number of pixels to output */
+ /* slx < np */
+ /* sl[slx] == first pixel of run */
+ /* sl[slx] != sl[slx + 1] */
+
+ while (count > 0)
+ {
+ *EncodedBuffer++ = (unsigned char)1;
+ BufSize++;
+ *EncodedBuffer++ = sl[slx];
+ BufSize++;
+ slx++;
+ count--;
+ }
+
+ state = BMP_READING;
+
+ break;
+ case BMP_ENDOFLINE:
+ *EncodedBuffer++ = (unsigned char)0;
+ BufSize++;
+ *EncodedBuffer++ = (unsigned char)0;
+ BufSize++;
+ done = 1;
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ return BufSize;
+}
+
+static const char* iBMPCompTable[2] =
+{
+ "NONE",
+ "RLE"
+};
+
+class imFormatBMP: public imFormat
+{
+ imBinFile* handle; /* the binary file handle */
+ unsigned short bpp; /* number of bits per pixel */
+ unsigned int offset, /* image data offset, used only when reading */
+ comp_type; /* bmp compression information */
+ int is_os2, /* indicates an os2 1.x BMP */
+ line_raw_size; // raw line size
+ unsigned int rmask, gmask, bmask,
+ roff, goff, boff; /* pixel bit mask control when reading 16 and 32 bpp images */
+
+ int ReadPalette();
+ int WritePalette();
+ void FixRGBOrder();
+
+public:
+ imFormatBMP()
+ :imFormat("BMP",
+ "Windows Device Independent Bitmap",
+ "*.bmp;*.dib;",
+ iBMPCompTable,
+ 2,
+ 0)
+ {}
+ ~imFormatBMP() {}
+
+ int Open(const char* file_name);
+ int New(const char* file_name);
+ void Close();
+ void* Handle(int index);
+ int ReadImageInfo(int index);
+ int ReadImageData(void* data);
+ int WriteImageInfo();
+ int WriteImageData(void* data);
+ int CanWrite(const char* compression, int color_mode, int data_type) const;
+};
+
+void imFormatRegisterBMP(void)
+{
+ imFormatRegister(new imFormatBMP());
+}
+
+int imFormatBMP::Open(const char* file_name)
+{
+ unsigned short id;
+ unsigned int dword;
+
+ /* opens the binary file for reading with intel byte order */
+ handle = imBinFileOpen(file_name);
+ if (!handle)
+ return IM_ERR_OPEN;
+
+ imBinFileByteOrder(handle, IM_LITTLEENDIAN);
+
+ /* reads the BMP format identifier */
+ imBinFileRead(handle, &id, 1, 2);
+ if (imBinFileError(handle))
+ {
+ imBinFileClose(handle);
+ return IM_ERR_ACCESS;
+ }
+
+ if (id != BMP_ID)
+ {
+ imBinFileClose(handle);
+ return IM_ERR_FORMAT;
+ }
+
+ /* jump 8 bytes (file size,reserved) */
+ imBinFileSeekOffset(handle, 8);
+
+ /* reads the image offset */
+ imBinFileRead(handle, &this->offset, 1, 4);
+
+ /* reads the header size */
+ imBinFileRead(handle, &dword, 1, 4);
+
+ if (dword == 40)
+ this->is_os2 = 0;
+ else if (dword == 12)
+ this->is_os2 = 1;
+ else
+ {
+ imBinFileClose(handle);
+ return IM_ERR_FORMAT;
+ }
+
+ this->image_count = 1;
+
+ /* reads the compression information */
+ if (this->is_os2)
+ {
+ this->comp_type = BMP_COMPRESS_RGB;
+ strcpy(this->compression, "NONE");
+ }
+ else
+ {
+ imBinFileSeekOffset(handle, 12);
+
+ imBinFileRead(handle, &this->comp_type, 1, 4);
+
+ switch (this->comp_type)
+ {
+ case BMP_COMPRESS_RGB:
+ strcpy(this->compression, "NONE");
+ break;
+ case BMP_COMPRESS_RLE8:
+ strcpy(this->compression, "RLE");
+ break;
+ case BMP_COMPRESS_RLE4:
+ default:
+ imBinFileClose(handle);
+ return IM_ERR_COMPRESS;
+ }
+
+ imBinFileSeekOffset(handle, -16);
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatBMP::New(const char* file_name)
+{
+ /* opens the binary file for writing with intel byte order */
+ handle = imBinFileNew(file_name);
+ if (!handle)
+ return IM_ERR_OPEN;
+
+ imBinFileByteOrder(handle, IM_LITTLEENDIAN);
+
+ this->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatBMP::Close()
+{
+ imBinFileClose(handle);
+}
+
+void* imFormatBMP::Handle(int index)
+{
+ if (index == 0)
+ return (void*)this->handle;
+ else
+ return NULL;
+}
+
+int imFormatBMP::ReadImageInfo(int index)
+{
+ (void)index;
+ unsigned int dword;
+
+ this->file_data_type = IM_BYTE;
+
+ if (this->is_os2)
+ {
+ short word;
+
+ /* reads the image width */
+ imBinFileRead(handle, &word, 1, 2);
+ this->width = (int)word;
+
+ /* reads the image height */
+ imBinFileRead(handle, &word, 1, 2);
+ this->height = (int)((word < 0)? -word: word);
+
+ dword = word; // it will be used later
+ }
+ else
+ {
+ /* reads the image width */
+ imBinFileRead(handle, &dword, 1, 4);
+ this->width = (int)dword;
+
+ /* reads the image height */
+ imBinFileRead(handle, &dword, 1, 4);
+ this->height = (int)dword;
+ if (this->height < 0)
+ this->height = -this->height;
+ }
+
+ /* jump 2 bytes (planes) */
+ imBinFileSeekOffset(handle, 2);
+
+ /* reads the number of bits per pixel */
+ imBinFileRead(handle, &this->bpp, 1, 2);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ // sanity check
+ if (this->bpp != 1 && this->bpp != 4 && this->bpp != 8 &&
+ this->bpp != 16 && this->bpp != 24 && this->bpp != 32)
+ return IM_ERR_DATA;
+
+ // another sanity check
+ if (this->comp_type == BMP_BITFIELDS && this->bpp != 16 && this->bpp != 32)
+ return IM_ERR_DATA;
+
+ if (this->bpp > 8)
+ {
+ this->file_color_mode = IM_RGB;
+ this->file_color_mode |= IM_PACKED;
+ }
+ else
+ {
+ this->palette_count = 1 << bpp;
+ this->file_color_mode = IM_MAP;
+ }
+
+ if (this->bpp < 8)
+ this->convert_bpp = this->bpp;
+
+ if (this->bpp == 32)
+ this->file_color_mode |= IM_ALPHA;
+
+ if (dword < 0)
+ this->file_color_mode |= IM_TOPDOWN;
+
+ this->line_raw_size = imFileLineSizeAligned(this->width, this->bpp, 4);
+ this->line_buffer_extra = 4; // room enough for padding
+
+ if (this->is_os2)
+ {
+ if (this->bpp < 24)
+ return ReadPalette();
+
+ return IM_ERR_NONE;
+ }
+
+ /* we already readed the compression information */
+ /* jump 8 bytes (compression, image size) */
+ imBinFileSeekOffset(handle, 8);
+
+ /* read the x resolution */
+ imBinFileRead(handle, &dword, 1, 4);
+ float xres = (float)dword / 100.0f;
+
+ /* read the y resolution */
+ imBinFileRead(handle, &dword, 1, 4);
+ float yres = (float)dword / 100.0f;
+
+ if (xres && yres)
+ {
+ imAttribTable* attrib_table = AttribTable();
+ attrib_table->Set("XResolution", IM_FLOAT, 1, &xres);
+ attrib_table->Set("YResolution", IM_FLOAT, 1, &yres);
+ attrib_table->Set("ResolutionUnit", IM_BYTE, 4, "DPC");
+ }
+
+ if (this->bpp <= 8)
+ {
+ /* reads the number of colors used */
+ imBinFileRead(handle, &dword, 1, 4);
+
+ /* updates the palette_count based on the number of colors used */
+ if (dword != 0 && dword < (unsigned int)this->palette_count)
+ this->palette_count = dword;
+
+ /* jump 4 bytes (important colors) */
+ imBinFileSeekOffset(handle, 4);
+ }
+ else
+ {
+ /* jump 8 bytes (used colors, important colors) */
+ imBinFileSeekOffset(handle, 8);
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (this->bpp <= 8)
+ return ReadPalette();
+
+ if (this->bpp == 16 || this->bpp == 32)
+ {
+ if (this->comp_type == BMP_BITFIELDS)
+ {
+ unsigned int Mask;
+ unsigned int PalMask[3];
+
+ imBinFileRead(handle, PalMask, 3, 4);
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ this->roff = 0;
+ this->rmask = Mask = PalMask[0];
+ while (!(Mask & 0x01) && (Mask != 0))
+ {Mask >>= 1; this->roff++;}
+
+ this->goff = 0;
+ this->gmask = Mask = PalMask[1];
+ while (!(Mask & 0x01) && (Mask != 0))
+ {Mask >>= 1; this->goff++;}
+
+ this->boff = 0;
+ this->bmask = Mask = PalMask[2];
+ while (!(Mask & 0x01) && (Mask != 0))
+ {Mask >>= 1; this->boff++;}
+ }
+ else
+ {
+ if (this->bpp == 16)
+ {
+ this->rmask = 0x7C00;
+ this->roff = 10;
+
+ this->gmask = 0x03E0;
+ this->goff = 5;
+
+ this->bmask = 0x001F;
+ this->boff = 0;
+ }
+ else
+ {
+ this->rmask = 0x00FF0000;
+ this->roff = 16;
+
+ this->gmask = 0x0000FF00;
+ this->goff = 8;
+
+ this->bmask = 0x000000FF;
+ this->boff = 0;
+ }
+ }
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatBMP::WriteImageInfo()
+{
+ // force bottom up orientation
+ this->file_data_type = IM_BYTE;
+ this->file_color_mode = imColorModeSpace(this->user_color_mode);
+
+ if (imStrEqual(this->compression, "RLE"))
+ this->comp_type = BMP_COMPRESS_RLE8;
+ else
+ this->comp_type = BMP_COMPRESS_RGB;
+
+ if (this->file_color_mode == IM_BINARY)
+ {
+ this->bpp = 1;
+ this->convert_bpp = 1;
+ }
+ else if (this->file_color_mode == IM_RGB)
+ {
+ this->file_color_mode |= IM_PACKED;
+ this->bpp = 24;
+
+ if (imColorModeHasAlpha(this->user_color_mode))
+ {
+ this->file_color_mode |= IM_ALPHA;
+ this->bpp = 32;
+
+ this->rmask = 0x00FF0000;
+ this->roff = 16;
+
+ this->gmask = 0x0000FF00;
+ this->goff = 8;
+
+ this->bmask = 0x000000FF;
+ this->boff = 0;
+ }
+ }
+ else
+ this->bpp = 8;
+
+ this->line_raw_size = imFileLineSizeAligned(this->width, this->bpp, 4);
+ this->line_buffer_extra = 4; // room enough for padding
+
+ if (this->comp_type == BMP_COMPRESS_RLE8)
+ {
+ // allocates more than enough since compression algoritm can be ineficient
+ this->line_buffer_extra += 2*this->line_raw_size;
+ }
+
+ /* writes the BMP file header */
+ int palette_size = (this->bpp > 8)? 0: palette_count*4;
+ short word_value = BMP_ID;
+ imBinFileWrite(handle, &word_value, 1, 2); /* identifier */
+ unsigned int dword_value = 14 + 40 + palette_size + line_raw_size * this->height;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* file size for uncompressed images */
+ word_value = 0;
+ imBinFileWrite(handle, &word_value, 1, 2); /* reserved 1 */
+ imBinFileWrite(handle, &word_value, 1, 2); /* reserved 2 */
+ dword_value = 14 + 40 + palette_size;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* data offset */
+
+ /* writes the BMP info header */
+
+ dword_value = 40;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* header size */
+ dword_value = this->width;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* width */
+ dword_value = this->height;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* height */
+ word_value = 1;
+ imBinFileWrite(handle, &word_value, 1, 2); /* planes */
+ word_value = this->bpp;
+ imBinFileWrite(handle, &word_value, 1, 2); /* bpp */
+ dword_value = this->comp_type;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* compression */
+ dword_value = line_raw_size * this->height;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* image size */
+
+ imAttribTable* attrib_table = AttribTable();
+ unsigned int xppm = 0, yppm = 0;
+
+ const void* attrib_data = attrib_table->Get("ResolutionUnit");
+ if (attrib_data)
+ {
+ char* res_unit = (char*)attrib_data;
+
+ float* xres = (float*)attrib_table->Get("XResolution");
+ float* yres = (float*)attrib_table->Get("YResolution");
+
+ if (xres && yres)
+ {
+ if (imStrEqual(res_unit, "DPI"))
+ {
+ xppm = (unsigned int)(*xres * 100. / 2.54);
+ yppm = (unsigned int)(*yres * 100. / 2.54);
+ }
+ else
+ {
+ xppm = (unsigned int)(*xres * 100.);
+ yppm = (unsigned int)(*yres * 100.);
+ }
+ }
+ }
+
+ imBinFileWrite(handle, &xppm, 1, 4); /* x dpm */
+ imBinFileWrite(handle, &yppm, 1, 4); /* y dpm */
+
+ dword_value = (this->bpp > 8)? 0: this->palette_count;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* colors used */
+ dword_value = 0;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* colors important (all) */
+
+ /* tests if everything was ok */
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (this->bpp < 24)
+ return WritePalette();
+
+ return IM_ERR_NONE;
+}
+
+int imFormatBMP::ReadPalette()
+{
+ int nc;
+ if (this->is_os2)
+ nc = 3;
+ else
+ nc = 4;
+
+ /* reads the color palette */
+ unsigned char bmp_colors[256 * 4];
+ imBinFileRead(handle, bmp_colors, this->palette_count * nc, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ /* convert the color map to the IM format */
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ int i = c * nc;
+ this->palette[c] = imColorEncode(bmp_colors[i + 2],
+ bmp_colors[i + 1],
+ bmp_colors[i]);
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatBMP::WritePalette()
+{
+ unsigned char bmp_colors[256 * 4];
+
+ /* convert the color map to the IM format */
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ int i = c * 4;
+ imColorDecode(&bmp_colors[i + 2], &bmp_colors[i + 1], &bmp_colors[i], this->palette[c]);
+ bmp_colors[i + 3] = 0;
+ }
+
+ /* writes the color palette */
+ imBinFileWrite(handle, bmp_colors, this->palette_count * 4, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatBMP::FixRGBOrder()
+{
+ int x;
+
+ switch (this->bpp)
+ {
+ case 16:
+ {
+ /* inverts the WORD values if not intel */
+ if (imBinCPUByteOrder() == IM_BIGENDIAN)
+ imBinSwapBytes2(this->line_buffer, this->width);
+
+ imushort* word_data = (imushort*)this->line_buffer;
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+
+ // from end to start
+ for (x = this->width-1; x >= 0; x--)
+ {
+ imushort word_value = word_data[x];
+ int c = x*3;
+ byte_data[c] = (imbyte)((((this->rmask & word_value) >> this->roff) * 255) / (this->rmask >> this->roff));
+ byte_data[c+1] = (imbyte)((((this->gmask & word_value) >> this->goff) * 255) / (this->gmask >> this->goff));
+ byte_data[c+2] = (imbyte)((((this->bmask & word_value) >> this->boff) * 255) / (this->bmask >> this->boff));
+ }
+ }
+ break;
+ case 32:
+ {
+ /* inverts the DWORD values if not intel */
+ if (imBinCPUByteOrder() == IM_BIGENDIAN)
+ imBinSwapBytes4(this->line_buffer, this->width);
+
+ unsigned int* dword_data = (unsigned int*)this->line_buffer;
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+
+ for (x = 0; x < this->width; x++)
+ {
+ unsigned int dword_value = dword_data[x];
+ int c = x*4;
+ byte_data[c] = (imbyte)((this->rmask & dword_value) >> this->roff);
+ byte_data[c+1] = (imbyte)((this->gmask & dword_value) >> this->goff);
+ byte_data[c+2] = (imbyte)((this->bmask & dword_value) >> this->boff);
+ byte_data[c+3] = (imbyte)((0xFF000000 & dword_value) >> 24);
+ }
+ }
+ break;
+ default: // 24
+ {
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+ for (x = 0; x < this->width; x++)
+ {
+ int c = x*3;
+ imbyte temp = byte_data[c]; // swap R and B
+ byte_data[c] = byte_data[c+2];
+ byte_data[c+2] = temp;
+ }
+ }
+ break;
+ }
+}
+
+int imFormatBMP::ReadImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Reading BMP...");
+
+ /* jump to the begin of image data */
+ imBinFileSeekTo(handle, this->offset);
+
+ for (int row = 0; row < this->height; row++)
+ {
+ /* read and decompress the data */
+ if (this->comp_type == BMP_COMPRESS_RGB)
+ {
+ imBinFileRead(handle, this->line_buffer, this->line_raw_size, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+ }
+ else
+ {
+ if (iBMPDecodeScanLine(handle, (imbyte*)this->line_buffer, this->width) == IM_ERR_ACCESS)
+ return IM_ERR_ACCESS;
+ }
+
+ if (this->bpp > 8)
+ FixRGBOrder();
+
+ imFileLineBufferRead(this, data, row, 0);
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatBMP::WriteImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Writing BMP...");
+
+ imbyte* compressed_buffer = NULL;
+ if (this->comp_type == BMP_COMPRESS_RLE8) // point to the extra buffer
+ compressed_buffer = (imbyte*)this->line_buffer + this->line_buffer_size+4;
+
+ for (int row = 0; row < this->height; row++)
+ {
+ imFileLineBufferWrite(this, data, row, 0);
+
+ if (this->bpp > 8)
+ FixRGBOrder();
+
+ if (this->comp_type == BMP_COMPRESS_RGB)
+ {
+ imBinFileWrite(handle, this->line_buffer, this->line_raw_size, 1);
+ }
+ else
+ {
+ int compressed_size = iBMPEncodeScanLine(compressed_buffer, (imbyte*)this->line_buffer, this->width);
+ imBinFileWrite(handle, compressed_buffer, compressed_size, 1);
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatBMP::CanWrite(const char* compression, int color_mode, int data_type) const
+{
+ int color_space = imColorModeSpace(color_mode);
+
+ if (color_space == IM_YCBCR || color_space == IM_LAB ||
+ color_space == IM_LUV || color_space == IM_XYZ ||
+ color_space == IM_CMYK)
+ return IM_ERR_DATA;
+
+ if (data_type != IM_BYTE)
+ return IM_ERR_DATA;
+
+ if (!compression || compression[0] == 0)
+ return IM_ERR_NONE;
+
+ if (!imStrEqual(compression, "NONE") && !imStrEqual(compression, "RLE"))
+ return IM_ERR_COMPRESS;
+
+ if (imStrEqual(compression, "RLE") && (color_space == IM_RGB || color_space == IM_BINARY))
+ return IM_ERR_COMPRESS;
+
+ return IM_ERR_NONE;
+}