source: liacs/MIR2010/SourceCode/cximage/ximapng.cpp@ 352

Last change on this file since 352 was 95, checked in by Rick van der Zwet, 15 years ago

Bad boy, improper move of directory

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