]> Creatis software - clitk.git/blob - utilities/CxImage/ximabmp.cpp
Debug RTStruct conversion with empty struc
[clitk.git] / utilities / CxImage / ximabmp.cpp
1 /*
2  * File:        ximabmp.cpp
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
6  */
7
8 #include "ximabmp.h"
9
10 #if CXIMAGE_SUPPORT_BMP
11
12 #include "ximaiter.h" 
13
14 ////////////////////////////////////////////////////////////////////////////////
15 #if CXIMAGE_SUPPORT_ENCODE
16 ////////////////////////////////////////////////////////////////////////////////
17 bool CxImageBMP::Encode(CxFile * hFile)
18 {
19
20         if (EncodeSafeCheck(hFile)) return false;
21
22         BITMAPFILEHEADER        hdr;
23
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();
28
29         hdr.bfType = ntohs(hdr.bfType); 
30         hdr.bfSize = ntohl(hdr.bfSize); 
31         hdr.bfOffBits = ntohl(hdr.bfOffBits); 
32
33 #if CXIMAGE_SUPPORT_ALPHA
34         if (GetNumColors()==0 && AlphaIsValid()){
35         
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;
42
43                 hdr.bfSize = infohdr.biSize + infohdr.biSizeImage + 14 /*sizeof(BITMAPFILEHEADER)*/;
44
45                 hdr.bfSize = ntohl(hdr.bfSize);
46                 bihtoh(&infohdr);
47
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);
58                                 srcdib += 3;
59                                 ++srcalpha;
60                         }
61                 }
62
63         } else 
64 #endif //CXIMAGE_SUPPORT_ALPHA
65         {
66                 // Write the file header
67                 hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1);
68                 //copy attributes
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);
74         }
75         return true;
76 }
77 ////////////////////////////////////////////////////////////////////////////////
78 #endif //CXIMAGE_SUPPORT_ENCODE
79 ////////////////////////////////////////////////////////////////////////////////
80 #if CXIMAGE_SUPPORT_DECODE
81 ////////////////////////////////////////////////////////////////////////////////
82 bool CxImageBMP::Decode(CxFile * hFile)
83 {
84         if (hFile == NULL) return false;
85
86         BITMAPFILEHEADER   bf;
87         DWORD off = hFile->Tell(); //<CSC>
88   cx_try {
89         if (hFile->Read(&bf,min(14,sizeof(bf)),1)==0) cx_throw("Not a BMP");
90
91         bf.bfSize = ntohl(bf.bfSize); 
92         bf.bfOffBits = ntohl(bf.bfOffBits); 
93
94     if (bf.bfType != BFT_BITMAP) { //do we have a RC HEADER?
95         bf.bfOffBits = 0L;
96         hFile->Seek(off,SEEK_SET);
97     }
98
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);
104
105         bool bTopDownDib = bmpHeader.biHeight<0; //<Flanders> check if it's a top-down bitmap
106         if (bTopDownDib) bmpHeader.biHeight=-bmpHeader.biHeight;
107
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");
114         }
115
116         if (!Create(bmpHeader.biWidth,bmpHeader.biHeight,bmpHeader.biBitCount,CXIMAGE_FORMAT_BMP))
117                 cx_throw("");
118
119         SetXDPI((long) floor(bmpHeader.biXPelsPerMeter * 254.0 / 10000.0 + 0.5));
120         SetYDPI((long) floor(bmpHeader.biYPelsPerMeter * 254.0 / 10000.0 + 0.5));
121
122         if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding
123
124     RGBQUAD *pRgb = GetPalette();
125     if (pRgb){
126         if (bIsOldBmp){
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;
135             }
136         } else {
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;
140         }
141     }
142
143         if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding
144
145         switch (dwBitCount) {
146                 case 32 :
147                         DWORD bfmask[3];
148                         if (dwCompression == BI_BITFIELDS)
149                         {
150                                 hFile->Read(bfmask, 12, 1);
151                         } else {
152                                 bfmask[0]=0x00FF0000;
153                                 bfmask[1]=0x0000FF00;
154                                 bfmask[2]=0x000000FF;
155                         }
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);
160                                 if (buff32){
161                                         hFile->Read(buff32, imagesize,1); // read in the pixels
162
163 #if CXIMAGE_SUPPORT_ALPHA
164                                         if (dwCompression == BI_RGB){
165                                                 AlphaCreate();
166                                                 if (AlphaIsValid()){
167                                                         bool bAlphaOk = false;
168                                                         BYTE* p;
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;
173                                                                         AlphaSet(x,y,*p);
174                                                                         p+=4;
175                                                                 }
176                                                         }
177                                                         // fix if alpha pixels are all zero
178                                                         if (!bAlphaOk) AlphaInvert();
179                                                 }
180                                         }
181 #endif //CXIMAGE_SUPPORT_ALPHA
182
183                                         Bitfield2RGB(buff32,bfmask[0],bfmask[1],bfmask[2],32);
184                                         free(buff32);
185                                 } else cx_throw("can't allocate memory");
186                         } else cx_throw("unknown compression");
187                         break;
188                 case 24 :
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");
193                         break;
194                 case 16 :
195                 {
196                         DWORD bfmask[3];
197                         if (dwCompression == BI_BITFIELDS)
198                         {
199                                 hFile->Read(bfmask, 12, 1);
200                         } else {
201                                 bfmask[0]=0x7C00; bfmask[1]=0x3E0; bfmask[2]=0x1F; //RGB555
202                         }
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);
209                         break;
210                 }
211                 case 8 :
212                 case 4 :
213                 case 1 :
214                 if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
215                 switch (dwCompression) {
216                         case BI_RGB :
217                                 hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels
218                                 break;
219                         case BI_RLE4 :
220                         {
221                                 BYTE status_byte = 0;
222                                 BYTE second_byte = 0;
223                                 int scanline = 0;
224                                 int bits = 0;
225                                 BOOL low_nibble = FALSE;
226                                 CImageIterator iter(this);
227
228                                 for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(BYTE), 1);) {
229                                         
230                                         switch (status_byte) {
231                                                 case RLE_COMMAND :
232                                                         hFile->Read(&status_byte, sizeof(BYTE), 1);
233                                                         switch (status_byte) {
234                                                                 case RLE_ENDOFLINE :
235                                                                         bits = 0;
236                                                                         scanline++;
237                                                                         low_nibble = FALSE;
238                                                                         break;
239                                                                 case RLE_ENDOFBITMAP :
240                                                                         bContinue=FALSE;
241                                                                         break;
242                                                                 case RLE_DELTA :
243                                                                 {
244                                                                         // read the delta values
245                                                                         BYTE delta_x;
246                                                                         BYTE delta_y;
247                                                                         hFile->Read(&delta_x, sizeof(BYTE), 1);
248                                                                         hFile->Read(&delta_y, sizeof(BYTE), 1);
249                                                                         // apply them
250                                                                         bits       += delta_x / 2;
251                                                                         scanline   += delta_y;
252                                                                         break;
253                                                                 }
254                                                                 default :
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)){
259                                                                                         if (low_nibble) {
260                                                                                                 if (i&1)
261                                                                                                         *(sline + bits) |= (second_byte & 0x0f);
262                                                                                                 else
263                                                                                                         *(sline + bits) |= (second_byte & 0xf0)>>4;
264                                                                                                 bits++;
265                                                                                         } else {
266                                                                                                 if (i&1)
267                                                                                                         *(sline + bits) = (BYTE)(second_byte & 0x0f)<<4;
268                                                                                                 else
269                                                                                                         *(sline + bits) = (BYTE)(second_byte & 0xf0);
270                                                                                         }
271                                                                                 }
272
273                                                                                 if ((i & 1) && (i != (status_byte - 1)))
274                                                                                         hFile->Read(&second_byte, sizeof(BYTE), 1);
275
276                                                                                 low_nibble = !low_nibble;
277                                                                         }
278                                                                         if ((((status_byte+1) >> 1) & 1 ) == 1)
279                                                                                 hFile->Read(&second_byte, sizeof(BYTE), 1);                                                                                             
280                                                                         break;
281                                                         };
282                                                         break;
283                                                 default :
284                                                 {
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)){
289                                                                         if (low_nibble) {
290                                                                                 if (i&1)
291                                                                                         *(sline + bits) |= (second_byte & 0x0f);
292                                                                                 else
293                                                                                         *(sline + bits) |= (second_byte & 0xf0)>>4;
294                                                                                 bits++;
295                                                                         } else {
296                                                                                 if (i&1)
297                                                                                         *(sline + bits) = (BYTE)(second_byte & 0x0f)<<4;
298                                                                                 else
299                                                                                         *(sline + bits) = (BYTE)(second_byte & 0xf0);
300                                                                         }
301                                                                 }
302                                                                 low_nibble = !low_nibble;
303                                                         }
304                                                 }
305                                                 break;
306                                         };
307                                 }
308                                 break;
309                         }
310                         case BI_RLE8 :
311                         {
312                                 BYTE status_byte = 0;
313                                 BYTE second_byte = 0;
314                                 int scanline = 0;
315                                 int bits = 0;
316                                 CImageIterator iter(this);
317
318                                 for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(BYTE), 1);) {
319                                         switch (status_byte) {
320                                                 case RLE_COMMAND :
321                                                         hFile->Read(&status_byte, sizeof(BYTE), 1);
322                                                         switch (status_byte) {
323                                                                 case RLE_ENDOFLINE :
324                                                                         bits = 0;
325                                                                         scanline++;
326                                                                         break;
327                                                                 case RLE_ENDOFBITMAP :
328                                                                         bContinue=FALSE;
329                                                                         break;
330                                                                 case RLE_DELTA :
331                                                                 {
332                                                                         // read the delta values
333                                                                         BYTE delta_x;
334                                                                         BYTE delta_y;
335                                                                         hFile->Read(&delta_x, sizeof(BYTE), 1);
336                                                                         hFile->Read(&delta_y, sizeof(BYTE), 1);
337                                                                         // apply them
338                                                                         bits     += delta_x;
339                                                                         scanline += delta_y;
340                                                                         break;
341                                                                 }
342                                                                 default :
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);                                                                                             
347                                                                         bits += status_byte;                                                                                                    
348                                                                         break;                                                          
349                                                         };
350                                                         break;
351                                                 default :
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;
357                                                                         bits++;                                 
358                                                                 } else {
359                                                                         break;
360                                                                 }
361                                                         }
362                                                         break;
363                                         };
364                                 }
365                                 break;
366                         }
367                         default :                                                               
368                                 cx_throw("compression type not supported");
369                 }
370         }
371
372         if (bTopDownDib) Flip(); //<Flanders>
373
374   } cx_catch {
375         if (strcmp(message,"")) strncpy(info.szLastError,message,255);
376         if (info.nEscape == -1 && info.dwType == CXIMAGE_FORMAT_BMP) return true;
377         return false;
378   }
379     return true;
380 }
381 ////////////////////////////////////////////////////////////////////////////////
382 /*  ReadDibBitmapInfo()
383  *
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.
387  */
388 bool CxImageBMP::DibReadBitmapInfo(CxFile* fh, BITMAPINFOHEADER *pdib)
389 {
390         if ((fh==NULL)||(pdib==NULL)) return false;
391
392     if (fh->Read(pdib,sizeof(BITMAPINFOHEADER),1)==0) return false;
393
394         bihtoh(pdib);
395
396     switch (pdib->biSize) // what type of bitmap info is this?
397     {
398         case sizeof(BITMAPINFOHEADER):
399             break;
400
401                 case 64: //sizeof(OS2_BMP_HEADER):
402             fh->Seek((long)(64 - sizeof(BITMAPINFOHEADER)),SEEK_CUR);
403                         break;
404
405         case sizeof(BITMAPCOREHEADER):
406                 {
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;
417             pdib->biClrUsed            = 0;
418             pdib->biClrImportant       = 0;
419
420                         fh->Seek((long)(sizeof(BITMAPCOREHEADER)-sizeof(BITMAPINFOHEADER)), SEEK_CUR);
421                 }
422             break;
423         default:
424                         //give a last chance
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))
428                          {
429                      if (pdib->biCompression==BI_RGB)
430                                          fh->Seek((long)(pdib->biSize - sizeof(BITMAPINFOHEADER)),SEEK_CUR);
431                                  break;
432                          }
433                         return false;
434     }
435
436     FixBitmapInfo(pdib);
437
438     return true;
439 }
440 ////////////////////////////////////////////////////////////////////////////////
441 #endif //CXIMAGE_SUPPORT_DECODE
442 ////////////////////////////////////////////////////////////////////////////////
443 #endif  // CXIMAGE_SUPPORT_BMP
444 ////////////////////////////////////////////////////////////////////////////////