/****************************************************************************
* ppm.c
*
* This module contains the code to read and write the PPM file format.
*
* from Persistence of Vision(tm) Ray Tracer
* Copyright 1996 Persistence of Vision Team
*---------------------------------------------------------------------------
* NOTICE: This source code file is provided so that users may experiment
* with enhancements to POV-Ray and to port the software to platforms other
* than those supported by the POV-Ray Team. There are strict rules under
* which you are permitted to use this file. The rules are in the file
* named POVLEGAL.DOC which should be distributed with this file. If
* POVLEGAL.DOC is not available or for more info please contact the POV-Ray
* Team Coordinator by leaving a message in CompuServe's Graphics Developer's
* Forum. The latest version of POV-Ray may be found there as well.
*
* This program is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
* Original patch copyright 1994 Tim Rowley
* Updated for POV 3.0 by Chris Cason, Jan '95.
*
*****************************************************************************/
/****************************************************************************
* The format is as follows:
*
* (header:)
* P3 - ASCII data OR
* P6 - raw binary data
* # hello - optional comment(s)
* wwww hhhh - Width, Height (ASCII text)
* # world - optional comment(s)
* nnn - maximum color (nnn = bright, 0 = black)
*
* (each pixel: one of the following)
* rr gg bb - Red, green, blue of intensity 0-nnn (binary byte)
* RRR GGG BBB - Red, green, blue of intensity 0-nnn (ASCII number)
*
*****************************************************************************/
#include "frame.h"
#include "povproto.h"
#include "povray.h"
#include "optout.h"
#include "pgm.h"
#include "ppm.h"
/*****************************************************************************
* Local preprocessor defines
******************************************************************************/
/*****************************************************************************
* Local typedefs
******************************************************************************/
/*****************************************************************************
* Local variables
******************************************************************************/
static int PPM_Line_Number;
/*****************************************************************************
* Static functions
******************************************************************************/
static int Open_PPM_File PARAMS ((FILE_HANDLE *handle, char *name, int *width,
int *height, int buffer_size, int mode));
static void Write_PPM_Line PARAMS ((FILE_HANDLE *handle, COLOUR *line_data,
int line_number));
static int Read_PPM_Line PARAMS((FILE_HANDLE *handle, COLOUR *line_data,
int *line_number));
static void Close_PPM_File PARAMS((FILE_HANDLE *handle));
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
FILE_HANDLE *Get_PPM_File_Handle()
{
FILE_HANDLE *handle;
handle = (FILE_HANDLE *) POV_MALLOC(sizeof(FILE_HANDLE), "PPM file handle") ;
handle->Open_File_p = Open_PPM_File;
handle->Write_Line_p = Write_PPM_Line;
handle->Read_Line_p = Read_PPM_Line;
handle->Read_Image_p = Read_PPM_Image;
handle->Close_File_p = Close_PPM_File;
handle->file = NULL;
handle->buffer = NULL;
handle->buffer_size = 0;
return (handle);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
static int Open_PPM_File(handle, name, width, height, buffer_size, mode)
FILE_HANDLE *handle;
char *name;
int *width;
int *height;
int buffer_size;
int mode;
{
char type;
int input;
char junk[512];
handle->mode = mode;
handle->filename = name;
PPM_Line_Number = 0;
switch (mode)
{
case READ_MODE:
/* We can't resume from stdout. */
if (opts.Options & TO_STDOUT ||
(handle->file = fopen(name, READ_FILE_STRING)) == NULL)
{
Status_Info("\n");
return(0);
}
if (buffer_size != 0)
{
handle->buffer = POV_MALLOC((size_t)buffer_size, "PPM file buffer") ;
setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
}
if (fscanf(handle->file, "P%c\n", &type) != 1 || type != '6')
{
return(0);
}
/* Ignore any comments (if they are written) */
while ((input = fgetc(handle->file)) == '#')
{
fgets(junk, 512, handle->file);
}
ungetc(input, handle->file);
if (fscanf(handle->file, "%d %d\n255\n", width, height) != 2)
{
return(0);
}
Status_Info("\nResuming interrupted trace from %s",handle->filename);
handle->width = *width;
handle->height = *height;
handle->buffer_size = buffer_size;
break;
case WRITE_MODE:
if (opts.Options & TO_STDOUT)
{
buffer_size = 0;
handle->file = stdout;
}
else
{
if ((handle->file = fopen(name, WRITE_FILE_STRING)) == NULL)
{
return(0);
}
}
if (buffer_size != 0)
{
handle->buffer = POV_MALLOC((size_t)buffer_size, "PPM file buffer") ;
setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
}
fprintf(handle->file, "P6\n");
#ifdef POV_COMMENTS
#ifdef TRACER
fprintf(handle->file, "# Author: %s\n", TRACER);
#endif
fprintf(handle->file, "# Source: Persistence of Vision(tm) Ray Tracer v%s%s\n",
POV_RAY_VERSION, COMPILER_VER);
if (!(opts.Options & TO_STDOUT))
{
fprintf(handle->file, "# Input File: %s\n", opts.Input_File_Name);
}
if (opts.FrameSeq.Clock_Value != 0)
{
fprintf(handle->file, "# POV Clock: %g\n", opts.FrameSeq.Clock_Value);
}
if (opts.Quality != 9)
{
fprintf(handle->file, "# Rendering Quality: %d\n", opts.Quality);
}
#endif /* POV_COMMENTS */
fprintf(handle->file, "%d %d\n255\n", *width, *height);
handle->width = *width;
handle->height = *height;
handle->buffer_size = buffer_size;
break;
case APPEND_MODE:
if (opts.Options & TO_STDOUT)
{
buffer_size = 0;
handle->file = stdout;
}
else
{
if ((handle->file = fopen(name, APPEND_FILE_STRING)) == NULL)
{
return(0);
}
}
if (buffer_size != 0)
{
handle->buffer = POV_MALLOC((size_t)buffer_size, "PPM file buffer") ;
setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
}
handle->buffer_size = buffer_size;
break;
}
return(1);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
static void Write_PPM_Line(handle, line_data, line_number)
FILE_HANDLE *handle;
COLOUR *line_data;
int line_number;
{
unsigned int gray;
register int x;
for (x = 0 ; x < handle->width ; x++)
{
if (opts.Options & HF_GRAY_16) /* 16 bit grayscale output */
{
gray = ((0.30 * line_data[x][RED]) +
(0.59 * line_data[x][GREEN]) +
(0.11 * line_data[x][BLUE])) * 65535;
if ((putc((gray >> 8) & 0xFF, handle->file) == EOF) ||
(putc(gray & 0xFF, handle->file) == EOF) ||
(putc(0, handle->file) == EOF))
{
Error("Error writing PPM output data to %s.\n",handle->filename);
}
}
else /* Normal 24 bit pixel coloring */
{
if ((putc((int)floor(line_data[x][RED] * 255.0), handle->file) == EOF) ||
(putc((int)floor(line_data[x][GREEN]*255.0), handle->file) == EOF) ||
(putc((int)floor(line_data[x][BLUE]*255.0), handle->file) == EOF))
{
Error("Error writing PPM output data to %s.\n",handle->filename);
}
}
}
PPM_Line_Number++;
if (handle->buffer_size == 0)
{
/* close and reopen file for integrity in case we crash */
fflush(handle->file);
if (!(opts.Options & TO_STDOUT))
{
handle->file = freopen(handle->filename,APPEND_FILE_STRING,handle->file);
}
}
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
static int Read_PPM_Line(handle, line_data, line_number)
FILE_HANDLE *handle;
COLOUR *line_data;
int *line_number;
{
int data, i;
if ((data = getc(handle->file)) == EOF)
{
return (0);
}
ungetc(data, handle->file);
*line_number = PPM_Line_Number++;
for (i = 0 ; i < handle->width ; i++)
{
if ((data = getc(handle->file)) == EOF)
{
return(-1);
}
line_data[i][RED] = (DBL) data / 255.0;
if ((data = getc(handle->file)) == EOF)
{
return(-1);
}
line_data[i][GREEN] = (DBL) data / 255.0;
if ((data = getc(handle->file)) == EOF)
{
return(-1);
}
line_data[i][BLUE] = (DBL) data / 255.0;
}
return (1);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
static void Close_PPM_File(handle)
FILE_HANDLE *handle;
{
if (handle->file)
{
fflush(handle->file);
/* Close and reopen file (if not stdout) for integrity in case we crash */
if (!(opts.Options & TO_STDOUT))
fclose(handle->file);
}
if (handle->buffer != NULL)
{
POV_FREE(handle->buffer);
}
handle->file = NULL;
handle->buffer = NULL;
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
void Read_PPM_Image(Image, name)
IMAGE *Image;
char *name;
{
char type;
int width, height;
int depth;
char input;
char junk[512];
int x, y;
int data;
IMAGE_LINE *line_data;
FILE *infile;
if ((infile = Locate_File(name, READ_FILE_STRING, ".ppm", ".PPM",TRUE)) == NULL)
{
Error("Error opening PPM image %s.\n", name);
}
if (fscanf(infile, "P%c\n", &type) != 1 || (type != '3' && type != '6'))
{
Error ("File is not in PPM format.\n", name);
}
/* Ignore any comments */
while ((input = fgetc(infile)) == '#')
{
fgets(junk, 512, infile);
}
ungetc(input, infile);
if (fscanf(infile, "%d %d\n", &width, &height) != 2)
{
Error ("Error reading width or height from PPM image.\n", name);
}
/* Ignore any comments */
while ((input = fgetc(infile)) == '#')
{
fgets(junk, 512, infile);
}
ungetc(input, infile);
if (fscanf(infile, "%d\n", &depth) != 1 || depth > 255 || depth < 1)
{
Error ("Unsupported number of colors (%d) in PPM image.\n", depth);
}
Image->width = (DBL)(Image->iwidth = width);
Image->height = (DBL)(Image->iheight = height);
Image->Colour_Map_Size = 0;
Image->Colour_Map = NULL;
Image->data.rgb_lines = (IMAGE_LINE *) POV_MALLOC(Image->iheight * sizeof (IMAGE_LINE), "PPM image");
for (y = 0; y < height; y++)
{
line_data = &Image->data.rgb_lines[y];
line_data->red = (unsigned char *)POV_MALLOC(width,"PPM image line");
line_data->green = (unsigned char *)POV_MALLOC(width,"PPM image line");
line_data->blue = (unsigned char *)POV_MALLOC(width,"PPM image line");
line_data->transm = (unsigned char *)NULL;
if (type == '3') /* ASCII data to be input */
{
for (x = 0; x < width; x++)
{
if (fscanf(infile,"%d",&data) != 1)
{
Error("Error reading data from PPM image.\n");
}
line_data->red[x] = data*255/depth;
if (fscanf(infile,"%d",&data) != 1)
{
Error("Error reading data from PPM image.\n");
}
line_data->green[x] = data*255/depth;
if (fscanf(infile,"%d",&data) != 1)
{
Error("Error reading data from PPM image.\n");
}
line_data->blue[x] = data*255/depth;
}
}
else /* (type == '6') Raw binary data to be input */
{
for (x = 0; x < width; x++)
{
if ((data = getc(infile)) == EOF)
{
Error("Error reading data from PPM image.\n");
}
line_data->red[x] = data*255/depth;
if ((data = getc(infile)) == EOF)
{
Error("Error reading data from PPM image.\n");
}
line_data->green[x] = data*255/depth;
if ((data = getc(infile)) == EOF)
{
Error("Error reading data from PPM image.\n");
}
line_data->blue[x] = data*255/depth;
}
}
}
fclose(infile);
}