]> Creatis software - clitk.git/blob - utilities/CxImage/ximapng.cpp
Debug RTStruct conversion with empty struc
[clitk.git] / utilities / CxImage / ximapng.cpp
1 /*
2  * File:        ximapng.cpp
3  * Purpose:     Platform Independent PNG 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 "ximapng.h"
9
10 #if CXIMAGE_SUPPORT_PNG
11
12 #include "ximaiter.h"
13
14 ////////////////////////////////////////////////////////////////////////////////
15 void CxImagePNG::ima_png_error(png_struct *png_ptr, char *message)
16 {
17         strcpy(info.szLastError,message);
18         longjmp(png_ptr->jmpbuf, 1);
19 }
20 ////////////////////////////////////////////////////////////////////////////////
21 #if CXIMAGE_SUPPORT_DECODE
22 ////////////////////////////////////////////////////////////////////////////////
23 void CxImagePNG::expand2to4bpp(BYTE* prow)
24 {
25         BYTE *psrc,*pdst;
26         BYTE pos,idx;
27         for(long x=head.biWidth-1;x>=0;x--){
28                 psrc = prow + ((2*x)>>3);
29                 pdst = prow + ((4*x)>>3);
30                 pos = (BYTE)(2*(3-x%4));
31                 idx = (BYTE)((*psrc & (0x03<<pos))>>pos);
32                 pos = (BYTE)(4*(1-x%2));
33                 *pdst &= ~(0x0F<<pos);
34                 *pdst |= (idx & 0x0F)<<pos;
35         }
36 }
37 ////////////////////////////////////////////////////////////////////////////////
38 bool CxImagePNG::Decode(CxFile *hFile)
39 {
40         png_struct *png_ptr;
41         png_info *info_ptr;
42         BYTE *row_pointers=NULL;
43         CImageIterator iter(this);
44
45   cx_try
46   {
47     /* Create and initialize the png_struct with the desired error handler
48     * functions.  If you want to use the default stderr and longjump method,
49     * you can supply NULL for the last three parameters.  We also supply the
50     * the compiler header file version, so that we know if the application
51     * was compiled with a compatible version of the library.  REQUIRED    */
52         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,(void *)NULL,NULL,NULL);
53         if (png_ptr == NULL)  cx_throw("Failed to create PNG structure");
54
55         /* Allocate/initialize the memory for image information.  REQUIRED. */
56         info_ptr = png_create_info_struct(png_ptr);
57         if (info_ptr == NULL) {
58                 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
59                 cx_throw("Failed to initialize PNG info structure");
60         }
61
62     /* Set error handling if you are using the setjmp/longjmp method (this is
63     * the normal method of doing things with libpng).  REQUIRED unless you
64     * set up your own error handlers in the png_create_read_struct() earlier. */
65         if (setjmp(png_ptr->jmpbuf)) {
66                 /* Free all of the memory associated with the png_ptr and info_ptr */
67                 delete [] row_pointers;
68                 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
69                 cx_throw("");
70         }
71
72         // use custom I/O functions
73     png_set_read_fn(png_ptr, hFile, /*(png_rw_ptr)*/user_read_data);
74         png_set_error_fn(png_ptr,info.szLastError,/*(png_error_ptr)*/user_error_fn,NULL);
75
76         /* read the file information */
77         png_read_info(png_ptr, info_ptr);
78
79         if (info.nEscape == -1){
80                 head.biWidth = info_ptr->width;
81                 head.biHeight= info_ptr->height;
82                 info.dwType = CXIMAGE_FORMAT_PNG;
83                 longjmp(png_ptr->jmpbuf, 1);
84         }
85
86         /* calculate new number of channels */
87         int channels=0;
88         switch(info_ptr->color_type){
89         case PNG_COLOR_TYPE_GRAY:
90         case PNG_COLOR_TYPE_PALETTE:
91                 channels = 1;
92                 break;
93         case PNG_COLOR_TYPE_GRAY_ALPHA:
94                 channels = 2;
95                 break;
96         case PNG_COLOR_TYPE_RGB:
97                 channels = 3;
98                 break;
99         case PNG_COLOR_TYPE_RGB_ALPHA:
100                 channels = 4;
101                 break;
102         default:
103                 strcpy(info.szLastError,"unknown PNG color type");
104                 longjmp(png_ptr->jmpbuf, 1);
105         }
106
107         //find the right pixel depth used for cximage
108         int pixel_depth = info_ptr->pixel_depth;
109         if (channels == 1 && pixel_depth>8) pixel_depth=8;
110         if (channels == 2) pixel_depth=8;
111         if (channels >= 3) pixel_depth=24;
112
113         if (!Create(info_ptr->width, info_ptr->height, pixel_depth, CXIMAGE_FORMAT_PNG)){
114                 longjmp(png_ptr->jmpbuf, 1);
115         }
116
117         /* get metrics */
118         switch (info_ptr->phys_unit_type)
119         {
120         case PNG_RESOLUTION_UNKNOWN:
121                 SetXDPI(info_ptr->x_pixels_per_unit);
122                 SetYDPI(info_ptr->y_pixels_per_unit);
123                 break;
124         case PNG_RESOLUTION_METER:
125                 SetXDPI((long)floor(info_ptr->x_pixels_per_unit * 254.0 / 10000.0 + 0.5));
126                 SetYDPI((long)floor(info_ptr->y_pixels_per_unit * 254.0 / 10000.0 + 0.5));
127                 break;
128         }
129
130         if (info_ptr->num_palette>0){
131                 SetPalette((rgb_color*)info_ptr->palette,info_ptr->num_palette);
132                 SetClrImportant(info_ptr->num_palette);
133         } else if (info_ptr->bit_depth ==2) { //<DP> needed for 2 bpp grayscale PNGs
134                 SetPaletteColor(0,0,0,0);
135                 SetPaletteColor(1,85,85,85);
136                 SetPaletteColor(2,170,170,170);
137                 SetPaletteColor(3,255,255,255);
138         } else SetGrayPalette(); //<DP> needed for grayscale PNGs
139         
140         int nshift = max(0,(info_ptr->bit_depth>>3)-1)<<3;
141
142         if (info_ptr->num_trans!=0){ //palette transparency
143                 if (info_ptr->num_trans==1){
144                         if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE){
145                                 info.nBkgndIndex = info_ptr->trans_values.index;
146                         } else{
147                                 info.nBkgndIndex = info_ptr->trans_values.gray>>nshift;
148                         }
149                 }
150                 if (info_ptr->num_trans>1){
151                         RGBQUAD* pal=GetPalette();
152                         if (pal){
153                                 DWORD ip;
154                                 for (ip=0;ip<min(head.biClrUsed,(unsigned long)info_ptr->num_trans);ip++)
155                                         pal[ip].rgbReserved=info_ptr->trans[ip];
156                                 for (ip=info_ptr->num_trans;ip<head.biClrUsed;ip++){
157                                         pal[ip].rgbReserved=255;
158                                 }
159                                 info.bAlphaPaletteEnabled=true;
160                         }
161                 }
162         }
163
164         if (channels == 3){ //check RGB binary transparency
165                 png_bytep trans;
166                 int num_trans;
167                 png_color_16 *image_background;
168                 if (png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &image_background)){
169                         info.nBkgndColor.rgbRed   = (BYTE)(info_ptr->trans_values.red>>nshift);
170                         info.nBkgndColor.rgbGreen = (BYTE)(info_ptr->trans_values.green>>nshift);
171                         info.nBkgndColor.rgbBlue  = (BYTE)(info_ptr->trans_values.blue>>nshift);
172                         info.nBkgndColor.rgbReserved = 0;
173                         info.nBkgndIndex = 0;
174                 }
175         }
176
177         int alpha_present = (channels - 1) % 2;
178         if (alpha_present){
179 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
180                 AlphaCreate();
181 #else
182                 png_set_strip_alpha(png_ptr);
183 #endif //CXIMAGE_SUPPORT_ALPHA
184         }
185
186         // <vho> - flip the RGB pixels to BGR (or RGBA to BGRA)
187         if (info_ptr->color_type & PNG_COLOR_MASK_COLOR){
188                 png_set_bgr(png_ptr);
189         }
190
191         // <vho> - handle cancel
192         if (info.nEscape) longjmp(png_ptr->jmpbuf, 1);
193
194         // row_bytes is the width x number of channels x (bit-depth / 8)
195         row_pointers = new BYTE[info_ptr->rowbytes + 8];
196
197         // turn on interlace handling
198         int number_passes = png_set_interlace_handling(png_ptr);
199
200         if (number_passes>1){
201                 SetCodecOption(1);
202         } else {
203                 SetCodecOption(0);
204         }
205
206         int chan_offset = info_ptr->bit_depth >> 3;
207         int pixel_offset = info_ptr->pixel_depth >> 3;
208
209         for (int pass=0; pass < number_passes; pass++) {
210                 iter.Upset();
211                 int y=0;
212                 do      {
213
214                         // <vho> - handle cancel
215                         if (info.nEscape) longjmp(png_ptr->jmpbuf, 1);
216
217 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
218                         if (AlphaIsValid()) {
219
220                                 //compute the correct position of the line
221                                 long ax,ay;
222                                 ay = head.biHeight-1-y;
223                                 BYTE* prow= iter.GetRow(ay);
224
225                                 //recover data from previous scan
226                                 if (info_ptr->interlace_type && pass>0 && pass!=7){
227                                         for(ax=0;ax<head.biWidth;ax++){
228                                                 long px = ax * pixel_offset;
229                                                 if (channels == 2){
230                                                         row_pointers[px] = prow[ax];
231                                                         row_pointers[px+chan_offset]=AlphaGet(ax,ay);
232                                                 } else {
233                                                         long qx = ax * 3;
234                                                         row_pointers[px]              =prow[qx];
235                                                         row_pointers[px+chan_offset]  =prow[qx+1];
236                                                         row_pointers[px+chan_offset*2]=prow[qx+2];
237                                                         row_pointers[px+chan_offset*3]=AlphaGet(ax,ay);
238                                                 }
239                                         }
240                                 }
241
242                                 //read next row
243                                 png_read_row(png_ptr, row_pointers, NULL);
244
245                                 //RGBA -> RGB + A
246                                 for(ax=0;ax<head.biWidth;ax++){
247                                         long px = ax * pixel_offset;
248                                         if (channels == 2){
249                                                 prow[ax] = row_pointers[px];
250                                                 AlphaSet(ax,ay,row_pointers[px+chan_offset]);
251                                         } else {
252                                                 long qx = ax * 3;
253                                                 prow[qx]  =row_pointers[px];
254                                                 prow[qx+1]=row_pointers[px+chan_offset];
255                                                 prow[qx+2]=row_pointers[px+chan_offset*2];
256                                                 AlphaSet(ax,ay,row_pointers[px+chan_offset*3]);
257                                         }
258                                 }
259                         } else
260 #endif // CXIMAGE_SUPPORT_ALPHA         // vho
261                         {
262                                 //recover data from previous scan
263                                 if (info_ptr->interlace_type && pass>0){
264                                         iter.GetRow(row_pointers, info_ptr->rowbytes);
265                                         //re-expand buffer for images with bit depth > 8
266                                         if (info_ptr->bit_depth > 8){
267                                                 for(long ax=(head.biWidth*channels-1);ax>=0;ax--)
268                                                         row_pointers[ax*chan_offset] = row_pointers[ax];
269                                         }
270                                 }
271
272                                 //read next row
273                                 png_read_row(png_ptr, row_pointers, NULL);
274
275                                 //shrink 16 bit depth images down to 8 bits
276                                 if (info_ptr->bit_depth > 8){
277                                         for(long ax=0;ax<(head.biWidth*channels);ax++)
278                                                 row_pointers[ax] = row_pointers[ax*chan_offset];
279                                 }
280
281                                 //copy the pixels
282                                 iter.SetRow(row_pointers, info_ptr->rowbytes);
283                                 //<DP> expand 2 bpp images only in the last pass
284                                 if (info_ptr->bit_depth==2 && pass==(number_passes-1))
285                                         expand2to4bpp(iter.GetRow());
286
287                                 //go on
288                                 iter.PrevRow();
289                         }
290
291                         y++;
292                 } while(y<head.biHeight);
293         }
294
295         delete [] row_pointers;
296
297         /* read the rest of the file, getting any additional chunks in info_ptr */
298         png_read_end(png_ptr, info_ptr);
299
300         /* clean up after the read, and free any memory allocated - REQUIRED */
301         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
302
303   } cx_catch {
304         if (strcmp(message,"")) strncpy(info.szLastError,message,255);
305         if (info.nEscape == -1 && info.dwType == CXIMAGE_FORMAT_PNG) return true;
306         return false;
307   }
308         /* that's it */
309         return true;
310 }
311 ////////////////////////////////////////////////////////////////////////////////
312 #endif //CXIMAGE_SUPPORT_DECODE
313 ////////////////////////////////////////////////////////////////////////////////
314 #if CXIMAGE_SUPPORT_ENCODE
315 ////////////////////////////////////////////////////////////////////////////////
316 bool CxImagePNG::Encode(CxFile *hFile)
317 {
318         if (EncodeSafeCheck(hFile)) return false;
319
320         CImageIterator iter(this);
321         BYTE trans[256];        //for transparency (don't move)
322         png_struct *png_ptr;
323         png_info *info_ptr;
324
325   cx_try
326   {
327    /* Create and initialize the png_struct with the desired error handler
328     * functions.  If you want to use the default stderr and longjump method,
329     * you can supply NULL for the last three parameters.  We also check that
330     * the library version is compatible with the one used at compile time,
331     * in case we are using dynamically linked libraries.  REQUIRED.
332     */
333         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,(void *)NULL,NULL,NULL);
334         if (png_ptr == NULL) cx_throw("Failed to create PNG structure");
335
336         /* Allocate/initialize the image information data.  REQUIRED */
337         info_ptr = png_create_info_struct(png_ptr);
338         if (info_ptr == NULL){
339                 png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
340                 cx_throw("Failed to initialize PNG info structure");
341         }
342
343    /* Set error handling.  REQUIRED if you aren't supplying your own
344     * error hadnling functions in the png_create_write_struct() call.
345     */
346         if (setjmp(png_ptr->jmpbuf)){
347                 /* If we get here, we had a problem reading the file */
348                 if (info_ptr->palette) free(info_ptr->palette);
349                 png_destroy_write_struct(&png_ptr,  (png_infopp)&info_ptr);
350                 cx_throw("Error saving PNG file");
351         }
352             
353         /* set up the output control */
354         //png_init_io(png_ptr, hFile);
355
356         // use custom I/O functions
357     png_set_write_fn(png_ptr,hFile,/*(png_rw_ptr)*/user_write_data,/*(png_flush_ptr)*/user_flush_data);
358
359         /* set the file information here */
360         info_ptr->width = GetWidth();
361         info_ptr->height = GetHeight();
362         info_ptr->pixel_depth = (BYTE)GetBpp();
363         info_ptr->channels = (GetBpp()>8) ? (BYTE)3: (BYTE)1;
364         info_ptr->bit_depth = (BYTE)(GetBpp()/info_ptr->channels);
365         info_ptr->compression_type = info_ptr->filter_type = 0;
366         info_ptr->valid = 0;
367
368         switch(GetCodecOption(CXIMAGE_FORMAT_PNG)){
369         case 1:
370                 info_ptr->interlace_type = PNG_INTERLACE_ADAM7;
371                 break;
372         default:
373                 info_ptr->interlace_type = PNG_INTERLACE_NONE;
374         }
375
376         /* set compression level */
377         //png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
378
379         bool bGrayScale = IsGrayScale();
380
381         if (GetNumColors()){
382                 if (bGrayScale){
383                         info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
384                 } else {
385                         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
386                 }
387         } else {
388                 info_ptr->color_type = PNG_COLOR_TYPE_RGB;
389         }
390 #if CXIMAGE_SUPPORT_ALPHA
391         if (AlphaIsValid()){
392                 info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
393                 info_ptr->channels++;
394                 info_ptr->bit_depth = 8;
395                 info_ptr->pixel_depth += 8;
396         }
397 #endif
398
399         /* set background */
400         png_color_16 image_background={ 0, 255, 255, 255, 0 };
401         RGBQUAD tc = GetTransColor();
402         if (info.nBkgndIndex>=0) {
403                 image_background.blue  = tc.rgbBlue;
404                 image_background.green = tc.rgbGreen;
405                 image_background.red   = tc.rgbRed;
406         }
407         png_set_bKGD(png_ptr, info_ptr, &image_background);
408
409         /* set metrics */
410         png_set_pHYs(png_ptr, info_ptr, head.biXPelsPerMeter, head.biYPelsPerMeter, PNG_RESOLUTION_METER);
411
412         png_set_IHDR(png_ptr, info_ptr, info_ptr->width, info_ptr->height, info_ptr->bit_depth,
413                                 info_ptr->color_type, info_ptr->interlace_type,
414                                 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
415
416         //<DP> simple transparency
417         if (info.nBkgndIndex >= 0){
418                 info_ptr->num_trans = 1;
419                 info_ptr->valid |= PNG_INFO_tRNS;
420                 info_ptr->trans = trans;
421                 info_ptr->trans_values.index = (BYTE)info.nBkgndIndex;
422                 info_ptr->trans_values.red   = tc.rgbRed;
423                 info_ptr->trans_values.green = tc.rgbGreen;
424                 info_ptr->trans_values.blue  = tc.rgbBlue;
425                 info_ptr->trans_values.gray  = info_ptr->trans_values.index;
426
427                 // the transparency indexes start from 0 for non grayscale palette
428                 if (!bGrayScale && head.biClrUsed && info.nBkgndIndex)
429                         SwapIndex(0,(BYTE)info.nBkgndIndex);
430         }
431
432         /* set the palette if there is one */
433         if (GetPalette()){
434                 if (!bGrayScale){
435                         info_ptr->valid |= PNG_INFO_PLTE;
436                 }
437
438                 int nc = GetClrImportant();
439                 if (nc==0) nc = GetNumColors();
440
441                 if (info.bAlphaPaletteEnabled){
442                         for(WORD ip=0; ip<nc;ip++)
443                                 trans[ip]=GetPaletteColor((BYTE)ip).rgbReserved;
444                         info_ptr->num_trans = (WORD)nc;
445                         info_ptr->valid |= PNG_INFO_tRNS;
446                         info_ptr->trans = trans;
447                 }
448
449                 // copy the palette colors
450                 info_ptr->palette = new png_color[nc];
451                 info_ptr->num_palette = (png_uint_16) nc;
452                 for (int i=0; i<nc; i++)
453                         GetPaletteColor(i, &info_ptr->palette[i].red, &info_ptr->palette[i].green, &info_ptr->palette[i].blue);
454         }  
455
456 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
457         //Merge the transparent color with the alpha channel
458         if (AlphaIsValid() && head.biBitCount==24 && info.nBkgndIndex>=0){
459                 for(long y=0; y < head.biHeight; y++){
460                         for(long x=0; x < head.biWidth ; x++){
461                                 RGBQUAD c=GetPixelColor(x,y,false);
462                                 if (*(long*)&c==*(long*)&tc)
463                                         AlphaSet(x,y,0);
464         }       }       }
465 #endif // CXIMAGE_SUPPORT_ALPHA // <vho>
466
467         int row_size = max(info.dwEffWidth, info_ptr->width*info_ptr->channels*(info_ptr->bit_depth/8));
468         info_ptr->rowbytes = row_size;
469         BYTE *row_pointers = new BYTE[row_size];
470
471         /* write the file information */
472         png_write_info(png_ptr, info_ptr);
473
474         //interlace handling
475         int num_pass = png_set_interlace_handling(png_ptr);
476         for (int pass = 0; pass < num_pass; pass++){
477                 //write image
478                 iter.Upset();
479                 long ay=head.biHeight-1;
480                 RGBQUAD c;
481                 do      {
482 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
483                         if (AlphaIsValid()){
484                                 for (long ax=head.biWidth-1; ax>=0;ax--){
485                                         c = BlindGetPixelColor(ax,ay);
486                                         int px = ax * info_ptr->channels;
487                                         if (!bGrayScale){
488                                                 row_pointers[px++]=c.rgbRed;
489                                                 row_pointers[px++]=c.rgbGreen;
490                                         }
491                                         row_pointers[px++]=c.rgbBlue;
492                                         row_pointers[px] = AlphaGet(ax,ay);
493                                 }
494                                 png_write_row(png_ptr, row_pointers);
495                                 ay--;
496                         }
497                         else
498 #endif //CXIMAGE_SUPPORT_ALPHA  // <vho>
499                         {
500                                 iter.GetRow(row_pointers, row_size);
501                                 if (info_ptr->color_type == PNG_COLOR_TYPE_RGB) //HACK BY OP
502                                         RGBtoBGR(row_pointers, row_size);
503                                 png_write_row(png_ptr, row_pointers);
504                         }
505                 } while(iter.PrevRow());
506         }
507
508         delete [] row_pointers;
509
510         //if necessary, restore the original palette
511         if (!bGrayScale && head.biClrUsed && info.nBkgndIndex>0)
512                 SwapIndex((BYTE)info.nBkgndIndex,0);
513
514         /* It is REQUIRED to call this to finish writing the rest of the file */
515         png_write_end(png_ptr, info_ptr);
516
517         /* if you malloced the palette, free it here */
518         if (info_ptr->palette){
519                 delete [] (info_ptr->palette);
520                 info_ptr->palette = NULL;
521         }
522
523         /* clean up after the write, and free any memory allocated */
524         png_destroy_write_struct(&png_ptr, (png_infopp)&info_ptr);
525
526   } cx_catch {
527         if (strcmp(message,"")) strncpy(info.szLastError,message,255);
528         return FALSE;
529   }
530         /* that's it */
531         return TRUE;
532 }
533 ////////////////////////////////////////////////////////////////////////////////
534 #endif // CXIMAGE_SUPPORT_ENCODE
535 ////////////////////////////////////////////////////////////////////////////////
536 #endif // CXIMAGE_SUPPORT_PNG