1 /*****************************************************************************
2 * png_pov.cpp
3 *
4 * This module contains the code to read and write the PNG output file
5 *
6 * from Persistence of Vision(tm) Ray Tracer version 3.6.
7 * Copyright 1991-2003 Persistence of Vision Team
8 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
9 *---------------------------------------------------------------------------
10 * NOTICE: This source code file is provided so that users may experiment
11 * with enhancements to POV-Ray and to port the software to platforms other
12 * than those supported by the POV-Ray developers. There are strict rules
13 * regarding how you are permitted to use this file. These rules are contained
14 * in the distribution and derivative versions licenses which should have been
15 * provided with this file.
16 *
17 * These licences may be found online, linked from the end-user license
18 * agreement that is located at http://www.povray.org/povlegal.html
19 *---------------------------------------------------------------------------
20 * This program is based on the popular DKB raytracer version 2.12.
21 * DKBTrace was originally written by David K. Buck.
22 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
23 *---------------------------------------------------------------------------
24 *
25 *===========================================================================
26 * This file is part of MegaPOV, a modified and unofficial version of POV-Ray
27 * For more information on MegaPOV visit our website:
28 * http://megapov.inetart.net/
29 *===========================================================================
30 *
31 * $RCSfile: png_pov.cpp,v $
32 * $Revision: 1.19 $
33 * $Author: chris $
34 *
35 *****************************************************************************/
36
37 /*****************************************************************************
38 * This code requires the use of libpng, Group 42's PNG reference library.
39 * libpng is Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
40 *
41 * This code also requires the use of Zlib,
42 * Zlib is Copyright (C) 1995 Jean-loup Gailly and Mark Adler
43 *
44 * The latest version of these libraries are available at ftp.uu.net as
45 *
46 * /graphics/png/libpngXX.tar.gz.
47 * /archiver/zlib/zlib-XX.tar.gz.
48 *
49 * where XX is the latest version of the library.
50 *
51 *****************************************************************************/
52
53 #include "frame.h"
54 #include "povray.h"
55 #include "optout.h"
56 #include "png.h"
57 #include "png_pov.h"
58 #include "pov_util.h"
59 #include "povmsend.h"
60 #include "colour.h"
61
62 USING_POV_NAMESPACE
63
64 /*****************************************************************************
65 * Local preprocessor defines
66 ******************************************************************************/
67
68 /* Number of scanlines between output flushes, and hence the maximum number of
69 * lines lost for an interrupted render. Note that making it much smaller
70 * than about 10 for a 640x480 image will noticably degrade compression.
71 * If a very small buffer is specified, we don't want to flush more than once
72 * every 10 lines or so (assuming a 2:1 compression ratio).
73 */
74 const int FLUSH_DIST = 16; // Set to 16 and removed complicated math [trf]
75
76 const int NTEXT = 15; // Maximum number of tEXt comment blocks
77 const int MAXTEXT = 1024; // Maximum length of a tEXt message
78
79
80 /*****************************************************************************
81 * Local typedefs
82 ******************************************************************************/
83
84
85 /*****************************************************************************
86 * Local variables
87 ******************************************************************************/
88
89 static png_struct *png_ptr = NULL; // GLOBAL VARIABLE
90 static png_info *info_ptr = NULL; // GLOBAL VARIABLE
91 static png_struct *o_png_ptr = NULL; // GLOBAL VARIABLE
92 static png_byte *row_ptr = NULL; // GLOBAL VARIABLE
93 static int png_stride; // GLOBAL VARIABLE
94 static char tmp_fname[FILE_NAME_LENGTH]; // GLOBAL VARIABLE
95 static IStream *tmp_fp = NULL; // GLOBAL VARIABLE
96
97 int PNG_Image::line_number = 0; // GLOBAL VARIABLE
98 IStream *PNG_Image::in_file = NULL; // GLOBAL VARIABLE
99 OStream *PNG_Image::out_file = NULL; // GLOBAL VARIABLE
100
101 /*****************************************************************************
102 * Static functions
103 ******************************************************************************/
104
105
106 extern "C"
107 {
108 // These are replacement error and warning functions for the libpng code
109 void png_pov_err(png_structp, png_const_charp);
110 void png_pov_warn(png_structp, png_const_charp);
111 void png_pov_read_data(png_structp, png_bytep, png_size_t);
112 void png_pov_write_data(png_structp, png_bytep, png_size_t);
113 void png_pov_flush_data(png_structp);
114
115
116 /*****************************************************************************
117 *
118 * FUNCTION : png_pov_warn
119 *
120 * ARGUMENTS : png_struct *png_ptr; char *msg;
121 *
122 * MODIFIED ARGS :
123 *
124 * RETURN VALUE :
125 *
126 * AUTHOR : Andreas Dilger
127 *
128 * DESCRIPTION
129 *
130 * Prints an warning message using the POV I/O functions. This uses the
131 * png io_ptr to determine whether error messages should be printed or
132 * not.
133 *
134 * CHANGES
135 *
136 ******************************************************************************/
137
png_pov_warn(png_structp png_ptr,png_const_charp msg)138 void png_pov_warn(png_structp png_ptr, png_const_charp msg)
139 {
140 if (png_get_error_ptr(png_ptr))
141 Warning(0,"libpng: %s",msg);
142 }
143
144
145 /*****************************************************************************
146 *
147 * FUNCTION : png_pov_err
148 *
149 * ARGUMENTS : png_struct *png_ptr; char *msg;
150 *
151 * MODIFIED ARGS :
152 *
153 * RETURN VALUE :
154 *
155 * AUTHOR : Andreas Dilger
156 *
157 * DESCRIPTION
158 *
159 * If the png io_ptr is true, this prints an error message using the POV
160 * I/O function. It will return to the location of the last setjmp call
161 * for this stream in any case.
162 *
163 * CHANGES
164 *
165 ******************************************************************************/
166
png_pov_err(png_structp png_ptr,png_const_charp msg)167 void png_pov_err(png_structp png_ptr, png_const_charp msg)
168 {
169 if (png_get_error_ptr(png_ptr))
170 PossibleError("libpng: %s",msg);
171
172 longjmp(png_jmpbuf(png_ptr),1);
173 }
174
175
176 /*****************************************************************************
177 *
178 * FUNCTION : png_pov_read_data
179 *
180 * ARGUMENTS : png_structp png_ptr; png_bytep data; png_uint_32 length;
181 *
182 * MODIFIED ARGS :
183 *
184 * RETURN VALUE :
185 *
186 * AUTHOR : Thorsten Froehlich
187 *
188 * DESCRIPTION
189 *
190 * Replacement read function.
191 *
192 * CHANGES
193 *
194 ******************************************************************************/
195
png_pov_read_data(png_structp png_ptr,png_bytep data,png_size_t length)196 void png_pov_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
197 {
198 IStream *file = (IStream *)png_get_io_ptr(png_ptr);
199
200 if (!file->read ((char *)data, length))
201 PossibleError("Cannot read PNG data.");
202 }
203
204
205 /*****************************************************************************
206 *
207 * FUNCTION : png_pov_write_data
208 *
209 * ARGUMENTS : png_structp png_ptr; png_bytep data; png_uint_32 length;
210 *
211 * MODIFIED ARGS :
212 *
213 * RETURN VALUE :
214 *
215 * AUTHOR : Thorsten Froehlich
216 *
217 * DESCRIPTION
218 *
219 * Replacement write function.
220 *
221 * CHANGES
222 *
223 ******************************************************************************/
224
png_pov_write_data(png_structp png_ptr,png_bytep data,png_size_t length)225 void png_pov_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
226 {
227 OStream *file = (OStream *)png_get_io_ptr(png_ptr);
228
229 if (!file->write ((char *)data, length))
230 PossibleError("Cannot write PNG data.");
231 }
232
233
234 /*****************************************************************************
235 *
236 * FUNCTION : png_pov_flush_data
237 *
238 * ARGUMENTS : png_structp png_ptr;
239 *
240 * MODIFIED ARGS :
241 *
242 * RETURN VALUE :
243 *
244 * AUTHOR : Thorsten Froehlich
245 *
246 * DESCRIPTION
247 *
248 * Replacement flush function.
249 *
250 * CHANGES
251 *
252 ******************************************************************************/
253
png_pov_flush_data(png_structp png_ptr)254 void png_pov_flush_data(png_structp png_ptr)
255 {
256 OStream *file = (OStream *)png_get_io_ptr(png_ptr);
257
258 file->flush();
259 }
260
261 }
262
263 BEGIN_POV_NAMESPACE
264
265 /*****************************************************************************
266 *
267 * FUNCTION : Open_Png_File
268 *
269 * ARGUMENTS : FILE_HANDLE *handle; char *name; int *width; int *height;
270 * int buffer_size; int mode;
271 *
272 * MODIFIED ARGS : handle, width, height
273 *
274 * RETURN VALUE : 1 or 0 for success or failure
275 *
276 * AUTHOR : Andreas Dilger
277 *
278 * DESCRIPTION
279 *
280 * Open a PNG file and allocate the needed PNG structure buffers
281 *
282 * CHANGES
283 *
284 * Updated for POV-Ray 3.X - [TIW]
285 * Updated to handle resuming interrupted traces, Sept 1995 - [AED]
286 * Updated to output grayscale heightfield if requested - [AED]
287 * Updated to allow grayscale and alpha together, Oct 1995 - [AED]
288 * Updated to write gamma differently based on file type, Nov 1995 - [AED]
289 * Changed temp file name from TEMP_FILE_BASE to scene name, Feb 1996 - [AED]
290 * Changed temp file from scene name to path + scene name, Jun 1996 - [AED]
291 *
292 ******************************************************************************/
293
PNG_Image(char * name,int w,int h,int m,int l)294 PNG_Image::PNG_Image(char *name, int w, int h, int m, int l)
295 {
296 mode = m;
297 filename = name;
298 initial_line_number = line_number = l;
299 width = w ;
300 height = h ;
301
302 switch (mode)
303 {
304 case READ_MODE:
305
306 in_file = NULL;
307 out_file = NULL;
308
309 // We can't resume from stdout.
310 if (opts.Options & TO_STDOUT)
311 return;
312
313 /* Initialize PNG output routines using temporary file name. We
314 * need to use the path, or the rename will fail if the temp file
315 * is not on the same drive as the output file.
316 */
317 // [NC] fix for potential buffer overrun.
318 {
319 long n1 = strlen(opts.Output_Path);
320 long n2 = strlen(opts.Scene_Name);
321
322 // "5" is strlen(".tpn") + '\0'
323 if (n1 + n2 + 5 < FILE_NAME_LENGTH)
324 sprintf(tmp_fname, "%s%s.tpn", opts.Output_Path, opts.Scene_Name);
325 else // sadly we can't use the path
326 sprintf(tmp_fname, "%.*s.tpn", FILE_NAME_LENGTH-5, opts.Scene_Name);
327 }
328
329 /* Move the old output file to a temporary file, so it can be
330 * read in and simultaneously written out to the new output file.
331 * Note that this can potentially be destructive, but it is
332 * impossible to change the output stream in mid-write. We have
333 * to check if a temp file already exists, in case the transfer
334 * has been previously aborted.
335 */
336 if ((tmp_fp = New_Checked_IStream(tmp_fname, POV_File_Image_PNG)) == NULL)
337 {
338 // The temp file doesn't exist. Try the original file.
339 if ((tmp_fp = New_Checked_IStream(name, POV_File_Image_PNG)) == NULL)
340 {
341 return; // Neither file exists - start from scratch.
342 }
343 else // The original file exists, but the temp file doesn't.
344 {
345 delete tmp_fp;
346
347 if (RENAME_FILE(name,tmp_fname) == RENAME_FILE_ERR)
348 {
349 Error("Cannot make temporary PNG file for continuing trace.");
350 }
351
352 // Open the original file (now with a new name) for reading
353 if ((tmp_fp = New_Checked_IStream(tmp_fname, POV_File_Image_PNG)) == NULL)
354 {
355 RENAME_FILE(tmp_fname,name); // Try to rename back - not crucial
356 Error("Cannot open temporary PNG file for continuing trace.");
357 }
358 }
359 }
360 else if ((in_file = New_Checked_IStream(name, POV_File_Image_PNG)) != NULL)
361 {
362 /* The temp file already exists. If we can open the original file
363 * as well, then there is something wrong, and we can't automatically
364 * decide which file to delete.
365 */
366 delete tmp_fp;
367 delete in_file;
368
369 Error ("Both original and temporary PNG files exist after an interrupted trace.\n"
370 "Please delete either %s or %s (preferrably the smaller).",name,tmp_fname);
371 }
372
373 /* Try to open the new output file for writing. If we can't, try
374 * to move the old one back so that users don't fret if it's missing.
375 * PNG will be able to continue without loss of data either way.
376 */
377 if ((out_file = New_Checked_OStream(name, POV_File_Image_PNG, false)) == NULL)
378 {
379 delete tmp_fp;
380 RENAME_FILE(tmp_fname,name);
381 return;
382 }
383
384 // The original input file
385 if ((o_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
386 (png_voidp)false, png_pov_err, png_pov_warn)) == NULL ||
387 (info_ptr = png_create_info_struct(o_png_ptr)) == NULL)
388 {
389 Error("Cannot allocate PNG data structures");
390 }
391
392 if (setjmp(png_jmpbuf(o_png_ptr)))
393 {
394 // If we get here, we had a problem reading the file
395 png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
396
397 delete out_file;
398 out_file = NULL;
399 delete tmp_fp;
400 tmp_fp = NULL;
401
402 return;
403 }
404
405 // Set up the compression structure
406 png_set_read_fn(o_png_ptr, tmp_fp, png_pov_read_data);
407
408 // Read in header info from the file
409 png_read_info(o_png_ptr, info_ptr);
410
411 if (png_get_image_width(o_png_ptr, info_ptr) != w ||
412 png_get_image_height(o_png_ptr, info_ptr) != h)
413 {
414 png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
415 delete tmp_fp;
416 tmp_fp = NULL;
417 delete out_file;
418 out_file = NULL;
419 // Try to get the original file back
420 if (DELETE_FILE(name) != DELETE_FILE_ERR)
421 RENAME_FILE(tmp_fname,name);
422 Error("PNG file dimensions do not match render resolution.");
423 }
424
425 if (png_get_color_type(o_png_ptr, info_ptr) & ~(PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA))
426 {
427 return;
428 }
429
430 Send_Progress("Resuming interrupted trace", PROGRESS_RESUMING_INTERRUPTED_TRACE);
431
432 // The new output file. Thank god for re-entrant libpng/libz code!
433 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
434 (png_voidp)true, png_pov_err, png_pov_warn)) == NULL)
435 {
436 Error("Cannot allocate PNG data structures");
437 }
438
439 if (setjmp(png_jmpbuf(png_ptr)))
440 {
441 // If we get here, we had a problem writing the file
442 png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
443 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
444
445 delete tmp_fp;
446 tmp_fp = NULL;
447
448 if (DELETE_FILE(name) != DELETE_FILE_ERR)
449 {
450 RENAME_FILE(tmp_fname,name); // Try to get the original file back
451 }
452
453 return;
454 }
455
456 // Set up the compression structure
457 png_set_write_fn(png_ptr, out_file, png_pov_write_data, png_pov_flush_data);
458
459 // Fill in the relevant image information from the resumed file
460 width = png_get_image_width(o_png_ptr, info_ptr);
461 height = png_get_image_height(o_png_ptr, info_ptr);
462
463 // Find out if file is a valid format, and if it had Alpha in it
464 if (png_get_color_type(o_png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
465 {
466 opts.Options |= OUTPUT_ALPHA;
467 }
468
469 if ((png_get_color_type(o_png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
470 {
471 opts.Options |= HF_GRAY_16;
472 opts.PaletteOption = GREY; // Force grayscale preview
473 }
474
475 #if defined(PNG_READ_sBIT_SUPPORTED)
476 png_color_8p read_sig_bit;
477
478 if (png_get_sBIT(o_png_ptr, info_ptr, &read_sig_bit) & PNG_INFO_sBIT)
479 {
480 if (png_get_color_type(o_png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR)
481 {
482 opts.OutputQuality = read_sig_bit->red;
483 }
484 else
485 {
486 opts.OutputQuality = read_sig_bit->gray;
487 }
488 }
489
490 #else // !PNG_READ_sBIT_SUPPORTED
491 if (info_ptr->bit_depth == 8 && opts.OutputQuality > 8 ||
492 info_ptr->bit_depth == 16 && opts.OutputQuality <= 8)
493 {
494 Error("Specified color depth +fn%d not the same as depth %d in %s",
495 opts.OutputQuality, info_ptr->bit_depth, name);
496 }
497 #endif // !PNG_READ_sBIT_SUPPORTED
498
499 #if defined(PNG_READ_oFFs_SUPPORTED)
500 opts.First_Column = png_get_x_offset_pixels(o_png_ptr, info_ptr);
501 opts.First_Line = png_get_y_offset_pixels(o_png_ptr, info_ptr);
502 #endif // PNG_READ_oFFs_SUPPORTED
503
504 png_write_info(png_ptr, info_ptr);
505
506 png_stride = png_get_color_type(o_png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR ? 3 : 1;
507
508 if (png_get_color_type(o_png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
509 png_stride++;
510
511 png_stride *= (opts.OutputQuality + 7) / 8;
512
513 row_ptr = (png_byte *)POV_MALLOC(w*png_stride,"PNG read row buffer");
514 break;
515
516 case WRITE_MODE:
517
518 in_file = NULL;
519 out_file = NULL;
520
521 if (opts.Options & TO_STDOUT)
522 {
523 out_file = New_OStream("stdout", POV_File_Image_PNG, false);
524 }
525 else if ((out_file = New_Checked_OStream(name, POV_File_Image_PNG, false)) == NULL)
526 {
527 return;
528 }
529
530 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
531 (png_voidp)true, png_pov_err, png_pov_warn)) == NULL ||
532 (info_ptr = png_create_info_struct(png_ptr)) == NULL)
533 {
534 Error("Cannot allocate PNG data structures");
535 }
536
537 if (setjmp(png_jmpbuf(png_ptr)))
538 {
539 // If we get here, we had a problem writing the file
540 png_destroy_write_struct(&png_ptr, &info_ptr);
541
542 delete out_file;
543 out_file = NULL;
544
545 return;
546 }
547
548 // Set up the compression structure
549
550 // png_set_read_fn(png_ptr, out_file, png_pov_read_data);
551 png_set_write_fn(png_ptr, out_file, png_pov_write_data, png_pov_flush_data);
552
553 // Fill in the relevant image information
554
555 png_byte bit_depth, color_type;
556
557 width = w;
558 height = h;
559 bit_depth = 8 * ((opts.OutputQuality + 7) / 8);
560
561 if (opts.Options & HF_GRAY_16)
562 {
563 if ( bit_depth < 16 ) {
564 bit_depth = 16;
565 opts.OutputQuality = 16;
566 }
567 color_type = PNG_COLOR_TYPE_GRAY;
568 }
569 else
570 {
571 color_type = PNG_COLOR_TYPE_RGB;
572 }
573
574 if (opts.Options & OUTPUT_ALPHA)
575 {
576 color_type |= PNG_COLOR_MASK_ALPHA;
577 }
578
579 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
580 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
581 PNG_FILTER_TYPE_BASE);
582
583 #if defined(PNG_WRITE_sBIT_SUPPORTED)
584 png_color_8 sig_bit;
585
586 if (color_type & PNG_COLOR_MASK_COLOR)
587 {
588 sig_bit.red =
589 sig_bit.green =
590 sig_bit.blue = opts.OutputQuality;
591 }
592 else
593 {
594 sig_bit.gray = opts.OutputQuality;
595 }
596
597 if (color_type & PNG_COLOR_MASK_ALPHA)
598 {
599 sig_bit.alpha = opts.OutputQuality;
600 }
601
602 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
603 #endif // PNG_WRITE_sBIT_SUPPORTED
604
605 #if defined(PNG_WRITE_gAMA_SUPPORTED)
606 if (!opts.histogram_on)
607 {
608 png_set_gAMA(png_ptr, info_ptr, 1.0/opts.DisplayGamma);
609 }
610 else
611 {
612 png_set_gAMA(png_ptr, info_ptr, 1.0);
613 }
614 #endif // PNG_WRITE_gAMA_SUPPORTED
615
616 #if defined(PNG_WRITE_oFFs_SUPPORTED)
617 if (opts.First_Column != 0 || opts.First_Line != 0)
618 {
619 png_set_oFFs(png_ptr, info_ptr,
620 opts.First_Column, opts.First_Line,
621 PNG_OFFSET_PIXEL);
622 }
623 #endif // PNG_WRITE_oFFs_SUPPORTED
624
625 if (opts.histogram_on)
626 {
627 /* If we are writing a histogram file, we could potentially output
628 * a pCAL chunk with the max histogram value, to allow recovery of
629 * the original timing data. However, pCAL is not yet official at
630 * the time of this writing.
631 */
632 }
633
634 png_write_info(png_ptr, info_ptr);
635
636 png_stride = color_type & PNG_COLOR_MASK_COLOR ? 3 : 1;
637
638 if (color_type & PNG_COLOR_MASK_ALPHA)
639 png_stride++;
640
641 png_stride *= (opts.OutputQuality + 7) / 8;
642
643 row_ptr = (png_byte *)POV_MALLOC(w*png_stride, "PNG write row buffer");
644
645 #if defined(PNG_WRITE_FLUSH_SUPPORTED)
646 /* Set libpng to flush the output buffers every few lines, so that
647 * in case of a rude crash we don't lose very much data.
648 */
649 png_set_flush(png_ptr, FLUSH_DIST);
650 #endif // PNG_WRITE_FLUSH_SUPPORTED
651
652 break;
653
654 case APPEND_MODE:
655
656 #if defined(PNG_WRITE_FLUSH_SUPPORTED)
657 if (setjmp(png_jmpbuf(png_ptr)))
658 {
659 // If we get here, we had a problem writing the file
660
661 png_destroy_write_struct(&png_ptr, &info_ptr);
662
663 delete out_file;
664 out_file = NULL;
665
666 return;
667 }
668
669 /* Write out the data in the PNG/zlib buffers, and set automatic
670 * flushing for every few scanlines, in case of a rude crash.
671 */
672 png_write_flush(png_ptr);
673 png_set_flush(png_ptr, FLUSH_DIST);
674 #else // !PNG_WRITE_FLUSH_SUPPORTED
675 fflush(file);
676 #endif // PNG_WRITE_FLUSH_SUPPORTED
677
678 if (!(opts.Options & TO_STDOUT))
679 {
680 delete out_file;
681 out_file = New_Checked_OStream(name, POV_File_Image_PNG, true);
682 if (out_file == NULL)
683 {
684 png_destroy_write_struct(&png_ptr, &info_ptr);
685
686 return;
687 }
688 png_set_write_fn(png_ptr, out_file, png_pov_write_data, png_pov_flush_data);
689 }
690
691 /* Delete the temporary data file. Note that the new output file
692 * is all ready to go - nothing needs to be done here.
693 */
694 if (tmp_fp != NULL)
695 {
696 delete tmp_fp;
697 tmp_fp = NULL;
698
699 if (DELETE_FILE(tmp_fname) == DELETE_FILE_ERR)
700 {
701 Warning(0,"Cannot delete temporary PNG file %s.\nPlease delete it.",tmp_fname);
702 }
703 }
704 }
705
706 valid = true;
707 }
708
709
710 /*****************************************************************************
711 *
712 * FUNCTION : Close_Png_File
713 *
714 * ARGUMENTS : FILE_HANDLE *handle
715 *
716 * MODIFIED ARGS : handle
717 *
718 * RETURN VALUE : none
719 *
720 * AUTHOR : Andreas Dilger
721 *
722 * DESCRIPTION
723 *
724 * Write any chunks coming after image (eg comments), and free all the
725 * memory associated with the PNG IO streams. Will output some rendering
726 * stats and info into tEXt chunks if POV_COMMENTS is #defined, and will
727 * also record the rendering time if CTIME is #defined.
728 *
729 * CHANGES
730 *
731 * Updated for POV-Ray 3.X - [TIW]
732 *
733 ******************************************************************************/
734
~PNG_Image()735 PNG_Image::~PNG_Image()
736 {
737 #ifdef POV_COMMENTS
738 int n, index = - 1;
739 png_text *text_ptr = NULL;
740 char allocated[NTEXT]; // Boolean array if text is MALLOCed
741 char bigtext[MAXTEXT]; // Large temporary string to print into
742 # ifdef CAMERA
743 CAMERA *Camera = Frame.Camera;
744 # endif
745 #ifdef CTIME
746 char months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
747 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
748 #endif
749 #endif
750
751 // Why are we here?
752
753 if (mode == WRITE_MODE || mode == APPEND_MODE)
754 {
755 // If no line was written to the file
756
757 if(initial_line_number == line_number)
758 {
759 if(png_ptr != NULL)
760 png_destroy_write_struct(&png_ptr, &info_ptr);
761 png_ptr = NULL;
762 }
763
764 if (png_ptr != NULL)
765 {
766 if (setjmp(png_jmpbuf(png_ptr)))
767 {
768 // If we get here, we had a problem writing the file
769
770 png_destroy_write_struct(&png_ptr, &info_ptr);
771
772 if (row_ptr != NULL)
773 {
774 POV_FREE(row_ptr);
775 row_ptr = NULL;
776 }
777
778 if (out_file != NULL)
779 {
780 delete out_file;
781 out_file = NULL;
782 }
783
784 Error("Cannot write PNG file.");
785 }
786
787 /*
788 * dholland 20110731: there is no longer any way to do this, and
789 * it seems unlikely to me that it would have worked anyhow. If
790 * something like this is needed to prevent libpng from crashing
791 * on row data that hasn't been given to it, maybe the best
792 * thing is to hand it some number of lines of phony row data.
793 * However, given that the endorsed way to deal with a write
794 * error partway through is to longjmp out and destroy
795 * everything, rows that haven't been provided must be
796 * initialized in some fashion. The fact that the logic below
797 * here writes metadata regardless of whether there's been an
798 * error may cause trouble I suppose, but the only right way to
799 * fix that is to stop misusing C++ destructors and move all
800 * that stuff out of this function.
801 */
802 #if 0 /* bad */
803 // have to check png_ptr again as it can be set to NULL by png_destroy_write_struct above.
804 if (png_ptr->row_number < png_ptr->num_rows)
805 {
806 // finished prematurely - trick into thinking done
807 png_ptr->num_rows = png_ptr->row_number;
808 png_write_finish_row(png_ptr);
809 }
810 #endif
811
812 #ifdef POV_COMMENTS // temporarily skip comment writing code
813 if (info_ptr != NULL)
814 {
815 #if defined(PNG_WRITE_tIME_SUPPORTED)
816 png_convert_from_time_t(&info_ptr->mod_time, tstart);
817 info_ptr->valid = PNG_INFO_tIME;
818 #endif // PNG_WRITE_tIME_SUPPORTED
819
820 #if defined(PNG_WRITE_tEXt_SUPPORTED)
821 text_ptr = (png_text *)POV_MALLOC(NTEXT*sizeof(png_text), "PNG comment structure");
822
823 // Init allocation flags.
824 for (n = 0; n < NTEXT; n++)
825 {
826 allocated[n] = false;
827 text_ptr[n].compression = - 1;
828 }
829
830 #ifdef TRACER
831 text_ptr[++index].key = "Author";
832 text_ptr[index].text = TRACER;
833 text_ptr[index].text_length = strlen(text_ptr[index].text);
834 #endif
835
836 #ifdef COPYRIGHT
837 text_ptr[++index].key = "Copyright";
838 // 0xA9 is the ISO-8859-1 (used in PNG tEXt) copyright character
839 sprintf(bigtext, "Copyright %c %d %s", 0xA9, info_ptr->mod_time.year,
840 COPYRIGHT);
841 text_ptr[index].text_length = strlen(bigtext);
842 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
843 strcpy(text_ptr[index].text, bigtext);
844 allocated[index] = true;
845 if (text_ptr[index].text_length > 200) // Compress if long copyright
846 text_ptr[index].compression = 0;
847 #endif
848
849 #ifdef CTIME
850 // Print the image "creation" time in RFC 1123 format
851 text_ptr[++index].key = "Creation Time";
852 sprintf(bigtext, "%02d %3s %4d %02d:%02d:%02d GMT",
853 info_ptr->mod_time.day, months[info_ptr->mod_time.month],
854 info_ptr->mod_time.year, info_ptr->mod_time.hour,
855 info_ptr->mod_time.minute, info_ptr->mod_time.second);
856 text_ptr[index].text_length = strlen(bigtext);
857 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
858 strcpy(text_ptr[index].text, bigtext);
859 allocated[index] = true;
860 #endif
861
862 text_ptr[++index].key = "Source";
863 sprintf(bigtext, "Persistence of Vision(tm) Ray Tracer v%s%s",
864 POV_RAY_VERSION, COMPILER_VER);
865 text_ptr[index].text_length = strlen(bigtext);
866 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
867 strcpy(text_ptr[index].text, bigtext);
868 allocated[index] = true;
869
870 if (!(opts.Options & FROM_STDIN))
871 {
872 text_ptr[++index].key = "Input File";
873 text_ptr[index].text = opts.Input_File_Name;
874 text_ptr[index].text_length = strlen(text_ptr[index].text);
875 }
876
877 #ifdef CAMERA
878 text_ptr[++index].key = "POV Camera";
879 sprintf(bigtext, "Location: %7g %7g %7g\n"
880 " Direction: %7g %7g %7g\n"
881 " Up: %7g %7g %7g\n"
882 " Right: %7g %7g %7g\n"
883 " Sky: %7g %7g %7g",
884 Camera->Location[X], Camera->Location[Y], Camera->Location[Z],
885 Camera->Direction[X], Camera->Direction[Y], Camera->Direction[Z],
886 Camera->Up[X], Camera->Up[Y], Camera->Up[Z],
887 Camera->Right[X], Camera->Right[Y], Camera->Right[Z],
888 Camera->Sky[X], Camera->Sky[Y], Camera->Sky[Z]);
889 text_ptr[index].text_length = strlen(bigtext);
890 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
891 strcpy(text_ptr[index].text, bigtext);
892 allocated[index] = true;
893 #endif
894
895 if (opts.FrameSeq.Clock_Value != 0)
896 {
897 text_ptr[++index].key = "POV Clock";
898 sprintf(bigtext, "%g", opts.FrameSeq.Clock_Value);
899 text_ptr[index].text_length = strlen(bigtext);
900 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
901 strcpy(text_ptr[index].text, bigtext);
902 allocated[index] = true;
903 }
904
905 if (opts.Quality != 9)
906 {
907 text_ptr[++index].key = "Rendering Quality";
908 sprintf(bigtext, "%d", opts.Quality);
909 text_ptr[index].text_length = strlen(bigtext);
910 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
911 strcpy(text_ptr[index].text, bigtext);
912 allocated[index] = true;
913 }
914
915 text_ptr[++index].key = "Rendering Time";
916 sprintf(bigtext, "%g s", trender);
917 text_ptr[index].text_length = strlen(bigtext);
918 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
919 strcpy(text_ptr[index].text, bigtext);
920 allocated[index] = true;
921
922 info_ptr->num_text = index + 1;
923 info_ptr->max_text = NTEXT;
924 info_ptr->text = text_ptr;
925 #endif // PNG_WRITE_tEXt_SUPPORTED
926 }
927 #endif // POV_COMMENTS
928
929 png_write_end(png_ptr, info_ptr);
930 png_destroy_write_struct(&png_ptr, &info_ptr);
931
932 #ifdef POV_COMMENTS
933 if (text_ptr != NULL)
934 {
935 for (n = 0; n <= index; n++)
936 {
937 if (allocated[n])
938 {
939 POV_FREE(text_ptr[n].text);
940 }
941 }
942
943 POV_FREE(text_ptr);
944 text_ptr = NULL;
945 }
946 #endif // POV_COMMENTS
947
948 }
949
950 if (row_ptr != NULL)
951 {
952 POV_FREE(row_ptr);
953 row_ptr = NULL;
954 }
955
956 if (out_file != NULL && !(opts.Options & TO_STDOUT))
957 {
958 delete out_file;
959 out_file = NULL;
960 }
961 }
962 else // READ_MODE
963 {
964 if (in_file != NULL)
965 {
966 delete in_file;
967 in_file = NULL;
968 }
969
970 if (o_png_ptr != NULL)
971 {
972 png_destroy_read_struct(&o_png_ptr, (png_infopp)NULL, (png_infopp)NULL);
973 }
974 }
975 }
976
977
978 /*****************************************************************************
979 *
980 * FUNCTION : Write_Png_Line
981 *
982 * ARGUMENTS : handle, line_data, line_number
983 *
984 * MODIFIED ARGS : none
985 *
986 * RETURN VALUE : none
987 *
988 * AUTHOR : Andreas Dilger
989 *
990 * DESCRIPTION
991 *
992 * Write a line of data to the PNG file
993 *
994 * CHANGES
995 *
996 * Updated for POV-Ray 3.X - [TIW]
997 * Updated to do flush output to reduce data loss - [AED]
998 * Updated to output Alpha channel if requested - [AED]
999 * Updated to output grayscale heightfield if requested - [AED]
1000 * Updated to allow grayscale in 5-8 bpp if desired, Oct 1995 - [AED]
1001 * Updated to allow grayscale and alpha together, Oct 1995 - [AED]
1002 * Changed how bit-depths 9-15 get promoted to 16 bits based on new
1003 * recommendations from the PNG Group, Nov 1995 - [AED]
1004 *
1005 ******************************************************************************/
1006
Write_Line(COLOUR * line_data)1007 void PNG_Image::Write_Line(COLOUR *line_data)
1008 {
1009 if(valid == false)
1010 Error("Cannot access output image file.");
1011
1012 register int col, j;
1013 int himask;
1014 int color;
1015 png_byte color_type;
1016
1017 color_type = png_get_color_type(png_ptr, info_ptr);
1018
1019 /*
1020 * We must copy all the values because PNG expects RGBRGB bytes, but
1021 * POV-Ray stores RGB components in separate arrays as floats. In
1022 * order to use the full scale values at the lower bit depth, PNG
1023 * recommends filling the low-order bits with a copy of the high-order
1024 * bits. However, studies have shown that filling the low order bits
1025 * with constant bits significantly improves compression, which I'm
1026 * doing here. Note that since the true bit depth is stored in the
1027 * sBIT chunk, the extra packed bits are not important.
1028 */
1029
1030 switch (opts.OutputQuality)
1031 {
1032 case 5:
1033 case 6:
1034 case 7:
1035 // Handle shifting for arbitrary output bit depth
1036
1037 himask = 0xFF ^ ((1 << (8 - opts.OutputQuality)) - 1);
1038
1039 if ((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1040 {
1041 for (col = j = 0; col < width; col++, j += png_stride)
1042 {
1043 color = (png_byte)floor((GREY_SCALE(line_data[col])) * 255.0);
1044
1045 // Use left-bit replication (LBR) for bit depths < 8
1046 row_ptr[j] = color & himask;
1047 row_ptr[j] |= color >> opts.OutputQuality;
1048
1049 /* Handle Alpha here if needed - must use exact bit replication
1050 * instead of truncation or 100... termination
1051 */
1052 if (color_type & PNG_COLOR_MASK_ALPHA)
1053 {
1054 color = 255 - (int)floor(line_data[col][pTRANSM] * 255.0);
1055
1056 row_ptr[j + 1] = color & himask;
1057 row_ptr[j + 1] |= color >> opts.OutputQuality;
1058 }
1059 }
1060 }
1061 else
1062 {
1063 for (col = j = 0; col < width; col++, j += png_stride)
1064 {
1065 color = (int)floor(line_data[col][pRED] * 255.0);
1066
1067 row_ptr[j] = color & himask;
1068 row_ptr[j] |= color >> opts.OutputQuality;
1069
1070 color = (int)floor(line_data[col][pGREEN] * 255.0);
1071
1072 row_ptr[j + 1] = color & himask;
1073 row_ptr[j + 1] |= color >> opts.OutputQuality;
1074
1075 color = (int)floor(line_data[col][pBLUE] * 255.0);
1076
1077 row_ptr[j + 2] = color & himask;
1078 row_ptr[j + 2] |= color >> opts.OutputQuality;
1079
1080 // Handle Alpha here if needed
1081 if (color_type & PNG_COLOR_MASK_ALPHA)
1082 {
1083 color = 255 - (int)floor(line_data[col][pTRANSM] * 255.0);
1084
1085 row_ptr[j + 3] = color & himask;
1086 row_ptr[j + 3] |= color >> opts.OutputQuality;
1087 }
1088 }
1089 }
1090 break;
1091
1092 case 8:
1093 if ((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1094 {
1095 for (col = j = 0; col < width; col++, j += png_stride)
1096 {
1097 row_ptr[j] = (png_byte)floor((GREY_SCALE( line_data[col] )) * 255.0);
1098
1099 // Handle Alpha here if needed
1100 if (color_type & PNG_COLOR_MASK_ALPHA)
1101 {
1102 row_ptr[j+1] = (png_byte)(255-floor(line_data[col][pTRANSM]*255.0));
1103 }
1104 }
1105 }
1106 else
1107 {
1108 for (col = j = 0; col < width; col++, j += png_stride)
1109 {
1110 row_ptr[j] = (png_byte)floor(line_data[col][pRED] * 255.0);
1111 row_ptr[j + 1] = (png_byte)floor(line_data[col][pGREEN] * 255.0);
1112 row_ptr[j + 2] = (png_byte)floor(line_data[col][pBLUE] * 255.0);
1113
1114 // Handle Alpha here if needed
1115 if (color_type & PNG_COLOR_MASK_ALPHA)
1116 {
1117 row_ptr[j+3] = (png_byte)(255-floor(line_data[col][pTRANSM]*255.0));
1118 }
1119 }
1120 }
1121 break;
1122
1123 case 16:
1124 if ((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1125 {
1126 for (col = j = 0; col < width; col++, j += png_stride)
1127 {
1128 color = (int)floor((GREY_SCALE( line_data[col] )) * 65535.0);
1129
1130 row_ptr[j] = color >> 8;
1131 row_ptr[j+1] = color & 0xff;
1132
1133 // Handle Alpha here if needed
1134 if (color_type & PNG_COLOR_MASK_ALPHA)
1135 {
1136 color = 65535 - (int)floor(line_data[col][pTRANSM] * 65535.0);
1137
1138 row_ptr[j+2] = color >> 8;
1139 row_ptr[j+3] = color & 0xff;
1140 }
1141 }
1142 }
1143 else
1144 {
1145 for (col = j = 0; col < width; col++, j += png_stride)
1146 {
1147 color = (int)floor(line_data[col][pRED] * 65535.0);
1148
1149 row_ptr[j] = color >> 8;
1150 row_ptr[j + 1] = color & 0xFF;
1151
1152 color = (int)floor(line_data[col][pGREEN] * 65535.0);
1153
1154 row_ptr[j + 2] = color >> 8;
1155 row_ptr[j + 3] = color & 0xFF;
1156
1157 color = (int)floor(line_data[col][pBLUE] * 65535.0);
1158
1159 row_ptr[j + 4] = color >> 8;
1160 row_ptr[j + 5] = color & 0xFF;
1161
1162 // Handle Alpha here if needed
1163 if (color_type & PNG_COLOR_MASK_ALPHA)
1164 {
1165 color = 65535 - (int)floor(line_data[col][pTRANSM] * 65535.0);
1166
1167 row_ptr[j + 6] = color >> 8;
1168 row_ptr[j + 7] = color & 0xFF;
1169 }
1170 }
1171 }
1172 break;
1173
1174 default: // OutputQuality 9 - 15
1175 // Handle shifting for arbitrary output bit depth
1176 himask = 0xFF ^ ((1 << (16 - opts.OutputQuality)) - 1);
1177
1178 if ((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1179 {
1180 for (col = j = 0; col < width; col++, j += png_stride)
1181 {
1182 color = (int)floor((GREY_SCALE( line_data[col] )) * 65535.0);
1183
1184 row_ptr[j] = color >> 8;
1185 row_ptr[j + 1] = color & himask;
1186 row_ptr[j + 1] |= color >> opts.OutputQuality;
1187
1188 if (color_type & PNG_COLOR_MASK_ALPHA)
1189 {
1190 color = 65535 - (int)floor(line_data[col][pTRANSM] * 65535.0);
1191
1192 row_ptr[j + 2] = color >> 8;
1193 row_ptr[j + 3] = color & himask;
1194 row_ptr[j + 3] |= color >> opts.OutputQuality;
1195 }
1196 }
1197 }
1198 else
1199 {
1200 for (col = j = 0; col < width; col++, j += png_stride)
1201 {
1202 color = (int)floor(line_data[col][pRED] * 65535.0);
1203
1204 row_ptr[j] = color >> 8;
1205 row_ptr[j + 1] = color & himask;
1206 row_ptr[j + 1] |= color >> opts.OutputQuality;
1207
1208 color = (int)floor(line_data[col][pGREEN] * 65535.0);
1209
1210 row_ptr[j + 2] = color >> 8;
1211 row_ptr[j + 3] = color & himask;
1212 row_ptr[j + 3] |= color >> opts.OutputQuality;
1213
1214 color = (int)floor(line_data[col][pBLUE] * 65535.0);
1215
1216 row_ptr[j + 4] = color >> 8;
1217 row_ptr[j + 5] = color & himask;
1218 row_ptr[j + 5] |= color >> opts.OutputQuality;
1219
1220 // Handle Alpha here if needed
1221 if (color_type & PNG_COLOR_MASK_ALPHA)
1222 {
1223 color = 65535 - (int)floor(line_data[col][pTRANSM] * 65535.0);
1224
1225 row_ptr[j + 6] = color >> 8;
1226 row_ptr[j + 7] = color & himask;
1227 row_ptr[j + 7] |= color >> opts.OutputQuality;
1228 }
1229 }
1230 }
1231 }
1232
1233 if (setjmp(png_jmpbuf(png_ptr)))
1234 {
1235 // If we get here, we had a problem writing the file
1236 delete out_file;
1237 out_file = NULL;
1238
1239 Error("Cannot write PNG output data to %s.", filename);
1240 }
1241
1242 // Write out a scanline
1243 png_write_row(png_ptr, row_ptr);
1244 line_number++;
1245
1246 out_file->flush();
1247 }
1248
1249
1250 /*****************************************************************************
1251 *
1252 * FUNCTION : Read_Png_Line
1253 *
1254 * ARGUMENTS : FILE_HANDLE *handle; COLOUR *line_data; int *line_number;
1255 *
1256 * MODIFIED ARGS : none
1257 *
1258 * RETURN VALUE : 1 if no error exit
1259 *
1260 * AUTHOR : Andreas Dilger
1261 *
1262 * DESCRIPTION
1263 *
1264 * Read a line of PNG data
1265 *
1266 * CHANGES
1267 *
1268 * Updated for POV-Ray 3.X - [TIW]
1269 * Updated to handle interrupted file resuming Sept 1995 - [AED]
1270 * Updated to support grayscale and alpha together, Oct 1995 - [AED]
1271 *
1272 ******************************************************************************/
1273
Read_Line(COLOUR * line_data)1274 int PNG_Image::Read_Line(COLOUR *line_data)
1275 {
1276 if(valid == false)
1277 Error("Cannot access output image file.");
1278
1279 register int col, j, step;
1280 png_byte bit_depth, color_type;
1281
1282 if (setjmp(png_jmpbuf(o_png_ptr)))
1283 {
1284 /* If we get here, we had a problem reading the file, which probably
1285 * means that we have read all the available data, rather than a real
1286 * error, but there is no sure way to know.
1287 */
1288 return 0;
1289 }
1290
1291 if (setjmp(png_jmpbuf(png_ptr)))
1292 {
1293 // If we get here, we had a problem writing the new file
1294 delete in_file;
1295 in_file = NULL;
1296 delete tmp_fp;
1297 tmp_fp = NULL;
1298
1299 if (DELETE_FILE(filename) != DELETE_FILE_ERR)
1300 {
1301 RENAME_FILE(tmp_fname,filename); // Move original file back
1302 }
1303
1304 return -1;
1305 }
1306
1307 // Read in another row if available
1308 png_read_row(o_png_ptr, row_ptr, NULL);
1309
1310 // We won't get here if there was a read error
1311 png_write_row(png_ptr, row_ptr);
1312
1313 /*
1314 * We must copy all the values because PNG supplies RGBRGB, but POV-Ray
1315 * stores RGB components in separate arrays. Note that since we have
1316 * already written the data out to the temporary file, we only need to
1317 * use the top 8 bits for the line_data info as it is only used for
1318 * potential screen output.
1319 */
1320
1321 bit_depth = png_get_bit_depth(o_png_ptr, info_ptr);
1322 color_type = png_get_color_type(o_png_ptr, info_ptr);
1323
1324 // How many bytes in a sample
1325 step = (bit_depth <= 8) ? 1 : 2;
1326
1327 if ((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1328 {
1329 for (col = j = 0; col < width; col++, j += png_stride)
1330 {
1331 line_data[col][pRED] = (DBL)row_ptr[j] / 255.0;
1332 line_data[col][pGREEN] = (DBL)row_ptr[j] / 255.0;
1333 line_data[col][pBLUE] = (DBL)row_ptr[j] / 255.0;
1334
1335 if (color_type & PNG_COLOR_MASK_ALPHA)
1336 {
1337 line_data[col][pTRANSM] = (DBL)(255 - row_ptr[j + step]) / 255.0;
1338 }
1339 }
1340 }
1341 else
1342 {
1343 for (col = j = 0; col < width; col++, j += png_stride)
1344 {
1345 line_data[col][pRED] = (DBL)row_ptr[j] / 255.0;
1346 line_data[col][pGREEN] = (DBL)row_ptr[j + step] / 255.0;
1347 line_data[col][pBLUE] = (DBL)row_ptr[j + 2*step] / 255.0;
1348
1349 if (color_type & PNG_COLOR_MASK_ALPHA)
1350 {
1351 line_data[col][pTRANSM] = (DBL)(255 - row_ptr[j + 3*step]) / 255.0;
1352 }
1353 }
1354 }
1355
1356 // Note that line_number is that of the first blank (i.e. missing) row
1357 #if defined(PNG_READ_oFFs_SUPPORTED)
1358 line_number = png_get_y_offset_pixels(png_ptr, info_ptr) + png_get_current_row_number(png_ptr);
1359 #else
1360 line_number = png_get_current_row_number(png_ptr);
1361 #endif
1362
1363 return 1;
1364 }
1365
1366
1367 /*****************************************************************************
1368 *
1369 * FUNCTION : Read_Png_Image
1370 *
1371 * ARGUMENTS : IMAGE *Image; char *name;
1372 *
1373 * MODIFIED ARGS : Image
1374 *
1375 * RETURN VALUE : none
1376 *
1377 * AUTHOR : Andreas Dilger
1378 *
1379 * DESCRIPTION
1380 *
1381 * Reads a PNG image into an RGB image buffer
1382 *
1383 * CHANGES
1384 *
1385 * Updated for POV-Ray 3.X - [TIW]
1386 * Updated to allow grayscale and alpha together, Oct 1995 - [AED]
1387 * Fixed palette size for grayscale images with bit-depth <= 8, Nov 1995 [AED]
1388 * Changed how grayscale images > 8bpp are stored based on use, Nov 1995 [AED]
1389 *
1390 ******************************************************************************/
1391
Read_Png_Image(IMAGE * Image,char * name)1392 void Read_Png_Image(IMAGE *Image, char *name)
1393 {
1394 unsigned int width, height;
1395 int row, col, j;
1396 int stride;
1397 IStream *filep;
1398 png_struct *r_png_ptr;
1399 png_info *r_info_ptr;
1400 png_byte **row_ptrs;
1401 png_byte bit_depth;
1402 png_byte color_type;
1403
1404 // Start by trying to open the file
1405 if((filep = Locate_File(name,POV_File_Image_PNG,NULL,true)) == NULL)
1406 Error("Cannot open PNG file.");
1407
1408 if(((r_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)true, png_pov_err, png_pov_warn)) == NULL) ||
1409 ((r_info_ptr = png_create_info_struct(r_png_ptr)) == NULL))
1410 Error("Cannot allocate PNG data structures");
1411
1412 if(setjmp(png_jmpbuf(r_png_ptr)))
1413 {
1414 // If we get here, we had a problem reading the file
1415 png_destroy_read_struct(&r_png_ptr, &r_info_ptr, (png_infopp)NULL);
1416 Error("Cannot read PNG image.");
1417 }
1418
1419 // set up the input control
1420 png_set_read_fn(r_png_ptr, filep, png_pov_read_data);
1421
1422 // read the file information
1423 png_read_info(r_png_ptr, r_info_ptr);
1424
1425 width = png_get_image_width(r_png_ptr, r_info_ptr);
1426 height = png_get_image_height(r_png_ptr, r_info_ptr);
1427 bit_depth = png_get_bit_depth(r_png_ptr, r_info_ptr);
1428 color_type = png_get_color_type(r_png_ptr, r_info_ptr);
1429
1430 Image->iwidth = width;
1431 Image->iheight = height;
1432 Image->width = (DBL)width;
1433 Image->height = (DBL)height;
1434
1435 // Allocate buffers for the image
1436 stride = 1;
1437
1438 // color palette image
1439 if(color_type == PNG_COLOR_TYPE_PALETTE)
1440 {
1441 IMAGE_COLOUR *cmap;
1442 png_color *png_cmap;
1443 int cmap_len;
1444 int index;
1445
1446 Image->Colour_Map_Size = cmap_len;
1447
1448 cmap = (IMAGE_COLOUR *)POV_MALLOC(cmap_len*sizeof(IMAGE_COLOUR), "PNG image color map");
1449
1450 Image->Colour_Map = cmap;
1451 png_get_PLTE(r_png_ptr, r_info_ptr, &png_cmap, &cmap_len);
1452
1453 for(index = 0; index < cmap_len; index++)
1454 {
1455 cmap[index].Red = png_cmap[index].red;
1456 cmap[index].Green = png_cmap[index].green;
1457 cmap[index].Blue = png_cmap[index].blue;
1458 cmap[index].Filter = 0;
1459 cmap[index].Transmit = 0;
1460 }
1461
1462 png_byte *trans_alpha;
1463 int num_trans;
1464 if (png_get_tRNS(r_png_ptr, r_info_ptr, &trans_alpha, &num_trans, NULL))
1465 {
1466 for (index = 0; index < num_trans; index++)
1467 cmap[index].Transmit = 255 - trans_alpha[index];
1468 }
1469
1470 Image->data.map_lines = (unsigned char **)POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
1471
1472 // tellg pnglib to expand data to 1 pixel/byte
1473 png_set_packing(r_png_ptr);
1474 }
1475 // grayscale palette image
1476 else if((color_type == PNG_COLOR_TYPE_GRAY) && (bit_depth <= 8))
1477 {
1478 IMAGE_COLOUR *cmap;
1479 int cmap_len;
1480 int index;
1481
1482 Image->Colour_Map_Size = cmap_len = 1 << bit_depth;
1483
1484 cmap = (IMAGE_COLOUR *)POV_MALLOC(cmap_len*sizeof(IMAGE_COLOUR), "PNG image color map");
1485
1486 Image->Colour_Map = cmap;
1487
1488 for(index = 0; index < cmap_len; index++)
1489 {
1490 cmap[index].Red =
1491 cmap[index].Green =
1492 cmap[index].Blue = index * 255 / (cmap_len - 1);
1493 cmap[index].Filter = 0;
1494 cmap[index].Transmit = 0;
1495 }
1496
1497 png_byte *trans_alpha;
1498 int num_trans;
1499 if (png_get_tRNS(r_png_ptr, r_info_ptr, &trans_alpha, &num_trans, NULL))
1500 {
1501 for (index = 0; index < num_trans; index++)
1502 cmap[index].Transmit = 255 - trans_alpha[index];
1503 }
1504
1505 Image->data.map_lines = (unsigned char **)POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
1506
1507 // tellg pnglib to expand data to 1 pixel/byte
1508 png_set_packing(r_png_ptr);
1509 }
1510 // grayscale image
1511 else if((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1512 {
1513 Image->Colour_Map = NULL;
1514
1515 if(color_type & PNG_COLOR_MASK_ALPHA)
1516 {
1517 if(bit_depth <= 8)
1518 {
1519 Image->data.rgb8_lines = (IMAGE8_LINE *)POV_MALLOC(height * sizeof(IMAGE8_LINE), "PNG image");
1520 stride = 2;
1521 }
1522 else
1523 {
1524 Image->data.rgb16_lines = (IMAGE16_LINE *)POV_MALLOC(height * sizeof(IMAGE16_LINE), "PNG image");
1525 stride = 4;
1526 }
1527 }
1528 else
1529 {
1530 if(bit_depth <= 8)
1531 {
1532 Image->data.map_lines = (unsigned char **)POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
1533 stride = 1;
1534 }
1535 else
1536 {
1537 Image->Image_Type |= IS16BITIMAGE;
1538 Image->Image_Type |= IS16GRAYIMAGE;
1539 Image->data.gray16_lines = (unsigned short **)POV_MALLOC(height * sizeof(unsigned short *), "PNG image");
1540 stride = 2;
1541 }
1542 }
1543 }
1544 // color image
1545 else if((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA))
1546 {
1547 Image->Colour_Map = NULL;
1548
1549 if(bit_depth > 8)
1550 {
1551 Image->Image_Type |= IS16BITIMAGE;
1552 Image->data.rgb16_lines = (IMAGE16_LINE *)POV_MALLOC(height * sizeof(IMAGE16_LINE), "PNG image");
1553
1554 if(color_type == PNG_COLOR_TYPE_RGB)
1555 stride = 6;
1556 else // PNG_COLOR_TYPE_RGB_ALPHA
1557 stride = 8;
1558 }
1559 else
1560 {
1561 Image->data.rgb8_lines = (IMAGE8_LINE *)POV_MALLOC(height * sizeof(IMAGE8_LINE), "PNG image");
1562
1563 if(color_type == PNG_COLOR_TYPE_RGB)
1564 stride = 3;
1565 else // PNG_COLOR_TYPE_RGB_ALPHA
1566 stride = 4;
1567 }
1568 }
1569 else // Unknown PNG type
1570 Error("Unsupported color type %d in PNG image.", color_type);
1571
1572 // tellg pnglib to handle the gamma conversion for you. Note that
1573 // GammaFactor * DisplayFactor = assumed_gamma, so we are converting
1574 // images into the "internal gamma" space of POV (rather than to a
1575 // gamma of 1.0) to avoid doing gamma correction on image maps twice for
1576 // those scene files which don't have a gamma of 1.0. For POV 3.0,
1577 // we will only do input gamma conversion on those files which will be
1578 // used as image maps, and the other types will load the raw pixel values.
1579 #if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_READ_gAMA_SUPPORTED)
1580 if (png_get_valid(r_png_ptr, r_info_ptr, PNG_INFO_gAMA) && (Image->Image_Type & IMAGE_FTYPE)) {
1581 double gamma;
1582 png_get_gAMA(r_png_ptr, r_info_ptr, &gamma);
1583 png_set_gamma(r_png_ptr, opts.GammaFactor*opts.DisplayGamma, gamma);
1584 }
1585 #endif // PNG_READ_GAMMA_SUPPORTED and PNG_READ_gAMA_SUPPORTED
1586
1587 png_set_interlace_handling(r_png_ptr);
1588 png_read_update_info(r_png_ptr, r_info_ptr);
1589
1590 // Allocate row buffers for the input
1591 row_ptrs = (png_byte **)POV_MALLOC(height*sizeof(png_byte *), "PNG image");
1592
1593 for(row = 0; row <
1594 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1595 (int)
1596 #endif
1597 height; row++)
1598 row_ptrs[row] = (png_byte *)POV_MALLOC(png_get_rowbytes(r_png_ptr, r_info_ptr), "PNG image line");
1599
1600 // Read in the entire image
1601 png_read_image(r_png_ptr, row_ptrs);
1602
1603 // We must copy all the values because PNG supplies RGBRGB, but POV-Ray
1604 // stores RGB components in separate arrays
1605 if(Image->Colour_Map == NULL)
1606 {
1607 for(row = 0; row <
1608 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1609 (int)
1610 #endif
1611 height; row++)
1612 {
1613 // grayscale image
1614 if((color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1615 {
1616 // with alpha channel
1617 if(color_type & PNG_COLOR_MASK_ALPHA)
1618 {
1619 if(bit_depth <= 8)
1620 {
1621 IMAGE8_LINE *rgb8_lines = &Image->data.rgb8_lines[row];
1622
1623 rgb8_lines->red = (unsigned char *)POV_MALLOC(width, "PNG image line");
1624 rgb8_lines->green = (unsigned char *)POV_MALLOC(width, "PNG image line");
1625 rgb8_lines->blue = (unsigned char *)POV_MALLOC(width, "PNG image line");
1626 rgb8_lines->transm = (unsigned char *)POV_MALLOC(width,"PNG image line");
1627
1628 for(col = j = 0; col <
1629 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1630 (int)
1631 #endif
1632 width; col ++, j += stride)
1633 {
1634 rgb8_lines->red[col] =
1635 rgb8_lines->green[col] =
1636 rgb8_lines->blue[col] = row_ptrs[row][j];
1637 rgb8_lines->transm[col] = 255 - row_ptrs[row][j + 1];
1638 }
1639
1640 POV_FREE(row_ptrs[row]);
1641 }
1642 else
1643 {
1644 IMAGE16_LINE *rgb16_lines = &Image->data.rgb16_lines[row];
1645
1646 rgb16_lines->red = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1647 rgb16_lines->green = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1648 rgb16_lines->blue = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1649 rgb16_lines->transm = (unsigned short *)POV_MALLOC(width * 2,"PNG image line");
1650
1651 for(col = j = 0; col <
1652 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1653 (int)
1654 #endif
1655 width; col ++, j += stride)
1656 {
1657 rgb16_lines->red[col] =
1658 rgb16_lines->green[col] =
1659 rgb16_lines->blue[col] = ((unsigned short)row_ptrs[row][j] << 8) | row_ptrs[row][j + 1];
1660 rgb16_lines->transm[col] = 65535 - (((unsigned short)row_ptrs[row][j + 2] << 8) | row_ptrs[row][j + 1]);
1661 }
1662
1663 POV_FREE(row_ptrs[row]);
1664 }
1665 }
1666 // without alpha channel
1667 else
1668 {
1669 if(bit_depth <= 8)
1670 {
1671 Image->data.map_lines[row] = row_ptrs[row];
1672
1673 // Do not free row_ptrs[row] here!
1674 }
1675 else
1676 {
1677 unsigned short *gray16_lines;
1678
1679 gray16_lines = Image->data.gray16_lines[row] = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1680
1681 for(col = j = 0; col <
1682 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1683 (int)
1684 #endif
1685 width; col ++, j += stride)
1686 gray16_lines[col] = ((unsigned short)row_ptrs[row][j] << 8) | row_ptrs[row][j + 1];
1687
1688 POV_FREE(row_ptrs[row]);
1689 }
1690 }
1691 }
1692 // color image
1693 else // color_type & PNG_COLOR_MASK_COLOR
1694 {
1695 if(bit_depth <= 8)
1696 {
1697 IMAGE8_LINE *rgb8_lines = &Image->data.rgb8_lines[row];
1698
1699 rgb8_lines->red = (unsigned char *)POV_MALLOC(width, "PNG image line");
1700 rgb8_lines->green = (unsigned char *)POV_MALLOC(width, "PNG image line");
1701 rgb8_lines->blue = (unsigned char *)POV_MALLOC(width, "PNG image line");
1702
1703 if(color_type & PNG_COLOR_MASK_ALPHA)
1704 rgb8_lines->transm = (unsigned char *)POV_MALLOC(width, "PNG image line");
1705 else
1706 rgb8_lines->transm = NULL;
1707
1708 for(col = j = 0; col <
1709 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1710 (int)
1711 #endif
1712 width; col ++, j += stride)
1713 {
1714 rgb8_lines->red[col] = row_ptrs[row][j];
1715 rgb8_lines->green[col] = row_ptrs[row][j + 1];
1716 rgb8_lines->blue[col] = row_ptrs[row][j + 2];
1717
1718 if(color_type & PNG_COLOR_MASK_ALPHA)
1719 rgb8_lines->transm[col] = 255 - row_ptrs[row][j + 3];
1720 }
1721
1722 POV_FREE(row_ptrs[row]);
1723 }
1724 else
1725 {
1726 IMAGE16_LINE *rgb16_lines = &Image->data.rgb16_lines[row];
1727
1728 rgb16_lines->red = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1729 rgb16_lines->green = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1730 rgb16_lines->blue = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1731
1732 if(color_type & PNG_COLOR_MASK_ALPHA)
1733 rgb16_lines->transm = (unsigned short *)POV_MALLOC(width * 2, "PNG image line");
1734 else
1735 rgb16_lines->transm = NULL;
1736
1737 for(col = j = 0; col <
1738 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1739 (int)
1740 #endif
1741 width; col ++, j += stride)
1742 {
1743 rgb16_lines->red[col] = ((unsigned short)row_ptrs[row][j] << 8) | row_ptrs[row][j + 1];
1744 rgb16_lines->green[col] = ((unsigned short)row_ptrs[row][j + 2] << 8) | row_ptrs[row][j + 3];
1745 rgb16_lines->blue[col] = ((unsigned short)row_ptrs[row][j + 4] << 8) | row_ptrs[row][j + 5];
1746
1747 if(color_type & PNG_COLOR_MASK_ALPHA)
1748 rgb16_lines->transm[col] = 65535 - (((unsigned short)row_ptrs[row][j + 6] << 8) | row_ptrs[row][j + 7]);
1749 }
1750
1751 POV_FREE(row_ptrs[row]);
1752 }
1753 }
1754 }
1755 }
1756 else // grayscale palette image or color palette image
1757 {
1758 for(row = 0; row <
1759 #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
1760 (int)
1761 #endif
1762 height; row++)
1763 Image->data.map_lines[row] = row_ptrs[row];
1764
1765 // Do not free row_ptrs[row] here!
1766 }
1767
1768 // Clean up the rest of the PNG memory and such
1769 POV_FREE(row_ptrs);
1770
1771 // read the rest of the file, getting any additional chunks in png_info
1772 png_read_end(r_png_ptr, r_info_ptr);
1773
1774 // clean up after the read, and free any memory allocated
1775 png_destroy_read_struct(&r_png_ptr, &r_info_ptr, (png_infopp)NULL);
1776
1777 delete filep;
1778 }
1779
1780 END_POV_NAMESPACE
1781