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