Attempt to fix nightly build.
[AROS-Contrib.git] / gfx / povray / png_pov.c
blob90ceb4e73562cc03affb0a1af748fd41adbd6453
1 /*****************************************************************************
2 * png_pov.c
4 * This module contains the code to read and write the PNG output file
6 * from Persistence of Vision(tm) Ray Tracer
7 * Copyright 1996,1999 Persistence of Vision Team
8 *---------------------------------------------------------------------------
9 * NOTICE: This source code file is provided so that users may experiment
10 * with enhancements to POV-Ray and to port the software to platforms other
11 * than those supported by the POV-Ray Team. There are strict rules under
12 * which you are permitted to use this file. The rules are in the file
13 * named POVLEGAL.DOC which should be distributed with this file.
14 * If POVLEGAL.DOC is not available or for more info please contact the POV-Ray
15 * Team Coordinator by email to team-coord@povray.org or visit us on the web at
16 * http://www.povray.org. The latest version of POV-Ray may be found at this site.
18 * This program is based on the popular DKB raytracer version 2.12.
19 * DKBTrace was originally written by David K. Buck.
20 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
22 * Original patch copyright 1995 Andreas Dilger
23 * Updated for POV 3.0 by Tim Wegner, August 1995.
24 * Updated to allow resuming by Andreas Dilger, Sept 1995.
25 * Updated to support Alpha channel input/output by Andreas Dilger, Sept 1995
26 * Updated to set the flush distance based on the file buffer size, Dec 1995
27 * Updated to use the libpng 0.87 messaging functions, Dec 1995
28 * Updated to use the libpng 0.89 structure interface, Jun 1996
30 * Modifications by Hans-Detlev Fink, January 1999, used with permission
32 *****************************************************************************/
34 /*****************************************************************************
35 * This code requires the use of libpng, Group 42's PNG reference library.
36 * libpng is Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
38 * This code also requires the use of Zlib,
39 * Zlib is Copyright (C) 1995 Jean-loup Gailly and Mark Adler
41 * The latest version of these libraries are available at ftp.uu.net as
43 * /graphics/png/libpngXX.tar.gz.
44 * /archiver/zlib/zlib-XX.tar.gz.
46 * where XX is the latest version of the library.
48 *****************************************************************************/
50 #include "frame.h"
51 #include "povproto.h"
52 #include "povray.h"
53 #include "optout.h"
54 #include "png.h"
55 #include "png_pov.h"
59 /*****************************************************************************
60 * Local preprocessor defines
61 ******************************************************************************/
63 /* Number of scanlines between output flushes, and hence the maximum number of
64 * lines lost for an interrupted render. Note that making it much smaller
65 * than about 10 for a 640x480 image will noticably degrade compression.
66 * If a very small buffer is specified, we don't want to flush more than once
67 * every 10 lines or so (assuming a 2:1 compression ratio).
70 #define FLUSH_DIST 10
71 #define FLUSH_DIST (*width >= 640 ? 10 : 6400 / *width)
73 #define FLUSH_DIST ((opts.Options & BUFFERED_OUTPUT && \
74 handle->buffer_size > (*width * png_stride * 5)) ? \
75 (handle->buffer_size / (*width * png_stride)): \
76 (*width >= 640 ? 10 : 6400 / *width))
78 #define NTEXT 15 /* Maximum number of tEXt comment blocks */
79 #define MAXTEXT 1024 /* Maximum length of a tEXt message */
83 /*****************************************************************************
84 * Local typedefs
85 ******************************************************************************/
89 /*****************************************************************************
90 * Local variables
91 ******************************************************************************/
93 static png_struct *png_ptr = NULL;
94 static png_info *info_ptr = NULL;
95 static png_struct *o_png_ptr = NULL;
96 static png_byte *row_ptr = NULL;
97 static int png_stride;
98 static char tmp_fname[FILE_NAME_LENGTH];
99 static FILE *tmp_fp = NULL;
103 /*****************************************************************************
104 * Static functions
105 ******************************************************************************/
107 static int Open_Png_File (FILE_HANDLE *handle, char *name, int *width,
108 int *height, int buffer_size, int mode);
109 static void Write_Png_Line (FILE_HANDLE *handle, COLOUR *line_data,
110 int line_number);
111 static int Read_Png_Line (FILE_HANDLE *handle, COLOUR *line_data,
112 int *line_number);
113 static void Close_Png_File (FILE_HANDLE *handle);
116 /* These are replacement error and warning functions for the libpng code */
117 static void png_pov_err (png_structp, png_const_charp);
118 static void png_pov_warn (png_structp, png_const_charp);
120 /* This is an internal function for libpng */
121 extern
122 #ifdef __cplusplus
123 "C" {
124 #endif
126 void png_write_finish_row (png_structp);
128 #ifdef __cplusplus
130 #endif
133 /*****************************************************************************
135 * FUNCTION : Get_Png_File_Handle
137 * ARGUMENTS : none
139 * MODIFIED ARGS : none
141 * RETURN VALUE : File Handle
143 * AUTHOR : Andreas Dilger
145 * DESCRIPTION
147 * Allocate memory for and setup file handle for PNG file.
149 * CHANGES
151 * Updated for POV-Ray 3.X - [TIW]
153 ******************************************************************************/
155 FILE_HANDLE *Get_Png_File_Handle()
157 FILE_HANDLE *handle;
159 handle = (FILE_HANDLE *)POV_MALLOC(sizeof(FILE_HANDLE), "PNG file handle");
161 handle->Open_File_p = Open_Png_File;
162 handle->Write_Line_p = Write_Png_Line;
163 handle->Read_Line_p = Read_Png_Line;
164 handle->Read_Image_p = Read_Png_Image;
165 handle->Close_File_p = Close_Png_File;
167 handle->buffer_size = 0;
169 handle->file = NULL;
170 handle->buffer = NULL;
172 return (handle);
176 /*****************************************************************************
178 * FUNCTION : png_pov_warn
180 * ARGUMENTS : png_struct *png_ptr; char *msg;
182 * MODIFIED ARGS :
184 * RETURN VALUE :
186 * AUTHOR : Andreas Dilger
188 * DESCRIPTION
190 * Prints an warning message using the POV I/O functions. This uses the
191 * png io_ptr to determine whether error messages should be printed or
192 * not.
194 * CHANGES
196 ******************************************************************************/
197 static void png_pov_warn(png_structp png_ptr, png_const_charp msg)
199 if (png_get_error_ptr(png_ptr))
200 Warning(0.0,"libpng: %s\n",msg);
204 /*****************************************************************************
206 * FUNCTION : png_pov_err
208 * ARGUMENTS : png_struct *png_ptr; char *msg;
210 * MODIFIED ARGS :
212 * RETURN VALUE :
214 * AUTHOR : Andreas Dilger
216 * DESCRIPTION
218 * If the png io_ptr is TRUE, this prints an error message using the POV
219 * I/O function. It will return to the location of the last setjmp call
220 * for this stream in any case.
222 * CHANGES
224 ******************************************************************************/
225 static void png_pov_err(png_structp png_ptr, png_const_charp msg)
227 if (png_get_error_ptr(png_ptr))
228 Error_Line("libpng: %s\n",msg);
230 png_longjmp(png_ptr,1);
234 /*****************************************************************************
236 * FUNCTION : Open_Png_File
238 * ARGUMENTS : FILE_HANDLE *handle; char *name; int *width; int *height;
239 * int buffer_size; int mode;
241 * MODIFIED ARGS : handle, width, height
243 * RETURN VALUE : 1 or 0 for success or failure
245 * AUTHOR : Andreas Dilger
247 * DESCRIPTION
249 * Open a PNG file and allocate the needed PNG structure buffers
251 * CHANGES
253 * Updated for POV-Ray 3.X - [TIW]
254 * Updated to handle resuming interrupted traces, Sept 1995 - [AED]
255 * Updated to output grayscale heightfield if requested - [AED]
256 * Updated to allow grayscale and alpha together, Oct 1995 - [AED]
257 * Updated to write gamma differently based on file type, Nov 1995 - [AED]
258 * Changed temp file name from TEMP_FILE_BASE to scene name, Feb 1996 - [AED]
259 * Changed temp file from scene name to path + scene name, Jun 1996 - [AED]
261 ******************************************************************************/
263 static int Open_Png_File(FILE_HANDLE *handle, char *name, int *width, int *height, int buffer_size, int mode)
265 int color_type;
266 handle->mode = mode;
267 handle->filename = name;
269 switch (mode)
271 case READ_MODE:
273 /* We can't resume from stdout. */
274 if (opts.Options & TO_STDOUT)
276 Status_Info("\n");
277 return(0);
280 /* Initialize PNG output routines using temporary file name. We
281 * need to use the path, or the rename will fail if the temp file
282 * is not on the same drive as the output file.
284 sprintf(tmp_fname, "%s%s.tpn", opts.Output_Path, opts.Scene_Name);
286 /* Move the old output file to a temporary file, so it can be
287 * read in and simultaneously written out to the new output file.
288 * Note that this can potentially be destructive, but it is
289 * impossible to change the output stream in mid-write. We have
290 * to check if a temp file already exists, in case the transfer
291 * has been previously aborted.
293 if ((tmp_fp = fopen(tmp_fname,READ_BINFILE_STRING)) == NULL)
295 /* The temp file doesn't exist. Try the original file. */
296 if ((tmp_fp = fopen(name,READ_BINFILE_STRING)) == NULL)
298 Status_Info("\n");
299 return(0); /* Neither file exists - start from scratch. */
301 else /* The original file exists, but the temp file doesn't. */
303 fclose(tmp_fp);
305 if (RENAME_FILE(name,tmp_fname) == RENAME_FILE_ERR)
307 Error("\nError making temporary PNG file for continuing trace.\n");
310 /* Open the original file (now with a new name) for reading */
311 if ((tmp_fp = fopen(tmp_fname,READ_BINFILE_STRING)) == NULL)
313 RENAME_FILE(tmp_fname,name); /* Try to rename back - not crucial */
314 Error("\nError opening temporary PNG file for continuing trace.\n");
318 /* The temp file already exists. If we can open the original file
319 * as well, then there is something wrong, and we can't automatically
320 * defide which file to delete.
322 else if((handle->file = fopen(name,READ_BINFILE_STRING)) != NULL)
324 fclose(tmp_fp);
325 fclose(handle->file);
327 Error_Line("\nBoth original and temporary PNG files exist after an interrupted trace.\n");
328 Error("Please delete either %s or %s (preferrably the smaller).\n",name,tmp_fname);
331 /* Try to open the new output file for writing. If we can't, try
332 * to move the old one back so that users don't fret if it's missing.
333 * PNG will be able to continue without loss of data either way.
335 if ((handle->file = fopen(name, WRITE_BINFILE_STRING)) == NULL)
337 Status_Info("\n");
339 fclose(tmp_fp);
340 RENAME_FILE(tmp_fname,name);
341 return(-1);
344 if (buffer_size > 0)
346 handle->buffer = (char *)POV_MALLOC(buffer_size, "PNG file buffer");
347 setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
350 handle->buffer_size = buffer_size;
352 /* The original input file */
353 if ((o_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
354 (png_voidp)FALSE, png_pov_err, png_pov_warn)) == NULL ||
355 (info_ptr = png_create_info_struct(o_png_ptr)) == NULL)
357 Error("Error allocating PNG data structures");
360 if (setjmp(*png_set_longjmp_fn(o_png_ptr, longjmp, sizeof (jmp_buf))))
362 /* If we get here, we had a problem reading the file */
363 Status_Info("\n");
365 if (handle->buffer != NULL)
367 POV_FREE(handle->buffer);
368 handle->buffer = NULL;
371 png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
373 fclose(handle->file);
374 handle->file = NULL;
375 fclose(tmp_fp);
376 tmp_fp = NULL;
378 return(0);
381 /* Set up the compression structure */
382 png_init_io(o_png_ptr, tmp_fp);
384 /* Read in header info from the file */
385 png_read_info(o_png_ptr, info_ptr);
387 if (png_get_color_type(o_png_ptr, info_ptr) & ~(PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA))
389 return(0);
392 Status_Info("\nResuming interrupted trace from %s",handle->filename);
394 /* The new output file. Thank god for re-entrant libpng/libz code! */
395 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
396 (png_voidp)TRUE, png_pov_err, png_pov_warn)) == NULL)
398 Error("Error allocating PNG data structures");
401 if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
403 /* If we get here, we had a problem writing the file */
404 Status_Info("\n");
406 if (handle->buffer != NULL)
408 POV_FREE(handle->buffer);
409 handle->buffer = NULL;
412 png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
413 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
415 fclose(handle->file);
416 handle->file = NULL;
417 fclose(tmp_fp);
418 tmp_fp = NULL;
420 if (DELETE_FILE(name) != DELETE_FILE_ERR)
422 RENAME_FILE(tmp_fname,name); /* Try to get the original file back */
425 return(-1);
428 /* Set up the compression structure */
429 png_init_io(png_ptr, handle->file);
431 /* Fill in the relevant image information from the resumed file */
432 *width = handle->width = png_get_image_width(png_ptr, info_ptr);
433 *height = handle->height = png_get_image_height(png_ptr, info_ptr);
435 /* Find out if file is a valid format, and if it had Alpha in it */
436 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
438 opts.Options |= OUTPUT_ALPHA;
441 if ((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
443 opts.Options |= HF_GRAY_16;
444 opts.PaletteOption = GREY; /* Force grayscale preview */
447 #if defined(PNG_READ_sBIT_SUPPORTED)
448 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
450 png_color_8 *color;
451 png_get_sBIT(png_ptr, info_ptr, &color);
452 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR)
454 opts.OutputQuality = color->red;
456 else
458 opts.OutputQuality = color->gray;
462 #else /* !PNG_READ_sBIT_SUPPORTED */
463 if (png_get_bit_depth(png_ptr, info_ptr) == 8 && opts.OutputQuality > 8 ||
464 png_get_bit_depth(png_ptr, info_ptr) == 16 && opts.OutputQuality <= 8)
466 Error("\nSpecified color depth +fn%d not the same as depth %d in %s\n",
467 opts.OutputQuality, png_get_bit_depth(png_ptr, info_ptr), name);
469 #endif /* !PNG_READ_sBIT_SUPPORTED */
471 #if defined(PNG_READ_oFFs_SUPPORTED)
472 opts.First_Column = png_get_x_offset_pixels(png_ptr, info_ptr);
473 opts.First_Line = png_get_y_offset_pixels(png_ptr, info_ptr);
474 #endif /* PNG_READ_oFFs_SUPPORTED */
476 png_write_info(png_ptr, info_ptr);
478 png_stride = png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR ? 3 : 1;
480 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
481 png_stride++;
483 png_stride *= (opts.OutputQuality + 7) / 8;
485 row_ptr = (png_byte *)POV_MALLOC(*width*png_stride,"PNG read row buffer");
486 break;
488 case WRITE_MODE:
490 if (opts.Options & TO_STDOUT)
492 buffer_size = 0;
493 handle->file = stdout;
495 else if ((handle->file = fopen(name, WRITE_BINFILE_STRING)) == NULL)
497 return(0);
500 if (buffer_size != 0)
502 handle->buffer = (char *)POV_MALLOC((size_t)buffer_size, "PNG file buffer");
503 setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
506 handle->buffer_size = buffer_size;
508 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
509 (png_voidp)TRUE, png_pov_err, png_pov_warn)) == NULL ||
510 (info_ptr = png_create_info_struct(png_ptr)) == NULL)
512 Error("Error allocating PNG data structures");
515 if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
517 /* If we get here, we had a problem writing the file */
518 if (handle->buffer != NULL)
520 POV_FREE(handle->buffer);
521 handle->buffer = NULL;
524 png_destroy_write_struct(&png_ptr, &info_ptr);
526 fclose(handle->file);
527 handle->file = NULL;
529 return(0);
532 /* Set up the compression structure */
534 png_init_io(png_ptr, handle->file);
536 handle->width = *width;
537 handle->height = *height;
539 color_type =
540 ((opts.Options & HF_GRAY_16)
541 ? PNG_COLOR_TYPE_GRAY
542 : PNG_COLOR_TYPE_RGB) |
543 ((opts.Options & OUTPUT_ALPHA)
544 ? PNG_COLOR_MASK_ALPHA
545 : 0);
547 /* Fill in the relevant image information */
548 png_set_IHDR(png_ptr, info_ptr, handle->width, handle->height,
549 8 * ((opts.OutputQuality + 7) / 8), /* Bit depth */
550 color_type,
551 PNG_INTERLACE_NONE,
552 PNG_COMPRESSION_TYPE_DEFAULT,
553 PNG_FILTER_TYPE_DEFAULT);
556 #if defined(PNG_WRITE_sBIT_SUPPORTED)
557 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR)
559 const png_color_8 color = {
560 .red = opts.OutputQuality,
561 .green = opts.OutputQuality,
562 .blue = opts.OutputQuality,
563 .alpha = (color_type & PNG_COLOR_MASK_ALPHA) ? opts.OutputQuality : 0,
565 png_set_sBIT(png_ptr, info_ptr, &color);
567 else
569 const png_color_8 color = {
570 .gray = opts.OutputQuality,
571 .alpha = (color_type & PNG_COLOR_MASK_ALPHA) ? opts.OutputQuality : 0,
573 png_set_sBIT(png_ptr, info_ptr, &color);
575 #endif /* PNG_WRITE_sBIT_SUPPORTED */
577 #if defined(PNG_WRITE_gAMA_SUPPORTED)
578 if (handle->file_type & (IMAGE_FTYPE | GRAY_FTYPE))
580 png_set_gAMA(png_ptr, info_ptr, 1.0/opts.DisplayGamma);
582 else if (handle->file_type & (HIST_FTYPE | HF_FTYPE))
584 png_set_gAMA(png_ptr, info_ptr, 1.0);
586 #endif /* PNG_WRITE_gAMA_SUPPORTED */
588 #if defined(PNG_WRITE_oFFs_SUPPORTED)
589 if (opts.First_Column != 0 || opts.First_Line != 0)
591 png_set_oFFs(png_ptr, info_ptr,
592 opts.First_Column, opts.First_Line,
593 PNG_OFFSET_PIXEL);
595 #endif /* PNG_WRITE_oFFs_SUPPORTED */
597 if (handle->file_type & HIST_FTYPE)
599 /* If we are writing a histogram file, we could potentially output
600 * a pCAL chunk with the max histogram value, to allow recovery of
601 * the original timing data. However, pCAL is not yet official at
602 * the time of this writing.
606 png_write_info(png_ptr, info_ptr);
608 png_stride = color_type & PNG_COLOR_MASK_COLOR ? 3 : 1;
610 if (color_type & PNG_COLOR_MASK_ALPHA)
611 png_stride++;
613 png_stride *= (opts.OutputQuality + 7) / 8;
615 row_ptr = (png_byte *)POV_MALLOC(*width*png_stride, "PNG write row buffer");
617 #if defined(PNG_WRITE_FLUSH_SUPPORTED)
618 /* Set libpng to flush the output buffers every few lines, so that
619 * in case of a rude crash we don't lose very much data.
621 png_set_flush(png_ptr, FLUSH_DIST);
622 #endif /* PNG_WRITE_FLUSH_SUPPORTED */
624 break;
626 case APPEND_MODE:
628 #if defined(PNG_WRITE_FLUSH_SUPPORTED)
629 if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
631 /* If we get here, we had a problem writing the file */
633 if (handle->buffer != NULL)
635 POV_FREE(handle->buffer);
636 handle->buffer = NULL;
639 png_destroy_write_struct(&png_ptr, &info_ptr);
641 fclose(handle->file);
642 handle->file = NULL;
644 return(0);
647 /* Write out the data in the PNG/zlib buffers, and set automatic
648 * flushing for every few scanlines, in case of a rude crash.
650 png_write_flush(png_ptr);
651 png_set_flush(png_ptr, FLUSH_DIST);
652 #else /* !PNG_WRITE_FLUSH_SUPPORTED */
653 fflush(handle->file);
654 #endif /* PNG_WRITE_FLUSH_SUPPORTED */
656 if (!(opts.Options & TO_STDOUT) && (handle->file =
657 freopen(name, APPEND_BINFILE_STRING, handle->file)) == NULL)
659 if (handle->buffer != NULL)
661 POV_FREE(handle->buffer);
662 handle->buffer = NULL;
665 png_destroy_write_struct(&png_ptr, &info_ptr);
667 return(0);
670 /* Delete the temporary data file. Note that the new output file
671 * is all ready to go - nothing needs to be done here.
673 if (tmp_fp != NULL)
675 fclose(tmp_fp);
676 tmp_fp = NULL;
678 if (DELETE_FILE(tmp_fname) == DELETE_FILE_ERR)
680 Warning(0.0,"Can't delete temporary PNG file %s. Please delete it.\n",tmp_fname);
685 return(1);
690 /*****************************************************************************
692 * FUNCTION : Write_Png_Line
694 * ARGUMENTS : handle, line_data, line_number
696 * MODIFIED ARGS : none
698 * RETURN VALUE : none
700 * AUTHOR : Andreas Dilger
702 * DESCRIPTION
704 * Write a line of data to the PNG file
706 * CHANGES
708 * Updated for POV-Ray 3.X - [TIW]
709 * Updated to do flush output to reduce data loss - [AED]
710 * Updated to output Alpha channel if requested - [AED]
711 * Updated to output grayscale heightfield if requested - [AED]
712 * Updated to allow grayscale in 5-8 bpp if desired, Oct 1995 - [AED]
713 * Updated to allow grayscale and alpha together, Oct 1995 - [AED]
714 * Changed how bit-depths 9-15 get promoted to 16 bits based on new
715 * recommendations from the PNG Group, Nov 1995 - [AED]
717 ******************************************************************************/
719 static void Write_Png_Line(FILE_HANDLE *handle, COLOUR *line_data, int line_number)
721 register int col, j;
722 int himask;
723 int color;
727 * We must copy all the values because PNG expects RGBRGB bytes, but
728 * POV-Ray stores RGB components in separate arrays as floats. In
729 * order to use the full scale values at the lower bit depth, PNG
730 * recommends filling the low-order bits with a copy of the high-order
731 * bits. However, studies have shown that filling the low order bits
732 * with constant bits significantly improves compression, which I'm
733 * doing here. Note that since the true bit depth is stored in the
734 * sBIT chunk, the extra packed bits are not important.
737 switch (opts.OutputQuality)
739 case 5:
740 case 6:
741 case 7:
742 /* Handle shifting for arbitrary output bit depth */
744 himask = 0xFF ^ ((1 << (8 - opts.OutputQuality)) - 1);
746 if ((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
748 for (col = j = 0; col < handle->width; col++, j += png_stride)
750 color = (png_byte)floor((line_data[col][RED]*0.30 +
751 line_data[col][GREEN]*0.59 +
752 line_data[col][BLUE]*0.11) * 255.0);
754 /* Use left-bit replication (LBR) for bit depths < 8 */
755 row_ptr[j] = color & himask;
756 row_ptr[j] |= color >> opts.OutputQuality;
758 /* Handle Alpha here if needed - must use exact bit replication
759 * instead of truncation or 100... termination
761 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
763 color = 255 - (int)floor(line_data[col][TRANSM] * 255.0);
765 row_ptr[j + 1] = color & himask;
766 row_ptr[j + 1] |= color >> opts.OutputQuality;
770 else
772 for (col = j = 0; col < handle->width; col++, j += png_stride)
774 color = (int)floor(line_data[col][RED] * 255.0);
776 row_ptr[j] = color & himask;
777 row_ptr[j] |= color >> opts.OutputQuality;
779 color = (int)floor(line_data[col][GREEN] * 255.0);
781 row_ptr[j + 1] = color & himask;
782 row_ptr[j + 1] |= color >> opts.OutputQuality;
784 color = (int)floor(line_data[col][BLUE] * 255.0);
786 row_ptr[j + 2] = color & himask;
787 row_ptr[j + 2] |= color >> opts.OutputQuality;
789 /* Handle Alpha here if needed */
790 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
792 color = 255 - (int)floor(line_data[col][TRANSM] * 255.0);
794 row_ptr[j + 3] = color & himask;
795 row_ptr[j + 3] |= color >> opts.OutputQuality;
799 break;
801 case 8:
802 if ((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
804 for (col = j = 0; col < handle->width; col++, j += png_stride)
806 row_ptr[j] = (png_byte)floor((line_data[col][RED]*0.30 +
807 line_data[col][GREEN]*0.59 +
808 line_data[col][BLUE]*0.11) * 255.0);
810 /* Handle Alpha here if needed */
811 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
813 row_ptr[j+1] = (png_byte)(255-floor(line_data[col][TRANSM]*255.0));
817 else
819 for (col = j = 0; col < handle->width; col++, j += png_stride)
821 row_ptr[j] = (png_byte)floor(line_data[col][RED] * 255.0);
822 row_ptr[j + 1] = (png_byte)floor(line_data[col][GREEN] * 255.0);
823 row_ptr[j + 2] = (png_byte)floor(line_data[col][BLUE] * 255.0);
825 /* Handle Alpha here if needed */
826 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
828 row_ptr[j+3] = (png_byte)(255-floor(line_data[col][TRANSM]*255.0));
832 break;
834 case 16:
835 if ((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
837 for (col = j = 0; col < handle->width; col++, j += png_stride)
839 color = (int)floor((line_data[col][RED]*0.30 +
840 line_data[col][GREEN]*0.59 +
841 line_data[col][BLUE]*0.11) * 65535.0);
843 row_ptr[j] = color >> 8;
844 row_ptr[j + 1] = color & 0xFF;
846 /* Handle Alpha here if needed */
847 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
849 color = 65535 - (int)floor(line_data[col][TRANSM] * 65535.0);
851 row_ptr[j + 2] = color >> 8;
852 row_ptr[j + 3] = color & 0xFF;
856 else
858 for (col = j = 0; col < handle->width; col++, j += png_stride)
860 color = (int)floor(line_data[col][RED] * 65535.0);
862 row_ptr[j] = color >> 8;
863 row_ptr[j + 1] = color & 0xFF;
865 color = (int)floor(line_data[col][GREEN] * 65535.0);
867 row_ptr[j + 2] = color >> 8;
868 row_ptr[j + 3] = color & 0xFF;
870 color = (int)floor(line_data[col][BLUE] * 65535.0);
872 row_ptr[j + 4] = color >> 8;
873 row_ptr[j + 5] = color & 0xFF;
875 /* Handle Alpha here if needed */
876 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
878 color = 65535 - (int)floor(line_data[col][TRANSM] * 65535.0);
880 row_ptr[j + 6] = color >> 8;
881 row_ptr[j + 7] = color & 0xFF;
885 break;
887 default: /* OutputQuality 9 - 15 */
888 /* Handle shifting for arbitrary output bit depth */
889 himask = 0xFF ^ ((1 << (16 - opts.OutputQuality)) - 1);
891 if ((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
893 for (col = j = 0; col < handle->width; col++, j += png_stride)
895 color = (int)floor((line_data[col][RED]*0.30 +
896 line_data[col][GREEN]*0.59 +
897 line_data[col][BLUE]*0.11) * 65535.0);
899 row_ptr[j] = color >> 8;
900 row_ptr[j + 1] = color & himask;
902 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
904 color = 65535 - (int)floor(line_data[col][TRANSM] * 65535.0);
906 row_ptr[j + 2] = color >> 8;
907 row_ptr[j + 3] = color & himask;
908 row_ptr[j + 3] |= color >> opts.OutputQuality;
912 else
914 for (col = j = 0; col < handle->width; col++, j += png_stride)
916 color = (int)floor(line_data[col][RED] * 65535.0);
918 row_ptr[j] = color >> 8;
919 row_ptr[j + 1] = color & himask;
921 color = (int)floor(line_data[col][GREEN] * 65535.0);
923 row_ptr[j + 2] = color >> 8;
924 row_ptr[j + 3] = color & himask;
926 color = (int)floor(line_data[col][BLUE] * 65535.0);
928 row_ptr[j + 4] = color >> 8;
929 row_ptr[j + 5] = color & himask;
931 /* Handle Alpha here if needed */
932 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
934 color = 65535 - (int)floor(line_data[col][TRANSM] * 65535.0);
936 row_ptr[j + 6] = color >> 8;
937 row_ptr[j + 7] = color & himask;
938 row_ptr[j + 7] |= color >> opts.OutputQuality;
944 if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
946 /* If we get here, we had a problem writing the file */
947 fclose(handle->file);
948 handle->file = NULL;
950 Error("Error writing PNG output data to %s.\n", handle->file);
953 /* Write out a scanline */
954 png_write_row(png_ptr, row_ptr);
956 /* Close and reopen file (if not stdout) for integrity in case we crash */
957 if (handle->buffer_size == 0 && !(opts.Options & TO_STDOUT))
959 #ifndef PNG_WRITE_FLUSH
960 fflush(handle->file);
961 #endif
963 handle->file = freopen(handle->filename, APPEND_BINFILE_STRING, handle->file);
969 /*****************************************************************************
971 * FUNCTION : Read_Png_Line
973 * ARGUMENTS : FILE_HANDLE *handle; COLOUR *line_data; int *line_number;
975 * MODIFIED ARGS : none
977 * RETURN VALUE : 1 if no error exit
979 * AUTHOR : Andreas Dilger
981 * DESCRIPTION
983 * Read a line of PNG data
985 * CHANGES
987 * Updated for POV-Ray 3.X - [TIW]
988 * Updated to handle interrupted file resuming Sept 1995 - [AED]
989 * Updated to support grayscale and alpha together, Oct 1995 - [AED]
991 ******************************************************************************/
993 static int Read_Png_Line(FILE_HANDLE *handle, COLOUR *line_data, int *line_number)
995 register int col, j, step;
997 if (setjmp(*png_set_longjmp_fn(o_png_ptr, longjmp, sizeof (jmp_buf))))
999 /* If we get here, we had a problem reading the file, which probably
1000 * means that we have read all the available data, rather than a real
1001 * error, but there is no sure way to know.
1003 Status_Info("\n");
1004 return(0);
1007 if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
1009 /* If we get here, we had a problem writing the new file */
1010 Status_Info("\n");
1012 fclose(handle->file);
1013 handle->file = NULL;
1014 fclose(tmp_fp);
1015 tmp_fp = NULL;
1017 if (DELETE_FILE(handle->filename) != DELETE_FILE_ERR)
1019 RENAME_FILE(tmp_fname,handle->filename); /* Move original file back */
1022 return(-1);
1025 /* Read in another row if available */
1026 png_read_row(o_png_ptr, row_ptr, NULL);
1028 /* We won't get here if there was a read error */
1029 png_write_row(png_ptr, row_ptr);
1031 if (*line_number % 32 == 31)
1032 Status_Info(".");
1035 * We must copy all the values because PNG supplies RGBRGB, but POV-Ray
1036 * stores RGB components in separate arrays. Note that since we have
1037 * already written the data out to the temporary file, we only need to
1038 * use the top 8 bits for the line_data info as it is only used for
1039 * potential screen output.
1042 /* How many bytes in a sample */
1043 step = (png_get_bit_depth(png_ptr, info_ptr) <= 8) ? 1 : 2;
1045 if ((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
1047 for (col = j = 0; col < handle->width; col++, j += png_stride)
1049 line_data[col][RED] = (DBL)row_ptr[j] / 255.0;
1050 line_data[col][GREEN] = (DBL)row_ptr[j] / 255.0;
1051 line_data[col][BLUE] = (DBL)row_ptr[j] / 255.0;
1053 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
1055 line_data[col][TRANSM] = (DBL)(255 - row_ptr[j + step]) / 255.0;
1059 else
1061 for (col = j = 0; col < handle->width; col++, j += png_stride)
1063 line_data[col][RED] = (DBL)row_ptr[j] / 255.0;
1064 line_data[col][GREEN] = (DBL)row_ptr[j + step] / 255.0;
1065 line_data[col][BLUE] = (DBL)row_ptr[j + 2*step] / 255.0;
1067 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA)
1069 line_data[col][TRANSM] = (DBL)(255 - row_ptr[j + 3*step]) / 255.0;
1074 /* Note that line_number is the line number of the completed row, not
1075 * the next row!
1077 #if defined(PNG_READ_oFFS_SUPPORTED)
1078 *line_number = png_get_y_offset_pixels(png_ptr, info_ptr) + png_get_current_row_number(png_ptr) - 1;
1079 #else
1080 *line_number = png_get_current_row_number(png_ptr) - 1;
1081 #endif
1083 return(1);
1088 /*****************************************************************************
1090 * FUNCTION : Close_Png_File
1092 * ARGUMENTS : FILE_HANDLE *handle
1094 * MODIFIED ARGS : handle
1096 * RETURN VALUE : none
1098 * AUTHOR : Andreas Dilger
1100 * DESCRIPTION
1102 * Write any chunks coming after image (eg comments), and free all the
1103 * memory associated with the PNG IO streams. Will output some rendering
1104 * stats and info into tEXt chunks if POV_COMMENTS is #defined, and will
1105 * also record the rendering time if CTIME is #defined.
1107 * CHANGES
1109 * Updated for POV-Ray 3.X - [TIW]
1111 ******************************************************************************/
1113 static void Close_Png_File(FILE_HANDLE *handle)
1115 #ifdef POV_COMMENTS
1116 int n, index = - 1;
1117 png_text *text_ptr = NULL;
1118 char allocated[NTEXT]; /* Boolean array if text is MALLOCed */
1119 char bigtext[MAXTEXT]; /* Large temporary string to print into */
1120 # ifdef CAMERA
1121 CAMERA *Camera = Frame.Camera;
1122 # endif
1123 #ifdef CTIME
1124 char months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1125 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1126 #endif
1127 #endif
1129 /* Why are we here? */
1131 if (handle->file == NULL)
1133 return;
1136 if (handle->mode == WRITE_MODE || handle->mode == APPEND_MODE)
1138 if (png_ptr != NULL)
1140 if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
1142 /* If we get here, we had a problem writing the file */
1144 png_destroy_write_struct(&png_ptr, &info_ptr);
1146 if (row_ptr != NULL)
1148 POV_FREE(row_ptr);
1149 row_ptr = NULL;
1152 if (handle->file != NULL)
1154 fclose (handle->file);
1155 handle->file = NULL;
1158 if (handle->buffer != NULL)
1160 POV_FREE(handle->buffer);
1161 handle->buffer = NULL;
1164 Error("Error writing PNG file.");
1167 #if 0
1168 if(png_ptr->row_number < png_ptr->num_rows)
1170 /* finished prematurely - trick into thinking done*/
1171 png_ptr->num_rows = png_ptr->row_number;
1172 png_write_finish_row(png_ptr);
1174 #endif
1176 #ifdef POV_COMMENTS /* temporarily skip comment writing code */
1177 if (info_ptr != NULL)
1179 #if defined(PNG_WRITE_tIME_SUPPORTED)
1180 png_convert_from_time_t(&info_ptr->mod_time, tstart);
1181 info_ptr->valid = PNG_INFO_tIME;
1182 #endif /* PNG_WRITE_tIME_SUPPORTED */
1184 #if defined(PNG_WRITE_tEXt_SUPPORTED)
1185 text_ptr = (png_text *)POV_MALLOC(NTEXT*sizeof(png_text), "PNG comment structure");
1187 /* Init allocation flags. */
1188 for (n = 0; n < NTEXT; n++)
1190 allocated[n] = FALSE;
1191 text_ptr[n].compression = - 1;
1194 #ifdef TRACER
1195 text_ptr[++index].key = "Author";
1196 text_ptr[index].text = TRACER;
1197 text_ptr[index].text_length = strlen(text_ptr[index].text);
1198 #endif
1200 #ifdef COPYRIGHT
1201 text_ptr[++index].key = "Copyright";
1202 /* 0xA9 is the ISO-8859-1 (used in PNG tEXt) copyright character */
1203 sprintf(bigtext, "Copyright %c %d %s", 0xA9, info_ptr->mod_time.year,
1204 COPYRIGHT);
1205 text_ptr[index].text_length = strlen(bigtext);
1206 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1207 strcpy(text_ptr[index].text, bigtext);
1208 allocated[index] = TRUE;
1209 if (text_ptr[index].text_length > 200) /* Compress if long copyright */
1210 text_ptr[index].compression = 0;
1211 #endif
1213 #ifdef CTIME
1214 /* Print the image "creation" time in RFC 1123 format */
1215 text_ptr[++index].key = "Creation Time";
1216 sprintf(bigtext, "%02d %3s %4d %02d:%02d:%02d GMT",
1217 info_ptr->mod_time.day, months[info_ptr->mod_time.month],
1218 info_ptr->mod_time.year, info_ptr->mod_time.hour,
1219 info_ptr->mod_time.minute, info_ptr->mod_time.second);
1220 text_ptr[index].text_length = strlen(bigtext);
1221 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1222 strcpy(text_ptr[index].text, bigtext);
1223 allocated[index] = TRUE;
1224 #endif
1226 text_ptr[++index].key = "Source";
1227 sprintf(bigtext, "Persistence of Vision(tm) Ray Tracer v%s%s",
1228 POV_RAY_VERSION, COMPILER_VER);
1229 text_ptr[index].text_length = strlen(bigtext);
1230 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1231 strcpy(text_ptr[index].text, bigtext);
1232 allocated[index] = TRUE;
1234 if (!(opts.Options & FROM_STDIN))
1236 text_ptr[++index].key = "Input File";
1237 text_ptr[index].text = opts.Input_File_Name;
1238 text_ptr[index].text_length = strlen(text_ptr[index].text);
1241 #ifdef CAMERA
1242 text_ptr[++index].key = "POV Camera";
1243 sprintf(bigtext, "Location: %7g %7g %7g\n"
1244 " Direction: %7g %7g %7g\n"
1245 " Up: %7g %7g %7g\n"
1246 " Right: %7g %7g %7g\n"
1247 " Sky: %7g %7g %7g",
1248 Camera->Location[X], Camera->Location[Y], Camera->Location[Z],
1249 Camera->Direction[X], Camera->Direction[Y], Camera->Direction[Z],
1250 Camera->Up[X], Camera->Up[Y], Camera->Up[Z],
1251 Camera->Right[X], Camera->Right[Y], Camera->Right[Z],
1252 Camera->Sky[X], Camera->Sky[Y], Camera->Sky[Z]);
1253 text_ptr[index].text_length = strlen(bigtext);
1254 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1255 strcpy(text_ptr[index].text, bigtext);
1256 allocated[index] = TRUE;
1257 #endif
1259 if (opts.FrameSeq.Clock_Value != 0)
1261 text_ptr[++index].key = "POV Clock";
1262 sprintf(bigtext, "%g", opts.FrameSeq.Clock_Value);
1263 text_ptr[index].text_length = strlen(bigtext);
1264 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1265 strcpy(text_ptr[index].text, bigtext);
1266 allocated[index] = TRUE;
1269 if (opts.Quality != 9)
1271 text_ptr[++index].key = "Rendering Quality";
1272 sprintf(bigtext, "%d", opts.Quality);
1273 text_ptr[index].text_length = strlen(bigtext);
1274 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1275 strcpy(text_ptr[index].text, bigtext);
1276 allocated[index] = TRUE;
1279 text_ptr[++index].key = "Rendering Time";
1280 sprintf(bigtext, "%g s", trender);
1281 text_ptr[index].text_length = strlen(bigtext);
1282 text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
1283 strcpy(text_ptr[index].text, bigtext);
1284 allocated[index] = TRUE;
1286 info_ptr->num_text = index + 1;
1287 info_ptr->max_text = NTEXT;
1288 info_ptr->text = text_ptr;
1289 #endif /* PNG_WRITE_tEXt_SUPPORTED */
1291 #endif /* POV_COMMENTS */
1293 png_write_end(png_ptr, info_ptr);
1294 png_destroy_write_struct(&png_ptr, &info_ptr);
1296 #ifdef POV_COMMENTS
1297 if (text_ptr != NULL)
1299 for (n = 0; n <= index; n++)
1301 if (allocated[n])
1303 POV_FREE(text_ptr[n].text);
1307 POV_FREE(text_ptr);
1308 text_ptr = NULL;
1310 #endif /* POV_COMMENTS */
1314 if (row_ptr != NULL)
1316 POV_FREE(row_ptr);
1317 row_ptr = NULL;
1320 if (handle->file != NULL && !(opts.Options & TO_STDOUT))
1322 fclose (handle->file);
1323 handle->file = NULL;
1326 if (handle->buffer != NULL)
1328 POV_FREE(handle->buffer);
1329 handle->buffer = NULL;
1332 else /* READ_MODE */
1334 if (o_png_ptr != NULL)
1336 png_destroy_read_struct(&o_png_ptr, (png_infopp)NULL, (png_infopp)NULL);
1342 /*****************************************************************************
1344 * FUNCTION : Read_Png_Image
1346 * ARGUMENTS : IMAGE *Image; char *name;
1348 * MODIFIED ARGS : Image
1350 * RETURN VALUE : none
1352 * AUTHOR : Andreas Dilger
1354 * DESCRIPTION
1356 * Reads a PNG image into an RGB image buffer
1358 * CHANGES
1360 * Updated for POV-Ray 3.X - [TIW]
1361 * Updated to allow grayscale and alpha together, Oct 1995 - [AED]
1362 * Fixed palette size for grayscale images with bit-depth <= 8, Nov 1995 [AED]
1363 * Changed how grayscale images > 8bpp are stored based on use, Nov 1995 [AED]
1365 ******************************************************************************/
1367 void Read_Png_Image(IMAGE *Image, char *name)
1369 unsigned int width, height;
1370 int row, col, j;
1371 int stride;
1372 FILE *filep;
1373 IMAGE_LINE *line_data;
1374 png_struct *r_png_ptr;
1375 png_info *r_info_ptr;
1376 png_byte **row_ptrs;
1378 /* Start by trying to open the file */
1380 if ((filep = Locate_File(name, READ_BINFILE_STRING, ".png", ".PNG",NULL,TRUE)) == NULL)
1382 Error("Error opening PNG file.\n");
1383 return; /* -hdf99- */
1386 if ((r_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1387 (png_voidp)TRUE, png_pov_err, png_pov_warn)) == NULL ||
1388 (r_info_ptr = png_create_info_struct(r_png_ptr)) == NULL)
1390 Error("Error allocating PNG data structures");
1391 return; /* -hdf99- */
1394 if (setjmp(*png_set_longjmp_fn(r_png_ptr, longjmp, sizeof (jmp_buf))))
1396 /* If we get here, we had a problem reading the file */
1398 png_destroy_read_struct(&r_png_ptr, &r_info_ptr, (png_infopp)NULL);
1399 Error("Error reading PNG image.");
1400 return; /* -hdf99- */
1403 /* set up the input control */
1405 png_init_io(r_png_ptr, filep);
1407 /* read the file information */
1409 png_read_info(r_png_ptr, r_info_ptr);
1411 width = png_get_image_width(r_png_ptr, r_info_ptr);
1412 height = png_get_image_height(r_png_ptr, r_info_ptr);
1414 Image->iwidth = width;
1415 Image->iheight = height;
1416 Image->width = (DBL)width;
1417 Image->height = (DBL)height;
1419 /* Allocate buffers for the image */
1420 stride = 1;
1422 if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_PALETTE)
1424 IMAGE_COLOUR *cmap;
1425 png_color *png_cmap;
1426 int cmap_len;
1427 int index;
1429 png_get_PLTE(r_png_ptr, r_info_ptr, &png_cmap, &cmap_len);
1431 Image->Colour_Map_Size = cmap_len;
1433 cmap = (IMAGE_COLOUR *)POV_MALLOC(cmap_len*sizeof(IMAGE_COLOUR), "PNG image color map");
1435 Image->Colour_Map = cmap;
1437 for (index = 0; index < cmap_len; index++)
1439 cmap[index].Red = png_cmap[index].red;
1440 cmap[index].Green = png_cmap[index].green;
1441 cmap[index].Blue = png_cmap[index].blue;
1442 cmap[index].Filter = 0;
1443 cmap[index].Transmit = 0;
1446 if (png_get_valid(r_png_ptr, r_info_ptr, PNG_INFO_tRNS))
1448 png_byte *trans;
1449 int num_trans;
1450 png_get_tRNS(r_png_ptr, r_info_ptr, &trans, &num_trans, NULL);
1451 for (index = 0; index < num_trans; index++)
1452 cmap[index].Transmit = 255 - trans[index];
1455 Image->data.map_lines = (unsigned char **)
1456 POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
1458 /* tell pnglib to expand data to 1 pixel/byte */
1459 png_set_packing(r_png_ptr);
1461 else if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_GRAY &&
1462 png_get_bit_depth(r_png_ptr, r_info_ptr) <= 8)
1464 IMAGE_COLOUR *cmap;
1465 int cmap_len;
1466 int index;
1468 Image->Colour_Map_Size = cmap_len = 1 << png_get_bit_depth(r_png_ptr, r_info_ptr);
1470 cmap = (IMAGE_COLOUR *)POV_MALLOC(cmap_len*sizeof(IMAGE_COLOUR), "PNG image color map");
1472 Image->Colour_Map = cmap;
1474 for (index = 0; index < cmap_len; index++)
1476 cmap[index].Red =
1477 cmap[index].Green =
1478 cmap[index].Blue = index;
1479 cmap[index].Filter = 0;
1480 cmap[index].Transmit = 0;
1483 if (png_get_valid(r_png_ptr, r_info_ptr, PNG_INFO_tRNS))
1485 png_byte *trans;
1486 int num_trans;
1487 png_get_tRNS(r_png_ptr, r_info_ptr, &trans, &num_trans, NULL);
1488 for (index = 0; index < num_trans; index++)
1489 cmap[index].Transmit = 255 - trans[index];
1492 Image->data.map_lines = (unsigned char **)
1493 POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
1495 /* tell pnglib to expand data to 1 pixel/byte */
1496 png_set_packing(r_png_ptr);
1498 else if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_GRAY ||
1499 png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA)
1501 Image->Colour_Map = NULL;
1503 Image->data.rgb_lines = (IMAGE_LINE *)
1504 POV_MALLOC(height * sizeof(IMAGE_LINE), "PNG image");
1506 if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_GRAY)
1508 stride = 2;
1510 else if (png_get_bit_depth(r_png_ptr, r_info_ptr) <= 8) /* PNG_COLOR_TYPE_GRAY_ALPHA */
1512 /* tell pnglib to expand data to 1 pixel/byte */
1513 png_set_packing(r_png_ptr);
1514 stride = 2;
1516 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
1518 stride = 4;
1521 else if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_RGB ||
1522 png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA)
1524 Image->Colour_Map = NULL;
1526 /* tell pnglib to strip 16 bit depth files down to 8 bits */
1527 if (png_get_bit_depth(r_png_ptr, r_info_ptr) > 8)
1529 if (opts.Options & VERBOSE)
1530 Warning(0.0,"\nConverting PNG image map to 8 bits/sample from higher bit depth.");
1531 png_set_strip_16(r_png_ptr);
1534 Image->data.rgb_lines = (IMAGE_LINE *)
1535 POV_MALLOC(height * sizeof(IMAGE_LINE), "PNG image");
1537 if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_RGB)
1538 stride = 3;
1539 else /* PNG_COLOR_TYPE_RGB_ALPHA */
1540 stride = 4;
1542 else /* Unknown PNG type */
1544 Error("Unsupported color type %d in PNG image.\n", png_get_color_type(r_png_ptr, r_info_ptr));
1547 /* Tell pnglib to handle the gamma conversion for you. Note that
1548 * GammaFactor * DisplayFactor = assumed_gamma, so we are converting
1549 * images into the "internal gamma" space of POV (rather than to a
1550 * gamma of 1.0) to avoid doing gamma correction on image maps twice for
1551 * those scene files which don't have a gamma of 1.0. For POV 3.0,
1552 * we will only do input gamma conversion on those files which will be
1553 * used as image maps, and the other types will load the raw pixel values.
1555 #if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_READ_gAMA_SUPPORTED)
1556 if (png_get_valid(r_png_ptr, r_info_ptr, PNG_INFO_gAMA) && (Image->Image_Type & IMAGE_FTYPE))
1558 double gamma;
1559 png_get_gAMA(r_png_ptr, r_info_ptr, &gamma);
1560 png_set_gamma(r_png_ptr, opts.GammaFactor*opts.DisplayGamma,gamma);
1562 #endif /* PNG_READ_GAMMA_SUPPORTED and PNG_READ_gAMA_SUPPORTED */
1564 png_set_interlace_handling(r_png_ptr);
1565 png_read_update_info(r_png_ptr, r_info_ptr);
1567 /* Allocate row buffers for the input */
1568 row_ptrs = (png_byte **)POV_MALLOC(height*sizeof(png_byte *), "PNG image");
1570 for (row = 0; row < height; row++)
1572 row_ptrs[row] = (png_byte *)POV_MALLOC(png_get_rowbytes(r_png_ptr, r_info_ptr), "PNG image line");
1575 /* Read in the entire image */
1576 png_read_image(r_png_ptr, row_ptrs);
1578 /* We must copy all the values because PNG supplies RGBRGB, but POV-Ray
1579 * stores RGB components in separate arrays
1581 for (row = 0; row < height; row++)
1583 if (Image->Colour_Map == NULL)
1585 line_data = &Image->data.rgb_lines[row];
1587 line_data->red = (unsigned char *)POV_MALLOC(width, "PNG image line");
1588 line_data->green = (unsigned char *)POV_MALLOC(width, "PNG image line");
1589 line_data->blue = (unsigned char *)POV_MALLOC(width, "PNG image line");
1591 if (png_get_color_type(r_png_ptr, r_info_ptr) & PNG_COLOR_MASK_ALPHA)
1593 line_data->transm = (unsigned char *)POV_MALLOC(width,"PNG image line");
1595 else
1597 line_data->transm = NULL;
1600 /* 8-bit grayscale image with a full alpha channel. Since the paletted
1601 * images don't support different transparencies for each pixel, we
1602 * have to make this a full-color image.
1604 if (png_get_color_type(r_png_ptr, r_info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA &&
1605 png_get_bit_depth(r_png_ptr, r_info_ptr) <= 8)
1607 for (col = j = 0; col < width; col ++, j += stride)
1609 line_data->red[col] =
1610 line_data->green[col] =
1611 line_data->blue[col] = row_ptrs[row][j];
1612 line_data->transm[col] = 255 - row_ptrs[row][j + 1];
1615 /* For 16 bit PNG heightfields, we need to know if the map will be used
1616 * for an image (in which case we want to store the values as grays, or
1617 * if we want to use it as a heightfield (in which case we need to store
1618 * it in the MSB-read, LSB-green format that POV uses to store 16-bit
1619 * heightfields.
1621 else if ((png_get_color_type(r_png_ptr, r_info_ptr) & PNG_COLOR_MASK_COLOR) ==
1622 PNG_COLOR_TYPE_GRAY)
1624 if (Image->Image_Type & HF_FTYPE)
1626 for (col = j = 0; col < width; col ++, j += stride)
1628 int red = row_ptrs[row][j];
1629 int green = row_ptrs[row][j + 1];
1631 line_data->red[col] = red;
1632 line_data->green[col] = green;
1633 line_data->blue[col] = 0;
1635 if (png_get_color_type(r_png_ptr, r_info_ptr) & PNG_COLOR_MASK_ALPHA)
1637 line_data->transm[col] = 255 - row_ptrs[row][j + 2];
1641 else
1643 for (col = j = 0; col < width; col ++, j += stride)
1645 int red = row_ptrs[row][j];
1647 line_data->red[col] = red;
1648 line_data->green[col] = red;
1649 line_data->blue[col] = red;
1651 if (png_get_color_type(r_png_ptr, r_info_ptr) & PNG_COLOR_MASK_ALPHA)
1653 line_data->transm[col] = 255 - row_ptrs[row][j + 2];
1658 else /* png_get_color_type(r_png_ptr, r_info_ptr) & PNG_COLOR_MASK_COLOR */
1660 for (col = j = 0; col < width; col ++, j += stride)
1662 line_data->red[col] = row_ptrs[row][j];
1663 line_data->green[col] = row_ptrs[row][j + 1];
1664 line_data->blue[col] = row_ptrs[row][j + 2];
1666 if (png_get_color_type(r_png_ptr, r_info_ptr) & PNG_COLOR_MASK_ALPHA)
1668 line_data->transm[col] = 255 - row_ptrs[row][j + 3];
1672 POV_FREE(row_ptrs[row]);
1674 else
1676 Image->data.map_lines[row] = row_ptrs[row];
1680 /* Clean up the rest of the PNG memory and such */
1682 POV_FREE(row_ptrs);
1684 /* read the rest of the file, getting any additional chunks in png_info */
1686 png_read_end(r_png_ptr, r_info_ptr);
1688 /* clean up after the read, and free any memory allocated */
1690 png_destroy_read_struct(&r_png_ptr, &r_info_ptr, (png_infopp)NULL);
1692 fclose(filep);