3 * Purpose: Platform Independent BMP Image Class Loader and Writer
\r
4 * 07/Aug/2001 Davide Pizzolato - www.xdp.it
\r
5 * CxImage version 6.0.0 02/Feb/2008
\r
10 #if CXIMAGE_SUPPORT_BMP
\r
12 #include "ximaiter.h"
\r
14 ////////////////////////////////////////////////////////////////////////////////
\r
15 #if CXIMAGE_SUPPORT_ENCODE
\r
16 ////////////////////////////////////////////////////////////////////////////////
\r
17 bool CxImageBMP::Encode(CxFile * hFile)
\r
20 if (EncodeSafeCheck(hFile)) return false;
\r
22 BITMAPFILEHEADER hdr;
\r
24 hdr.bfType = 0x4d42; // 'BM' WINDOWS_BITMAP_SIGNATURE
\r
25 hdr.bfSize = GetSize() + 14 /*sizeof(BITMAPFILEHEADER)*/;
\r
26 hdr.bfReserved1 = hdr.bfReserved2 = 0;
\r
27 hdr.bfOffBits = 14 /*sizeof(BITMAPFILEHEADER)*/ + head.biSize + GetPaletteSize();
\r
29 hdr.bfType = ntohs(hdr.bfType);
\r
30 hdr.bfSize = ntohl(hdr.bfSize);
\r
31 hdr.bfOffBits = ntohl(hdr.bfOffBits);
\r
33 #if CXIMAGE_SUPPORT_ALPHA
\r
34 if (GetNumColors()==0 && AlphaIsValid()){
\r
36 BITMAPINFOHEADER infohdr;
\r
37 memcpy(&infohdr,&head,sizeof(BITMAPINFOHEADER));
\r
38 infohdr.biCompression = BI_RGB;
\r
39 infohdr.biBitCount = 32;
\r
40 DWORD dwEffWidth = ((((infohdr.biBitCount * infohdr.biWidth) + 31) / 32) * 4);
\r
41 infohdr.biSizeImage = dwEffWidth * infohdr.biHeight;
\r
43 hdr.bfSize = infohdr.biSize + infohdr.biSizeImage + 14 /*sizeof(BITMAPFILEHEADER)*/;
\r
45 hdr.bfSize = ntohl(hdr.bfSize);
\r
48 // Write the file header
\r
49 hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1);
\r
50 hFile->Write(&infohdr,sizeof(BITMAPINFOHEADER),1);
\r
51 //and DIB+ALPHA interlaced
\r
52 BYTE *srcalpha = AlphaGetPointer();
\r
53 for(long y = 0; y < infohdr.biHeight; ++y){
\r
54 BYTE *srcdib = GetBits(y);
\r
55 for(long x = 0; x < infohdr.biWidth; ++x){
\r
56 hFile->Write(srcdib,3,1);
\r
57 hFile->Write(srcalpha,1,1);
\r
64 #endif //CXIMAGE_SUPPORT_ALPHA
\r
66 // Write the file header
\r
67 hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1);
\r
69 memcpy(pDib,&head,sizeof(BITMAPINFOHEADER));
\r
70 bihtoh((BITMAPINFOHEADER*)pDib);
\r
71 // Write the DIB header and the pixels
\r
72 hFile->Write(pDib,GetSize(),1);
\r
73 bihtoh((BITMAPINFOHEADER*)pDib);
\r
77 ////////////////////////////////////////////////////////////////////////////////
\r
78 #endif //CXIMAGE_SUPPORT_ENCODE
\r
79 ////////////////////////////////////////////////////////////////////////////////
\r
80 #if CXIMAGE_SUPPORT_DECODE
\r
81 ////////////////////////////////////////////////////////////////////////////////
\r
82 bool CxImageBMP::Decode(CxFile * hFile)
\r
84 if (hFile == NULL) return false;
\r
86 BITMAPFILEHEADER bf;
\r
87 DWORD off = hFile->Tell(); //<CSC>
\r
89 if (hFile->Read(&bf,min(14,sizeof(bf)),1)==0) cx_throw("Not a BMP");
\r
91 bf.bfSize = ntohl(bf.bfSize);
\r
92 bf.bfOffBits = ntohl(bf.bfOffBits);
\r
94 if (bf.bfType != BFT_BITMAP) { //do we have a RC HEADER?
\r
96 hFile->Seek(off,SEEK_SET);
\r
99 BITMAPINFOHEADER bmpHeader;
\r
100 if (!DibReadBitmapInfo(hFile,&bmpHeader)) cx_throw("Error reading BMP info");
\r
101 DWORD dwCompression=bmpHeader.biCompression;
\r
102 DWORD dwBitCount=bmpHeader.biBitCount; //preserve for BI_BITFIELDS compression <Thomas Ernst>
\r
103 bool bIsOldBmp = bmpHeader.biSize == sizeof(BITMAPCOREHEADER);
\r
105 bool bTopDownDib = bmpHeader.biHeight<0; //<Flanders> check if it's a top-down bitmap
\r
106 if (bTopDownDib) bmpHeader.biHeight=-bmpHeader.biHeight;
\r
108 if (info.nEscape == -1) {
\r
109 // Return output dimensions only
\r
110 head.biWidth = bmpHeader.biWidth;
\r
111 head.biHeight = bmpHeader.biHeight;
\r
112 info.dwType = CXIMAGE_FORMAT_BMP;
\r
113 cx_throw("output dimensions returned");
\r
116 if (!Create(bmpHeader.biWidth,bmpHeader.biHeight,bmpHeader.biBitCount,CXIMAGE_FORMAT_BMP))
\r
119 SetXDPI((long) floor(bmpHeader.biXPelsPerMeter * 254.0 / 10000.0 + 0.5));
\r
120 SetYDPI((long) floor(bmpHeader.biYPelsPerMeter * 254.0 / 10000.0 + 0.5));
\r
122 if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding
\r
124 RGBQUAD *pRgb = GetPalette();
\r
127 // convert a old color table (3 byte entries) to a new
\r
128 // color table (4 byte entries)
\r
129 hFile->Read((void*)pRgb,DibNumColors(&bmpHeader) * sizeof(RGBTRIPLE),1);
\r
130 for (int i=DibNumColors(&head)-1; i>=0; i--){
\r
131 pRgb[i].rgbRed = ((RGBTRIPLE *)pRgb)[i].rgbtRed;
\r
132 pRgb[i].rgbBlue = ((RGBTRIPLE *)pRgb)[i].rgbtBlue;
\r
133 pRgb[i].rgbGreen = ((RGBTRIPLE *)pRgb)[i].rgbtGreen;
\r
134 pRgb[i].rgbReserved = (BYTE)0;
\r
137 hFile->Read((void*)pRgb,DibNumColors(&bmpHeader) * sizeof(RGBQUAD),1);
\r
138 //force rgbReserved=0, to avoid problems with some WinXp bitmaps
\r
139 for (unsigned int i=0; i<head.biClrUsed; i++) pRgb[i].rgbReserved=0;
\r
143 if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding
\r
145 switch (dwBitCount) {
\r
148 if (dwCompression == BI_BITFIELDS)
\r
150 hFile->Read(bfmask, 12, 1);
\r
152 bfmask[0]=0x00FF0000;
\r
153 bfmask[1]=0x0000FF00;
\r
154 bfmask[2]=0x000000FF;
\r
156 if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
\r
157 if (dwCompression == BI_BITFIELDS || dwCompression == BI_RGB){
\r
158 long imagesize=4*head.biHeight*head.biWidth;
\r
159 BYTE* buff32=(BYTE*)malloc(imagesize);
\r
161 hFile->Read(buff32, imagesize,1); // read in the pixels
\r
163 #if CXIMAGE_SUPPORT_ALPHA
\r
164 if (dwCompression == BI_RGB){
\r
166 if (AlphaIsValid()){
\r
167 bool bAlphaOk = false;
\r
169 for (long y=0; y<head.biHeight; y++){
\r
170 p = buff32 + 3 + head.biWidth * 4 * y;
\r
171 for (long x=0; x<head.biWidth; x++){
\r
172 if (*p) bAlphaOk = true;
\r
177 // fix if alpha pixels are all zero
\r
178 if (!bAlphaOk) AlphaInvert();
\r
181 #endif //CXIMAGE_SUPPORT_ALPHA
\r
183 Bitfield2RGB(buff32,bfmask[0],bfmask[1],bfmask[2],32);
\r
185 } else cx_throw("can't allocate memory");
\r
186 } else cx_throw("unknown compression");
\r
189 if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
\r
190 if (dwCompression == BI_RGB){
\r
191 hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels
\r
192 } else cx_throw("unknown compression");
\r
197 if (dwCompression == BI_BITFIELDS)
\r
199 hFile->Read(bfmask, 12, 1);
\r
201 bfmask[0]=0x7C00; bfmask[1]=0x3E0; bfmask[2]=0x1F; //RGB555
\r
203 // bf.bfOffBits required after the bitfield mask <Cui Ying Jie>
\r
204 if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
\r
205 // read in the pixels
\r
206 hFile->Read(info.pImage, head.biHeight*((head.biWidth+1)/2)*4,1);
\r
207 // transform into RGB
\r
208 Bitfield2RGB(info.pImage,bfmask[0],bfmask[1],bfmask[2],16);
\r
214 if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
\r
215 switch (dwCompression) {
\r
217 hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels
\r
221 BYTE status_byte = 0;
\r
222 BYTE second_byte = 0;
\r
225 BOOL low_nibble = FALSE;
\r
226 CImageIterator iter(this);
\r
228 for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(BYTE), 1);) {
\r
230 switch (status_byte) {
\r
232 hFile->Read(&status_byte, sizeof(BYTE), 1);
\r
233 switch (status_byte) {
\r
234 case RLE_ENDOFLINE :
\r
237 low_nibble = FALSE;
\r
239 case RLE_ENDOFBITMAP :
\r
244 // read the delta values
\r
247 hFile->Read(&delta_x, sizeof(BYTE), 1);
\r
248 hFile->Read(&delta_y, sizeof(BYTE), 1);
\r
250 bits += delta_x / 2;
\r
251 scanline += delta_y;
\r
255 hFile->Read(&second_byte, sizeof(BYTE), 1);
\r
256 BYTE *sline = iter.GetRow(scanline);
\r
257 for (int i = 0; i < status_byte; i++) {
\r
258 if ((BYTE*)(sline+bits) < (BYTE*)(info.pImage+head.biSizeImage)){
\r
261 *(sline + bits) |= (second_byte & 0x0f);
\r
263 *(sline + bits) |= (second_byte & 0xf0)>>4;
\r
267 *(sline + bits) = (BYTE)(second_byte & 0x0f)<<4;
\r
269 *(sline + bits) = (BYTE)(second_byte & 0xf0);
\r
273 if ((i & 1) && (i != (status_byte - 1)))
\r
274 hFile->Read(&second_byte, sizeof(BYTE), 1);
\r
276 low_nibble = !low_nibble;
\r
278 if ((((status_byte+1) >> 1) & 1 ) == 1)
\r
279 hFile->Read(&second_byte, sizeof(BYTE), 1);
\r
285 BYTE *sline = iter.GetRow(scanline);
\r
286 hFile->Read(&second_byte, sizeof(BYTE), 1);
\r
287 for (unsigned i = 0; i < status_byte; i++) {
\r
288 if ((BYTE*)(sline+bits) < (BYTE*)(info.pImage+head.biSizeImage)){
\r
291 *(sline + bits) |= (second_byte & 0x0f);
\r
293 *(sline + bits) |= (second_byte & 0xf0)>>4;
\r
297 *(sline + bits) = (BYTE)(second_byte & 0x0f)<<4;
\r
299 *(sline + bits) = (BYTE)(second_byte & 0xf0);
\r
302 low_nibble = !low_nibble;
\r
312 BYTE status_byte = 0;
\r
313 BYTE second_byte = 0;
\r
316 CImageIterator iter(this);
\r
318 for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(BYTE), 1);) {
\r
319 switch (status_byte) {
\r
321 hFile->Read(&status_byte, sizeof(BYTE), 1);
\r
322 switch (status_byte) {
\r
323 case RLE_ENDOFLINE :
\r
327 case RLE_ENDOFBITMAP :
\r
332 // read the delta values
\r
335 hFile->Read(&delta_x, sizeof(BYTE), 1);
\r
336 hFile->Read(&delta_y, sizeof(BYTE), 1);
\r
339 scanline += delta_y;
\r
343 hFile->Read((void *)(iter.GetRow(scanline) + bits), sizeof(BYTE) * status_byte, 1);
\r
344 // align run length to even number of bytes
\r
345 if ((status_byte & 1) == 1)
\r
346 hFile->Read(&second_byte, sizeof(BYTE), 1);
\r
347 bits += status_byte;
\r
352 BYTE *sline = iter.GetRow(scanline);
\r
353 hFile->Read(&second_byte, sizeof(BYTE), 1);
\r
354 for (unsigned i = 0; i < status_byte; i++) {
\r
355 if ((DWORD)bits<info.dwEffWidth){
\r
356 *(sline + bits) = second_byte;
\r
368 cx_throw("compression type not supported");
\r
372 if (bTopDownDib) Flip(); //<Flanders>
\r
375 if (strcmp(message,"")) strncpy(info.szLastError,message,255);
\r
376 if (info.nEscape == -1 && info.dwType == CXIMAGE_FORMAT_BMP) return true;
\r
381 ////////////////////////////////////////////////////////////////////////////////
\r
382 /* ReadDibBitmapInfo()
\r
384 * Will read a file in DIB format and return a global HANDLE to its
\r
385 * BITMAPINFO. This function will work with both "old" and "new"
\r
386 * bitmap formats, but will always return a "new" BITMAPINFO.
\r
388 bool CxImageBMP::DibReadBitmapInfo(CxFile* fh, BITMAPINFOHEADER *pdib)
\r
390 if ((fh==NULL)||(pdib==NULL)) return false;
\r
392 if (fh->Read(pdib,sizeof(BITMAPINFOHEADER),1)==0) return false;
\r
396 switch (pdib->biSize) // what type of bitmap info is this?
\r
398 case sizeof(BITMAPINFOHEADER):
\r
401 case 64: //sizeof(OS2_BMP_HEADER):
\r
402 fh->Seek((long)(64 - sizeof(BITMAPINFOHEADER)),SEEK_CUR);
\r
405 case sizeof(BITMAPCOREHEADER):
\r
407 BITMAPCOREHEADER bc = *(BITMAPCOREHEADER*)pdib;
\r
408 pdib->biSize = bc.bcSize;
\r
409 pdib->biWidth = (DWORD)bc.bcWidth;
\r
410 pdib->biHeight = (DWORD)bc.bcHeight;
\r
411 pdib->biPlanes = bc.bcPlanes;
\r
412 pdib->biBitCount = bc.bcBitCount;
\r
413 pdib->biCompression = BI_RGB;
\r
414 pdib->biSizeImage = 0;
\r
415 pdib->biXPelsPerMeter = 0;
\r
416 pdib->biYPelsPerMeter = 0;
\r
417 pdib->biClrUsed = 0;
\r
418 pdib->biClrImportant = 0;
\r
420 fh->Seek((long)(sizeof(BITMAPCOREHEADER)-sizeof(BITMAPINFOHEADER)), SEEK_CUR);
\r
424 //give a last chance
\r
425 if (pdib->biSize>(sizeof(BITMAPINFOHEADER))&&
\r
426 (pdib->biSizeImage>=(unsigned long)(pdib->biHeight*((((pdib->biBitCount*pdib->biWidth)+31)/32)*4)))&&
\r
427 (pdib->biPlanes==1)&&(pdib->biClrUsed==0))
\r
429 if (pdib->biCompression==BI_RGB)
\r
430 fh->Seek((long)(pdib->biSize - sizeof(BITMAPINFOHEADER)),SEEK_CUR);
\r
436 FixBitmapInfo(pdib);
\r
440 ////////////////////////////////////////////////////////////////////////////////
\r
441 #endif //CXIMAGE_SUPPORT_DECODE
\r
442 ////////////////////////////////////////////////////////////////////////////////
\r
443 #endif // CXIMAGE_SUPPORT_BMP
\r
444 ////////////////////////////////////////////////////////////////////////////////
\r