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