/**************************************************************************** * 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); }