1 /*****************************************************************************
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 *****************************************************************************/
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).
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 /*****************************************************************************
85 ******************************************************************************/
89 /*****************************************************************************
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 /*****************************************************************************
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
,
111 static int Read_Png_Line (FILE_HANDLE
*handle
, COLOUR
*line_data
,
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 */
126 void png_write_finish_row (png_structp
);
133 /*****************************************************************************
135 * FUNCTION : Get_Png_File_Handle
139 * MODIFIED ARGS : none
141 * RETURN VALUE : File Handle
143 * AUTHOR : Andreas Dilger
147 * Allocate memory for and setup file handle for PNG file.
151 * Updated for POV-Ray 3.X - [TIW]
153 ******************************************************************************/
155 FILE_HANDLE
*Get_Png_File_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;
170 handle
->buffer
= NULL
;
176 /*****************************************************************************
178 * FUNCTION : png_pov_warn
180 * ARGUMENTS : png_struct *png_ptr; char *msg;
186 * AUTHOR : Andreas Dilger
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
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;
214 * AUTHOR : Andreas Dilger
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.
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
249 * Open a PNG file and allocate the needed PNG structure buffers
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
)
267 handle
->filename
= name
;
273 /* We can't resume from stdout. */
274 if (opts
.Options
& TO_STDOUT
)
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
)
299 return(0); /* Neither file exists - start from scratch. */
301 else /* The original file exists, but the temp file doesn't. */
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
)
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
)
340 RENAME_FILE(tmp_fname
,name
);
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 */
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
);
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
))
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 */
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
);
420 if (DELETE_FILE(name
) != DELETE_FILE_ERR
)
422 RENAME_FILE(tmp_fname
,name
); /* Try to get the original file back */
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
))
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
;
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
)
483 png_stride
*= (opts
.OutputQuality
+ 7) / 8;
485 row_ptr
= (png_byte
*)POV_MALLOC(*width
*png_stride
,"PNG read row buffer");
490 if (opts
.Options
& TO_STDOUT
)
493 handle
->file
= stdout
;
495 else if ((handle
->file
= fopen(name
, WRITE_BINFILE_STRING
)) == NULL
)
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
);
532 /* Set up the compression structure */
534 png_init_io(png_ptr
, handle
->file
);
536 handle
->width
= *width
;
537 handle
->height
= *height
;
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
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 */
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
);
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
,
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
)
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 */
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
);
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
);
670 /* Delete the temporary data file. Note that the new output file
671 * is all ready to go - nothing needs to be done here.
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
);
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
704 * Write a line of data to the PNG file
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
)
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
)
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
;
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
;
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));
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));
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;
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;
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
;
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
);
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
);
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
983 * Read a line of PNG data
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.
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 */
1012 fclose(handle
->file
);
1013 handle
->file
= NULL
;
1017 if (DELETE_FILE(handle
->filename
) != DELETE_FILE_ERR
)
1019 RENAME_FILE(tmp_fname
,handle
->filename
); /* Move original file back */
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)
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;
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
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;
1080 *line_number
= png_get_current_row_number(png_ptr
) - 1;
1088 /*****************************************************************************
1090 * FUNCTION : Close_Png_File
1092 * ARGUMENTS : FILE_HANDLE *handle
1094 * MODIFIED ARGS : handle
1096 * RETURN VALUE : none
1098 * AUTHOR : Andreas Dilger
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.
1109 * Updated for POV-Ray 3.X - [TIW]
1111 ******************************************************************************/
1113 static void Close_Png_File(FILE_HANDLE
*handle
)
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 */
1121 CAMERA
*Camera
= Frame
.Camera
;
1124 char months
[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1125 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1129 /* Why are we here? */
1131 if (handle
->file
== NULL
)
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
)
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.");
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
);
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;
1195 text_ptr
[++index
].key
= "Author";
1196 text_ptr
[index
].text
= TRACER
;
1197 text_ptr
[index
].text_length
= strlen(text_ptr
[index
].text
);
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
,
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;
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
;
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
);
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
;
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
);
1297 if (text_ptr
!= NULL
)
1299 for (n
= 0; n
<= index
; n
++)
1303 POV_FREE(text_ptr
[n
].text
);
1310 #endif /* POV_COMMENTS */
1314 if (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
1356 * Reads a PNG image into an RGB image buffer
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
;
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 */
1422 if (png_get_color_type(r_png_ptr
, r_info_ptr
) == PNG_COLOR_TYPE_PALETTE
)
1425 png_color
*png_cmap
;
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
))
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)
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
++)
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
))
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
)
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
);
1516 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
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
)
1539 else /* PNG_COLOR_TYPE_RGB_ALPHA */
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
))
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");
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
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];
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
]);
1676 Image
->data
.map_lines
[row
] = row_ptrs
[row
];
1680 /* Clean up the rest of the PNG memory and such */
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
);