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