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