]> Creatis software - clitk.git/blob - utilities/CxImage/ximapcx.cpp
0d7e0a862465109064c5001675549f4b75ea44ff
[clitk.git] / utilities / CxImage / ximapcx.cpp
1 /*\r
2  * File:        ximapcx.cpp\r
3  * Purpose:     Platform Independent PCX Image Class Loader and Writer\r
4  * 05/Jan/2002 Davide Pizzolato - www.xdp.it\r
5  * CxImage version 6.0.0 02/Feb/2008\r
6  *\r
7  * based on ppmtopcx.c - convert a portable pixmap to PCX\r
8  * Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)\r
9  * based on ppmtopcx.c by Michael Davidson\r
10  */\r
11 \r
12 #include "ximapcx.h"\r
13 \r
14 #if CXIMAGE_SUPPORT_PCX\r
15 \r
16 #include "xmemfile.h"\r
17 \r
18 #define PCX_MAGIC 0X0A  // PCX magic number\r
19 #define PCX_256_COLORS 0X0C  // magic number for 256 colors\r
20 #define PCX_HDR_SIZE 128  // size of PCX header\r
21 #define PCX_MAXCOLORS 256\r
22 #define PCX_MAXPLANES 4\r
23 #define PCX_MAXVAL 255\r
24 \r
25 ////////////////////////////////////////////////////////////////////////////////\r
26 #if CXIMAGE_SUPPORT_DECODE\r
27 ////////////////////////////////////////////////////////////////////////////////\r
28 bool CxImagePCX::Decode(CxFile *hFile)\r
29 {\r
30         if (hFile == NULL) return false;\r
31 \r
32         PCXHEADER pcxHeader;\r
33         int i, x, y, y2, nbytes, count, Height, Width;\r
34         BYTE c, ColorMap[PCX_MAXCOLORS][3];\r
35         BYTE *pcximage = NULL, *lpHead1 = NULL, *lpHead2 = NULL;\r
36         BYTE *pcxplanes, *pcxpixels;\r
37 \r
38   cx_try\r
39   {\r
40         if (hFile->Read(&pcxHeader,sizeof(PCXHEADER),1)==0) cx_throw("Can't read PCX image");\r
41 \r
42         PCX_toh(&pcxHeader);\r
43 \r
44     if (pcxHeader.Manufacturer != PCX_MAGIC) cx_throw("Error: Not a PCX file");\r
45     // Check for PCX run length encoding\r
46     if (pcxHeader.Encoding != 1) cx_throw("PCX file has unknown encoding scheme");\r
47  \r
48     Width = (pcxHeader.Xmax - pcxHeader.Xmin) + 1;\r
49     Height = (pcxHeader.Ymax - pcxHeader.Ymin) + 1;\r
50         info.xDPI = pcxHeader.Hres;\r
51         info.yDPI = pcxHeader.Vres;\r
52 \r
53         if (info.nEscape == -1){\r
54                 head.biWidth = Width;\r
55                 head.biHeight= Height;\r
56                 info.dwType = CXIMAGE_FORMAT_PCX;\r
57                 return true;\r
58         }\r
59 \r
60     // Check that we can handle this image format\r
61     if (pcxHeader.ColorPlanes > 4)\r
62                 cx_throw("Can't handle image with more than 4 planes");\r
63 \r
64         // Create the image\r
65         if (pcxHeader.ColorPlanes >= 3 && pcxHeader.BitsPerPixel == 8){\r
66                 Create (Width, Height, 24, CXIMAGE_FORMAT_PCX);\r
67 #if CXIMAGE_SUPPORT_ALPHA\r
68                 if (pcxHeader.ColorPlanes==4) AlphaCreate();\r
69 #endif //CXIMAGE_SUPPORT_ALPHA\r
70         } else if (pcxHeader.ColorPlanes == 4 && pcxHeader.BitsPerPixel == 1)\r
71                 Create (Width, Height, 4, CXIMAGE_FORMAT_PCX);\r
72         else\r
73                 Create (Width, Height, pcxHeader.BitsPerPixel, CXIMAGE_FORMAT_PCX);\r
74 \r
75         if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding\r
76 \r
77         //Read the image and check if it's ok\r
78     nbytes = pcxHeader.BytesPerLine * pcxHeader.ColorPlanes * Height;\r
79     lpHead1 = pcximage = (BYTE*)malloc(nbytes);\r
80     while (nbytes > 0){\r
81                 if (hFile == NULL || hFile->Eof()) cx_throw("corrupted PCX");\r
82 \r
83                 hFile->Read(&c,1,1);\r
84                 if ((c & 0XC0) != 0XC0){ // Repeated group\r
85                         *pcximage++ = c;\r
86                         --nbytes;\r
87                         continue;\r
88                 }\r
89                 count = c & 0X3F; // extract count\r
90                 hFile->Read(&c,1,1);\r
91                 if (count > nbytes) cx_throw("repeat count spans end of image");\r
92 \r
93                 nbytes -= count;\r
94                 while (--count >=0) *pcximage++ = c;\r
95         }\r
96     pcximage = lpHead1;\r
97 \r
98         //store the palette\r
99     for (i = 0; i < 16; i++){\r
100                 ColorMap[i][0] = pcxHeader.ColorMap[i][0];\r
101                 ColorMap[i][1] = pcxHeader.ColorMap[i][1];\r
102                 ColorMap[i][2] = pcxHeader.ColorMap[i][2];\r
103         }\r
104     if (pcxHeader.BitsPerPixel == 8 && pcxHeader.ColorPlanes == 1){\r
105                 hFile->Read(&c,1,1);\r
106                 if (c != PCX_256_COLORS) cx_throw("bad color map signature");\r
107                 \r
108                 for (i = 0; i < PCX_MAXCOLORS; i++){\r
109                         hFile->Read(&ColorMap[i][0],1,1);\r
110                         hFile->Read(&ColorMap[i][1],1,1);\r
111                         hFile->Read(&ColorMap[i][2],1,1);\r
112                 }\r
113         }\r
114     if (pcxHeader.BitsPerPixel == 1 && pcxHeader.ColorPlanes == 1){\r
115                 ColorMap[0][0] = ColorMap[0][1] = ColorMap[0][2] = 0;\r
116                 ColorMap[1][0] = ColorMap[1][1] = ColorMap[1][2] = 255;\r
117         }\r
118 \r
119         for (DWORD idx=0; idx<head.biClrUsed; idx++) SetPaletteColor((BYTE)idx,ColorMap[idx][0],ColorMap[idx][1],ColorMap[idx][2]);\r
120 \r
121     lpHead2 = pcxpixels = (BYTE *)malloc(Width + pcxHeader.BytesPerLine * 8);\r
122     // Convert the image\r
123     for (y = 0; y < Height; y++){\r
124 \r
125                 if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding\r
126 \r
127                 y2=Height-1-y;\r
128                 pcxpixels = lpHead2;\r
129                 pcxplanes = pcximage + (y * pcxHeader.BytesPerLine * pcxHeader.ColorPlanes);\r
130 \r
131                 if (pcxHeader.ColorPlanes == 3 && pcxHeader.BitsPerPixel == 8){\r
132                         // Deal with 24 bit color image\r
133                         for (x = 0; x < Width; x++){\r
134                                 SetPixelColor(x,y2,RGB(pcxplanes[x],pcxplanes[pcxHeader.BytesPerLine + x],pcxplanes[2*pcxHeader.BytesPerLine + x]));\r
135                         }\r
136                         continue;\r
137 #if CXIMAGE_SUPPORT_ALPHA\r
138                 } else if (pcxHeader.ColorPlanes == 4 && pcxHeader.BitsPerPixel == 8){\r
139                         for (x = 0; x < Width; x++){\r
140                                 SetPixelColor(x,y2,RGB(pcxplanes[x],pcxplanes[pcxHeader.BytesPerLine + x],pcxplanes[2*pcxHeader.BytesPerLine + x]));\r
141                                 AlphaSet(x,y2,pcxplanes[3*pcxHeader.BytesPerLine + x]);\r
142                         }\r
143                         continue;\r
144 #endif //CXIMAGE_SUPPORT_ALPHA\r
145                 } else if (pcxHeader.ColorPlanes == 1) {\r
146                         if (!PCX_UnpackPixels(pcxpixels, pcxplanes, pcxHeader.BytesPerLine, pcxHeader.ColorPlanes, pcxHeader.BitsPerPixel)){\r
147                                 cx_throw("PCX_UnpackPixels: Can't handle packed pixels with more than 1 plane");\r
148                         }\r
149                 } else {\r
150                         if (!PCX_PlanesToPixels(pcxpixels, pcxplanes, pcxHeader.BytesPerLine, pcxHeader.ColorPlanes, pcxHeader.BitsPerPixel)){\r
151                                 cx_throw("PCX_PlanesToPixels: more than 4 planes or more than 1 bit per pixel");\r
152                         }\r
153                 }\r
154                 for (x = 0; x < Width; x++)     SetPixelIndex(x,y2,pcxpixels[x]);\r
155         }\r
156 \r
157   } cx_catch {\r
158         if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
159         if (lpHead1){ free(lpHead1); lpHead1 = NULL; }\r
160     if (lpHead2){ free(lpHead2); lpHead2 = NULL; }\r
161         return false;\r
162   }\r
163         if (lpHead1){ free(lpHead1); lpHead1 = NULL; }\r
164     if (lpHead2){ free(lpHead2); lpHead2 = NULL; }\r
165         return true;\r
166 }\r
167 ////////////////////////////////////////////////////////////////////////////////\r
168 #endif //CXIMAGE_SUPPORT_DECODE\r
169 ////////////////////////////////////////////////////////////////////////////////\r
170 #if CXIMAGE_SUPPORT_ENCODE\r
171 ////////////////////////////////////////////////////////////////////////////////\r
172 bool CxImagePCX::Encode(CxFile * hFile)\r
173 {\r
174         if (EncodeSafeCheck(hFile)) return false;\r
175 \r
176   cx_try\r
177   {\r
178         PCXHEADER pcxHeader;\r
179         memset(&pcxHeader,0,sizeof(pcxHeader));\r
180         pcxHeader.Manufacturer = PCX_MAGIC;\r
181         pcxHeader.Version = 5;\r
182         pcxHeader.Encoding = 1;\r
183         pcxHeader.Xmin = 0;\r
184         pcxHeader.Ymin = 0;\r
185         pcxHeader.Xmax = (WORD)head.biWidth-1;\r
186         pcxHeader.Ymax = (WORD)head.biHeight-1;\r
187         pcxHeader.Hres = (WORD)info.xDPI;\r
188         pcxHeader.Vres = (WORD)info.yDPI;\r
189         pcxHeader.Reserved = 0;\r
190         pcxHeader.PaletteType = head.biClrUsed==0;\r
191 \r
192         switch(head.biBitCount){\r
193         case 24:\r
194         case 8:\r
195                 {\r
196                         pcxHeader.BitsPerPixel = 8;\r
197                         pcxHeader.ColorPlanes = head.biClrUsed==0 ? 3 : 1;\r
198 #if CXIMAGE_SUPPORT_ALPHA\r
199                         if (AlphaIsValid() && head.biClrUsed==0) pcxHeader.ColorPlanes =4;\r
200 #endif //CXIMAGE_SUPPORT_ALPHA\r
201                         pcxHeader.BytesPerLine = (WORD)head.biWidth;\r
202                         break;\r
203                 }\r
204         default: //(4 1)\r
205                 pcxHeader.BitsPerPixel = 1;\r
206                 pcxHeader.ColorPlanes = head.biClrUsed==16 ? 4 : 1;\r
207                 pcxHeader.BytesPerLine = (WORD)((head.biWidth * pcxHeader.BitsPerPixel + 7)>>3);\r
208         }\r
209 \r
210     if (pcxHeader.BitsPerPixel == 1 && pcxHeader.ColorPlanes == 1){\r
211                 pcxHeader.ColorMap[0][0] = pcxHeader.ColorMap[0][1] = pcxHeader.ColorMap[0][2] = 0;\r
212                 pcxHeader.ColorMap[1][0] = pcxHeader.ColorMap[1][1] = pcxHeader.ColorMap[1][2] = 255;\r
213         }\r
214         if (pcxHeader.BitsPerPixel == 1 && pcxHeader.ColorPlanes == 4){\r
215                 RGBQUAD c;\r
216                 for (int i = 0; i < 16; i++){\r
217                         c=GetPaletteColor(i);\r
218                         pcxHeader.ColorMap[i][0] = c.rgbRed;\r
219                         pcxHeader.ColorMap[i][1] = c.rgbGreen;\r
220                         pcxHeader.ColorMap[i][2] = c.rgbBlue;\r
221                 }\r
222         }\r
223 \r
224         pcxHeader.BytesPerLine = (pcxHeader.BytesPerLine + 1)&(~1);\r
225 \r
226         PCX_toh(&pcxHeader);\r
227         if (hFile->Write(&pcxHeader, sizeof(pcxHeader), 1) == 0 )\r
228            cx_throw("cannot write PCX header");\r
229         PCX_toh(&pcxHeader);\r
230 \r
231         CxMemFile buffer;\r
232         buffer.Open();\r
233 \r
234         BYTE c,n;\r
235         long x,y;\r
236         if (head.biClrUsed==0){\r
237                 for (y = head.biHeight-1; y >=0 ; y--){\r
238                         for (int p=0; p<pcxHeader.ColorPlanes; p++){\r
239                                 c=n=0;\r
240                                 for (x = 0; x<head.biWidth; x++){\r
241                                         if (p==0)\r
242                                                 PCX_PackPixels(BlindGetPixelColor(x,y).rgbRed,c,n,buffer);\r
243                                         else if (p==1)\r
244                                                 PCX_PackPixels(BlindGetPixelColor(x,y).rgbGreen,c,n,buffer);\r
245                                         else if (p==2)\r
246                                                 PCX_PackPixels(BlindGetPixelColor(x,y).rgbBlue,c,n,buffer);\r
247 #if CXIMAGE_SUPPORT_ALPHA\r
248                                         else if (p==3)\r
249                                                 PCX_PackPixels(BlindAlphaGet(x,y),c,n,buffer);\r
250 #endif //CXIMAGE_SUPPORT_ALPHA\r
251                                 }\r
252                                 PCX_PackPixels(-1-(head.biWidth&0x1),c,n,buffer);\r
253                         }\r
254                 }\r
255 \r
256                 hFile->Write(buffer.GetBuffer(false),buffer.Tell(),1);\r
257 \r
258         } else if (head.biBitCount==8) {\r
259 \r
260                 for (y = head.biHeight-1; y >=0 ; y--){\r
261                         c=n=0;\r
262                         for (x = 0; x<head.biWidth; x++){\r
263                                 PCX_PackPixels(GetPixelIndex(x,y),c,n,buffer);\r
264                         }\r
265                         PCX_PackPixels(-1-(head.biWidth&0x1),c,n,buffer);\r
266                 }\r
267 \r
268                 hFile->Write(buffer.GetBuffer(false),buffer.Tell(),1);\r
269 \r
270                 if (head.biBitCount == 8){\r
271                         hFile->PutC(0x0C);\r
272                         BYTE* pal = (BYTE*)malloc(768);\r
273                         RGBQUAD c;\r
274                         for (int i=0;i<256;i++){\r
275                                 c=GetPaletteColor(i);\r
276                                 pal[3*i+0] = c.rgbRed;\r
277                                 pal[3*i+1] = c.rgbGreen;\r
278                                 pal[3*i+2] = c.rgbBlue;\r
279                         }\r
280                         hFile->Write(pal,768,1);\r
281                         free(pal);\r
282                 }\r
283         } else { //(head.biBitCount==4) || (head.biBitCount==1)\r
284 \r
285                 RGBQUAD *rgb = GetPalette();\r
286                 bool binvert = false;\r
287                 if (CompareColors(&rgb[0],&rgb[1])>0) binvert=(head.biBitCount==1);\r
288                 \r
289                 BYTE* plane = (BYTE*)malloc(pcxHeader.BytesPerLine);\r
290                 BYTE* raw = (BYTE*)malloc(head.biWidth);\r
291 \r
292                 for(y = head.biHeight-1; y >=0 ; y--) {\r
293 \r
294                         for( x = 0; x < head.biWidth; x++)      raw[x] = (BYTE)GetPixelIndex(x,y);\r
295 \r
296                         if (binvert) for( x = 0; x < head.biWidth; x++) raw[x] = 1-raw[x];\r
297 \r
298                         for( x = 0; x < pcxHeader.ColorPlanes; x++ ) {\r
299                                 PCX_PixelsToPlanes(raw, head.biWidth, plane, x);\r
300                                 PCX_PackPlanes(plane, pcxHeader.BytesPerLine, buffer);\r
301                         }\r
302                 }\r
303 \r
304                 free(plane);\r
305                 free(raw);\r
306 \r
307                 hFile->Write(buffer.GetBuffer(false),buffer.Tell(),1);\r
308 \r
309         }\r
310 \r
311   } cx_catch {\r
312         if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
313         return false;\r
314   }\r
315     return true;\r
316 }\r
317 ////////////////////////////////////////////////////////////////////////////////\r
318 #endif // CXIMAGE_SUPPORT_ENCODE\r
319 ////////////////////////////////////////////////////////////////////////////////\r
320 // Convert multi-plane format into 1 pixel per byte\r
321 // from unpacked file data bitplanes[] into pixel row pixels[]\r
322 // image Height rows, with each row having planes image planes each\r
323 // bytesperline bytes\r
324 bool CxImagePCX::PCX_PlanesToPixels(BYTE * pixels, BYTE * bitplanes, short bytesperline, short planes, short bitsperpixel)\r
325 {\r
326         int i, j, npixels;\r
327         BYTE * p;\r
328         if (planes > 4) return false;\r
329         if (bitsperpixel != 1) return false;\r
330 \r
331         // Clear the pixel buffer\r
332         npixels = (bytesperline * 8) / bitsperpixel;\r
333         p = pixels;\r
334         while (--npixels >= 0) *p++ = 0;\r
335 \r
336         // Do the format conversion\r
337         for (i = 0; i < planes; i++){\r
338                 int pixbit, bits, mask;\r
339                 p = pixels;\r
340                 pixbit = (1 << i);  // pixel bit for this plane\r
341                 for (j = 0; j < bytesperline; j++){\r
342                         bits = *bitplanes++;\r
343                         for (mask = 0X80; mask != 0; mask >>= 1, p++)\r
344                                 if (bits & mask) *p |= pixbit;\r
345                 }\r
346         }\r
347         return true;\r
348 }\r
349 ////////////////////////////////////////////////////////////////////////////////\r
350 // convert packed pixel format into 1 pixel per byte\r
351 // from unpacked file data bitplanes[] into pixel row pixels[]\r
352 // image Height rows, with each row having planes image planes each\r
353 // bytesperline bytes\r
354 bool CxImagePCX::PCX_UnpackPixels(BYTE * pixels, BYTE * bitplanes, short bytesperline, short planes, short bitsperpixel)\r
355 {\r
356         register int bits;\r
357         if (planes != 1) return false;\r
358         \r
359         if (bitsperpixel == 8){  // 8 bits/pixels, no unpacking needed\r
360                 while (bytesperline-- > 0) *pixels++ = *bitplanes++;\r
361         } else if (bitsperpixel == 4){  // 4 bits/pixel, two pixels per byte\r
362                 while (bytesperline-- > 0){\r
363                         bits = *bitplanes++;\r
364                         *pixels++ = (BYTE)((bits >> 4) & 0X0F);\r
365                         *pixels++ = (BYTE)((bits) & 0X0F);\r
366                 }\r
367         } else if (bitsperpixel == 2){  // 2 bits/pixel, four pixels per byte\r
368                 while (bytesperline-- > 0){\r
369                         bits = *bitplanes++;\r
370                         *pixels++ = (BYTE)((bits >> 6) & 0X03);\r
371                         *pixels++ = (BYTE)((bits >> 4) & 0X03);\r
372                         *pixels++ = (BYTE)((bits >> 2) & 0X03);\r
373                         *pixels++ = (BYTE)((bits) & 0X03);\r
374                 }\r
375         } else if (bitsperpixel == 1){  // 1 bits/pixel, 8 pixels per byte\r
376                 while (bytesperline-- > 0){\r
377                         bits = *bitplanes++;\r
378                         *pixels++ = ((bits & 0X80) != 0);\r
379                         *pixels++ = ((bits & 0X40) != 0);\r
380                         *pixels++ = ((bits & 0X20) != 0);\r
381                         *pixels++ = ((bits & 0X10) != 0);\r
382                         *pixels++ = ((bits & 0X08) != 0);\r
383                         *pixels++ = ((bits & 0X04) != 0);\r
384                         *pixels++ = ((bits & 0X02) != 0);\r
385                         *pixels++ = ((bits & 0X01) != 0);\r
386                 }\r
387         }\r
388         return true;\r
389 }\r
390 ////////////////////////////////////////////////////////////////////////////////\r
391 /* PCX_PackPixels(const long p,BYTE &c, BYTE &n, long &l, CxFile &f)\r
392  * p = current pixel (-1 ends the line -2 ends odd line)\r
393  * c = previous pixel\r
394  * n = number of consecutive pixels\r
395  */\r
396 void CxImagePCX::PCX_PackPixels(const long p,BYTE &c, BYTE &n, CxFile &f)\r
397 {\r
398         if (p!=c && n){\r
399                 if (n==1 && c<0xC0){\r
400                         f.PutC(c);\r
401                 } else {\r
402                         f.PutC(0xC0|n);\r
403                         f.PutC(c);\r
404                 }\r
405                 n=0;\r
406         }\r
407         if (n==0x3F) {\r
408                 f.PutC(0xFF);\r
409                 f.PutC(c);\r
410                 n=0;\r
411         }\r
412         if (p==-2) f.PutC(0);\r
413         c=(BYTE)p;\r
414         n++;\r
415 }\r
416 ////////////////////////////////////////////////////////////////////////////////\r
417 void CxImagePCX::PCX_PackPlanes(BYTE* buff, const long size, CxFile &f)\r
418 {\r
419     BYTE *start,*end;\r
420     BYTE c, previous, count;\r
421 \r
422         start = buff;\r
423     end = buff + size;\r
424     previous = *start++;\r
425     count    = 1;\r
426 \r
427     while (start < end) {\r
428         c = *start++;\r
429         if (c == previous && count < 63) {\r
430             ++count;\r
431             continue;\r
432         }\r
433 \r
434         if (count > 1 || (previous & 0xc0) == 0xc0) {\r
435             f.PutC( count | 0xc0 );\r
436         }\r
437         f.PutC(previous);\r
438         previous = c;\r
439         count   = 1;\r
440     }\r
441 \r
442     if (count > 1 || (previous & 0xc0) == 0xc0) {\r
443         count |= 0xc0;\r
444         f.PutC(count);\r
445     }\r
446     f.PutC(previous);\r
447 }\r
448 ////////////////////////////////////////////////////////////////////////////////\r
449 void CxImagePCX::PCX_PixelsToPlanes(BYTE* raw, long width, BYTE* buf, long plane)\r
450 {\r
451     int cbit, x, mask;\r
452     unsigned char *cp = buf-1;\r
453 \r
454     mask = 1 << plane;\r
455     cbit = -1;\r
456     for( x = 0; x < width; x++ ) {\r
457         if( cbit < 0 ) {\r
458             cbit = 7;\r
459             *++cp = 0;\r
460         }\r
461         if( raw[x] & mask )\r
462             *cp |= (1<<cbit);\r
463         --cbit;\r
464     }\r
465 }\r
466 ////////////////////////////////////////////////////////////////////////////////\r
467 void CxImagePCX::PCX_toh(PCXHEADER* p)\r
468 {\r
469         p->Xmin = ntohs(p->Xmin);\r
470         p->Ymin = ntohs(p->Ymin);\r
471         p->Xmax = ntohs(p->Xmax);\r
472         p->Ymax = ntohs(p->Ymax);\r
473         p->Hres = ntohs(p->Hres);\r
474         p->Vres = ntohs(p->Vres);\r
475         p->BytesPerLine = ntohs(p->BytesPerLine);\r
476         p->PaletteType = ntohs(p->PaletteType);\r
477 }\r
478 ////////////////////////////////////////////////////////////////////////////////\r
479 #endif // CXIMAGE_SUPPORT_PCX\r