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