1 /****************************************************************************
2  *               ppm.cpp
3  *
4  * This module contains the code to read and write the PPM file format.
5  *
6  * from Persistence of Vision(tm) Ray Tracer version 3.6.
7  * Copyright 1991-2003 Persistence of Vision Team
8  * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
9  *---------------------------------------------------------------------------
10  * NOTICE: This source code file is provided so that users may experiment
11  * with enhancements to POV-Ray and to port the software to platforms other
12  * than those supported by the POV-Ray developers. There are strict rules
13  * regarding how you are permitted to use this file. These rules are contained
14  * in the distribution and derivative versions licenses which should have been
15  * provided with this file.
16  *
17  * These licences may be found online, linked from the end-user license
18  * agreement that is located at http://www.povray.org/povlegal.html
19  *---------------------------------------------------------------------------
20  * This program is based on the popular DKB raytracer version 2.12.
21  * DKBTrace was originally written by David K. Buck.
22  * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
23  *---------------------------------------------------------------------------
24  * $File: //depot/povray/3.6-release/source/ppm.cpp $
25  * $Revision: #3 $
26  * $Change: 3032 $
27  * $DateTime: 2004/08/02 18:43:41 $
28  * $Author: chrisc $
29  * $Log$
30  *****************************************************************************/
31 
32 /****************************************************************************
33 *
34 *  PPM format according to NetPBM specs (http://netpbm.sourceforge.net/doc/):
35 *
36 *  This module implements read support for PPM image maps and
37 *  write support for PPM output.
38 *
39 *  For reading both ASCII and binary files are supported ('P3' and 'P6').
40 *
41 *  For writing we use binary files. OutputQuality > 8 leads to 16 bit files.
42 *  Special handling of HF_GRAY_16 -> 16 bit PGM files ('P5')
43 *  All formats supported for writing can now also be used in continued trace.
44 *
45 *****************************************************************************/
46 
47 #include "frame.h"
48 #include "povray.h"
49 #include "optout.h"
50 #include "pgm.h"
51 #include "ppm.h"
52 #include "pov_util.h"
53 #include "povmsend.h"
54 #include "colour.h"
55 
56 #include <ctype.h>
57 
58 BEGIN_POV_NAMESPACE
59 
60 /*****************************************************************************
61 * Local preprocessor defines
62 ******************************************************************************/
63 
64 /*****************************************************************************
65 * Local typedefs
66 ******************************************************************************/
67 
68 /*****************************************************************************
69 * Local variables
70 ******************************************************************************/
71 
72 /*****************************************************************************
73 * Static functions
74 ******************************************************************************/
75 
76 /*****************************************************************************
77 *
78 * FUNCTION
79 *
80 * INPUT
81 *
82 * OUTPUT
83 *
84 * RETURNS
85 *
86 * AUTHOR
87 *
88 * DESCRIPTION
89 *
90 * CHANGES
91 *
92 ******************************************************************************/
93 
PPM_Image(char * name,int w,int h,int m,int l)94 PPM_Image::PPM_Image(char *name, int w, int h, int m, int l)
95 {
96   int  file_type;
97   unsigned char header[2];
98   char line[1024];
99   char *ptr;
100   int depth;
101 
102   mode = m;
103   filename = name;
104   in_file = NULL;
105   out_file = NULL;
106   width = w ;
107   height = h ;
108   line_number = l;
109 
110   // HF_GRAY_16 leads to 16 bit grayscale (PGM) output
111   if (opts.Options & HF_GRAY_16) file_type = POV_File_Image_PGM;
112   else file_type = POV_File_Image_PPM;
113 
114   switch (mode)
115   {
116     case READ_MODE:
117 
118       // We can't resume from stdout.
119       if (opts.Options & TO_STDOUT || (in_file = New_Checked_IStream(name, file_type)) == NULL)
120       {
121         return;
122       }
123 
124       // --- Read Header ---
125       if (!in_file->read((char *)header, 2)) return;
126 
127       if(header[0] != 'P') return;
128 
129       if((header[1] != '5') && (header[1] != '6')) return;
130 
131       do
132       {
133         in_file->getline (line, 1024);
134         line[1023] = '\0';
135         if ((ptr = strchr(line, '#')) != NULL) *ptr = '\0';  // remove comment
136       }
137       while (line[0]=='\0');  // read until line without comment from beginning
138 
139       // --- First: two numbers: with and height ---
140       if (sscanf(line,"%d %d",&width, &height) != 2) return;
141 
142       do
143       {
144         in_file->getline (line, 1024);
145         line[1023] = '\0';
146         if ((ptr = strchr(line, '#')) != NULL) *ptr = '\0';  // remove comment
147       }
148       while (line[0]=='\0');  // read until line without comment from beginning
149 
150       // --- Second: one number: color depth ---
151       if (sscanf(line,"%d",&depth) != 1) return;
152 
153       if ((depth > 65535) || (depth < 1)) return;
154 
155       if (w != width || h != height)
156         Error ("PPM file dimensions do not match render resolution.");
157 
158       Send_Progress("Resuming interrupted trace", PROGRESS_RESUMING_INTERRUPTED_TRACE);
159 
160       break;
161 
162     case WRITE_MODE:
163 
164       if (opts.Options & TO_STDOUT)
165       {
166         out_file = New_OStream("stdout", file_type, false);
167       }
168       else if ((out_file = New_Checked_OStream(name, file_type, false)) == NULL)
169       {
170         return;
171       }
172 
173       // HF_GRAY_16 leads to 16 bit grayscale (PGM) output
174       if (opts.Options & HF_GRAY_16)
175       {
176         out_file->printf("P5\n%d %d\n65535\n", w, h);
177       }
178       else
179       {
180         out_file->printf("P6\n%d %d\n%d\n", w, h, (1 << opts.OutputQuality) - 1);
181       }
182 
183       width = w;
184       height = h;
185 
186       break;
187 
188     case APPEND_MODE:
189 
190       if (opts.Options & TO_STDOUT)
191       {
192         out_file = New_OStream("stdout", file_type, true);
193       }
194       else if ((out_file = New_Checked_OStream(name, file_type, true)) == NULL)
195       {
196         return;
197       }
198 
199       break;
200   }
201 
202   valid = true;
203 }
204 
205 /*****************************************************************************
206 *
207 * FUNCTION
208 *
209 * INPUT
210 *
211 * OUTPUT
212 *
213 * RETURNS
214 *
215 * AUTHOR
216 *
217 * DESCRIPTION
218 *
219 * CHANGES
220 *
221 *    Identical to targa version
222 *
223 ******************************************************************************/
224 
~PPM_Image()225 PPM_Image::~PPM_Image()
226 {
227   // Close the input file (if open)
228   if(in_file != NULL)
229     delete in_file;
230 
231   // Close the output file (if open)
232   if(out_file != NULL)
233   {
234     out_file->flush();
235     delete out_file;
236   }
237 
238   in_file = NULL;
239   out_file = NULL;
240 }
241 
242 /*****************************************************************************
243 *
244 * FUNCTION
245 *
246 *  PPM_Image::Write_Line
247 *
248 * INPUT
249 *
250 * OUTPUT
251 *
252 * RETURNS
253 *
254 * AUTHOR
255 *
256 *    Christoph Hormann
257 *
258 * DESCRIPTION
259 *
260 *    Writes an image line to PPM image file
261 *
262 * CHANGES
263 *
264 *    August 2003 - New implementation based on targa/png code
265 *
266 ******************************************************************************/
267 
Write_Line(COLOUR * line_data)268 void PPM_Image::Write_Line(COLOUR *line_data)
269 {
270   if(valid == false)
271     Error("Cannot access output image file.");
272 
273   register int x;
274   unsigned int rval, gval, bval, gray;
275   unsigned int mask;
276   COLC fac;
277 
278   for (x = 0; x < width; x++)
279   {
280     // HF_GRAY_16 leads to 16 bit grayscale (PGM) output
281     if (opts.Options & HF_GRAY_16)
282     {
283       gray = (int)floor((GREY_SCALE3(line_data[x][pRED],line_data[x][pGREEN],line_data[x][pBLUE])) * 65535.0);
284 
285       out_file->Write_Byte((gray >> 8) & 0xFF);
286       if (!out_file->Write_Byte(gray & 0xFF))
287         Error("Cannot write PPM output data to %s.",filename);
288     }
289     else
290     // otherwise 3*OutputQuality bit pixel coloring written to 8 or 16 bit file
291     {
292       mask = (1 << opts.OutputQuality) - 1 ;
293       fac = (COLC) (mask) ;
294 
295       rval = (unsigned int)floor(line_data[x][pRED] * fac) & mask;
296       gval = (unsigned int)floor(line_data[x][pGREEN] * fac) & mask;
297       bval = (unsigned int)floor(line_data[x][pBLUE] * fac) & mask;
298 
299       if (opts.OutputQuality>8)  // 16 bit per value
300       {
301         out_file->Write_Byte(rval >> 8) ;
302         out_file->Write_Byte(rval & 0xFF) ;
303         out_file->Write_Byte(gval >> 8) ;
304         out_file->Write_Byte(gval & 0xFF) ;
305         out_file->Write_Byte(bval >> 8) ;
306         if (!out_file->Write_Byte(bval & 0xFF))
307         {
308           Error("Error writing PPM output data to %s.",filename);
309         }
310       }
311       else                       // 8 bit per value
312       {
313         out_file->Write_Byte(rval & 0xFF) ;
314         out_file->Write_Byte(gval & 0xFF) ;
315         if (!out_file->Write_Byte(bval & 0xFF))
316         {
317           Error("Cannot write PPM output data to %s.",filename);
318         }
319       }
320     }
321   }
322 
323   line_number++;
324 
325   out_file->flush();
326 }
327 
328 /*****************************************************************************
329 *
330 * FUNCTION
331 *
332 *  PPM_Image::Read_Line
333 *
334 * INPUT
335 *
336 * OUTPUT
337 *
338 * RETURNS
339 *
340 * AUTHOR
341 *
342 *    Christoph Hormann
343 *
344 * DESCRIPTION
345 *
346 *    Reads a PPM image line for continued trace
347 *
348 * CHANGES
349 *
350 *    August 2003 - New implementation based on targa/png code
351 *
352 ******************************************************************************/
353 
Read_Line(COLOUR * line_data)354 int PPM_Image::Read_Line(COLOUR *line_data)
355 {
356   if(valid == false)
357     Error("Cannot access output image file.");
358 
359   int data, x;
360   int data_hi, data_lo;
361 
362 
363   if (in_file->eof()) return 0;
364 
365   if (opts.Options & HF_GRAY_16)
366   {
367     for (x = 0; x < width; x++)
368     {
369       if ((data_hi = in_file->Read_Byte ()) == EOF)
370       {
371         if (x == 0)
372         {
373           return 0;
374         }
375         else
376         {
377           return -1;
378         }
379       }
380       if ((data_lo = in_file->Read_Byte ()) == EOF) return -1;
381 
382       line_data[x][pRED]   = ((COLC)(256*data_hi + data_lo))/65535.0;
383       line_data[x][pGREEN] = line_data[x][pRED];
384       line_data[x][pBLUE]  = line_data[x][pRED];
385     }
386   }
387   /* depending on the output Quality settings we expect
388    * an 8 or 16 bit file.  So using continued trace requires
389    * correct OutputQuality settings
390    */
391   else if (opts.OutputQuality>8)
392   {
393     COLC fac = (COLC)((1 << opts.OutputQuality) - 1);
394 
395     for (x = 0; x < width; x++)
396     {
397       /* Read the first data byte.  If EOF is reached on the first
398        * character read, then this line hasn't been rendered yet.
399        * Return 0.  If an EOF occurs somewhere within the line, this
400        * is an error - return -1.
401        */
402       if ((data_hi = in_file->Read_Byte ()) == EOF)
403       {
404         if (x == 0)
405         {
406           return 0;
407         }
408         else
409         {
410           return -1;
411         }
412       }
413       if ((data_lo = in_file->Read_Byte ()) == EOF) return -1;
414 
415       line_data[x][pRED] = ((COLC)(256*data_hi + data_lo))/fac;
416 
417       if ((data_hi = in_file->Read_Byte ()) == EOF) return -1;
418       if ((data_lo = in_file->Read_Byte ()) == EOF) return -1;
419 
420       line_data[x][pGREEN] = ((COLC)(256*data_hi + data_lo))/fac;
421 
422       if ((data_hi = in_file->Read_Byte ()) == EOF) return -1;
423       if ((data_lo = in_file->Read_Byte ()) == EOF) return -1;
424 
425       line_data[x][pBLUE] = ((COLC)(256*data_hi + data_lo))/fac;
426     }
427   }
428   else
429   {
430     for (x = 0; x < width; x++)
431     {
432       /* Read the first data byte.  If EOF is reached on the first
433        * character read, then this line hasn't been rendered yet.
434        * Return 0.  If an EOF occurs somewhere within the line, this
435        * is an error - return -1.
436        */
437       if ((data = in_file->Read_Byte ()) == EOF)
438       {
439         if (x == 0)
440         {
441           return 0;
442         }
443         else
444         {
445           return -1;
446         }
447       }
448 
449       line_data[x][pRED]  = (DBL) data / 255.0;
450 
451       // Read the GREEN data byte.
452 
453       if ((data = in_file->Read_Byte ()) == EOF)
454       {
455         return -1;
456       }
457 
458       line_data[x][pGREEN] = (DBL) data / 255.0;
459 
460       // Read the BLUE data byte.
461 
462       if ((data = in_file->Read_Byte ()) == EOF)
463       {
464         return -1;
465       }
466 
467       line_data[x][pBLUE]  = (DBL) data / 255.0;
468     }
469   }
470 
471   line_number++;
472 
473   return 1;
474 }
475 
476 /*****************************************************************************
477 *
478 * FUNCTION
479 *
480 *  Read_PPM_Image
481 *
482 * INPUT
483 *
484 * OUTPUT
485 *
486 * RETURNS
487 *
488 * AUTHOR
489 *
490 *    Christoph Hormann
491 *
492 * DESCRIPTION
493 *
494 *    Reads an PPM image file
495 *
496 * CHANGES
497 *
498 *    August 2003 - New implementation based on targa/png reading code
499 *
500 ******************************************************************************/
501 
Read_PPM_Image(IMAGE * Image,char * name)502 void Read_PPM_Image(IMAGE *Image, char *name)
503 {
504   IStream *filep;
505   unsigned char header[2];
506   char line[1024];
507   char *ptr;
508   int nbr;
509 
510   int width, height;
511   unsigned int depth;
512 
513   IMAGE8_LINE *line_data;
514   IMAGE16_LINE *line_16_data;
515   int data_hi, data_lo;
516   int x, i;
517 
518   // --- Start by trying to open the file ---
519   if((filep = Locate_File(name,POV_File_Image_PPM,NULL,true)) == NULL)
520     Error ("Cannot open PPM image %s.", name);
521 
522   // --- Read Header ---
523   if (!filep->read((char *)header, 2))
524     Error ("Cannot read header of PPM image %s.", name);
525 
526   if(header[0] != 'P') Error ("File is not in PPM format.");
527 
528   if((header[1] != '3') && (header[1] != '6'))
529     Error ("File is not in PPM format (type %d).", header[1]);
530 
531   do
532   {
533     filep->getline (line, 1024);
534     line[1023] = '\0';
535     if ((ptr = strchr(line, '#')) != NULL) *ptr = '\0';  // remove comment
536   }
537   while (line[0]=='\0');  // read until line without comment from beginning
538 
539   // --- First: two numbers: with and height ---
540   if (sscanf(line,"%d %d",&width, &height) != 2)
541     Error ("Cannot read width and height from PPM image.");
542 
543   if (width <= 0 || height <= 0)
544     Error ("Invalid width or height read from PPM image.");
545 
546   do
547   {
548     filep->getline (line, 1024) ;
549     line[1023] = '\0';
550     if ((ptr = strchr(line, '#')) != NULL) *ptr = '\0';  // remove comment
551   }
552   while (line[0]=='\0');  // read until line without comment from beginning
553 
554   // --- Second: one number: color depth ---
555   if (sscanf(line,"%d",&depth) != 1)
556     Error ("Cannot read color depth from PPM image.");
557 
558   if ((depth > 65535) || (depth < 1))
559     Error ("Unsupported number of colors (%d) in PPM image.", depth);
560 
561   Image->iwidth = width;
562   Image->iheight = height;
563   Image->width = (DBL)width;
564   Image->height = (DBL)height;
565   Image->Colour_Map = NULL;
566   Image->Colour_Map_Size = 0;
567 
568   if (depth < 256)
569   {
570     Image->data.rgb8_lines = (IMAGE8_LINE *)POV_MALLOC(height * sizeof(IMAGE8_LINE), "PPM image");
571 
572     for (i = 0; i < height; i++)
573     {
574       line_data = &Image->data.rgb8_lines[i];
575 
576       line_data->red    = (unsigned char *)POV_MALLOC(width, "PPM image line");
577       line_data->green  = (unsigned char *)POV_MALLOC(width, "PPM image line");
578       line_data->blue   = (unsigned char *)POV_MALLOC(width, "PPM image line");
579       line_data->transm = (unsigned char *)NULL;
580 
581       if (header[1] == '3') // --- ASCII PPM file (type 3) ---
582       {
583         for (x = 0; x < width; x++)
584         {
585           nbr = Read_ASCII_File_Number(filep);
586           if (nbr >= 0) line_data->red[x] = (nbr*255)/depth;
587           else Error ("Cannot read image data from PPM image.");
588           nbr = Read_ASCII_File_Number(filep);
589           if (nbr >= 0) line_data->green[x] = (nbr*255)/depth;
590           else Error ("Cannot read image data from PPM image.");
591           nbr = Read_ASCII_File_Number(filep);
592           if (nbr >= 0) line_data->blue[x] = (nbr*255)/depth;
593           else Error ("Cannot read image data from PPM image.");
594         }
595       }
596       else                  // --- binary PPM file (type 6) ---
597       {
598         for (x = 0; x < width; x++)
599         {
600           if ((nbr = filep->Read_Byte ()) == EOF)
601             Error("Cannot read data from PPM image.");
602 
603           line_data->red[x] = (nbr*255)/depth;
604 
605           if ((nbr = filep->Read_Byte ()) == EOF)
606             Error("Cannot read data from PPM image.");
607 
608           line_data->green[x] = (nbr*255)/depth;
609 
610           if ((nbr = filep->Read_Byte ()) == EOF)
611             Error("Cannot read data from PPM image.");
612 
613           line_data->blue[x] = (nbr*255)/depth;
614         }
615       }
616     }
617   }
618   else // --- 16 bit PPM (binary or ASCII) ---
619   {
620     Image->Image_Type |= IS16BITIMAGE;
621 
622     Image->data.rgb16_lines = (IMAGE16_LINE *)POV_MALLOC(height * sizeof(IMAGE16_LINE), "PPM image");
623 
624     for (i = 0; i < height; i++)
625     {
626       line_16_data = &Image->data.rgb16_lines[i];
627 
628       line_16_data->red    = (unsigned short *)POV_MALLOC(width * sizeof(unsigned short), "PPM image line");
629       line_16_data->green  = (unsigned short *)POV_MALLOC(width * sizeof(unsigned short), "PPM image line");
630       line_16_data->blue   = (unsigned short *)POV_MALLOC(width * sizeof(unsigned short), "PPM image line");
631       line_16_data->transm = (unsigned short *)NULL;
632 
633       if (header[1] == '3') // --- ASCII PPM file (type 3) ---
634       {
635         for (x = 0; x < width; x++)
636         {
637           nbr = Read_ASCII_File_Number(filep);
638           if (nbr >= 0) line_16_data->red[x] = (nbr*65535)/depth;
639           else Error ("Cannot read image data from PPM image.");
640 
641           nbr = Read_ASCII_File_Number(filep);
642           if (nbr >= 0) line_16_data->green[x] = (nbr*65535)/depth;
643           else Error ("Cannot read image data from PPM image.");
644 
645           nbr = Read_ASCII_File_Number(filep);
646           if (nbr >= 0) line_16_data->blue[x] = (nbr*65535)/depth;
647           else Error ("Cannot read image data from PPM image.");
648         }
649       }
650       else                  // --- binary PPM file (type 6) ---
651       {
652         for (x = 0; x < width; x++)
653         {
654           if ((data_hi = filep->Read_Byte ()) == EOF)
655             Error ("Cannot read data from PPM image.");
656           if ((data_lo = filep->Read_Byte ()) == EOF)
657             Error ("Cannot read data from PPM image.");
658           line_16_data->red[x] = (256*data_hi + data_lo)*65535/depth;
659 
660           if ((data_hi = filep->Read_Byte ()) == EOF)
661             Error ("Cannot read data from PPM image.");
662           if ((data_lo = filep->Read_Byte ()) == EOF)
663             Error ("Cannot read data from PPM image.");
664           line_16_data->green[x] = (256*data_hi + data_lo)*65535/depth;
665 
666           if ((data_hi = filep->Read_Byte ()) == EOF)
667             Error ("Cannot read data from PPM image.");
668           if ((data_lo = filep->Read_Byte ()) == EOF)
669             Error ("Cannot read data from PPM image.");
670           line_16_data->blue[x] = (256*data_hi + data_lo)*65535/depth;
671         }
672       }
673     }
674   }
675 
676   // Close the image file
677 
678   delete filep;
679 }
680 
681 END_POV_NAMESPACE
682