Panda3D
Loading...
Searching...
No Matches
pnmFileTypePNM.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file pnmFileTypePNM.cxx
10 * @author drose
11 * @date 1998-04-04
12 */
13
14#include "pnmFileTypePNM.h"
15
16#ifdef HAVE_PNM
17
19
20#include "pnmFileTypeRegistry.h"
21#include "bamReader.h"
22
23using std::istream;
24using std::ostream;
25using std::string;
26
27static const char * const extensions_PNM[] = {
28 "pbm", "pgm", "ppm", "pnm"
29};
30static const int num_extensions_PNM = sizeof(extensions_PNM) / sizeof(const char *);
31
32TypeHandle PNMFileTypePNM::_type_handle;
33
34// Some macros lifted from the original Netpbm sources.
35
36#define PBM_MAGIC1 'P'
37#define PBM_MAGIC2 '1'
38#define RPBM_MAGIC2 '4'
39#define PBM_FORMAT (PBM_MAGIC1 * 256 + PBM_MAGIC2)
40#define RPBM_FORMAT (PBM_MAGIC1 * 256 + RPBM_MAGIC2)
41#define PBM_TYPE PBM_FORMAT
42
43#define PBM_FORMAT_TYPE(f) \
44 ((f) == PBM_FORMAT || (f) == RPBM_FORMAT ? PBM_TYPE : -1)
45
46#define PGM_OVERALLMAXVAL 65535
47
48#define PGM_MAGIC1 'P'
49#define PGM_MAGIC2 '2'
50#define RPGM_MAGIC2 '5'
51#define PGM_FORMAT (PGM_MAGIC1 * 256 + PGM_MAGIC2)
52#define RPGM_FORMAT (PGM_MAGIC1 * 256 + RPGM_MAGIC2)
53#define PGM_TYPE PGM_FORMAT
54
55#define PGM_FORMAT_TYPE(f) ((f) == PGM_FORMAT || (f) == RPGM_FORMAT ? PGM_TYPE : PBM_FORMAT_TYPE(f))
56
57#define PPM_OVERALLMAXVAL PGM_OVERALLMAXVAL
58#define PPM_MAXMAXVAL PGM_MAXMAXVAL
59
60#define PPM_MAGIC1 'P'
61#define PPM_MAGIC2 '3'
62#define RPPM_MAGIC2 '6'
63#define PPM_FORMAT (PPM_MAGIC1 * 256 + PPM_MAGIC2)
64#define RPPM_FORMAT (PPM_MAGIC1 * 256 + RPPM_MAGIC2)
65#define PPM_TYPE PPM_FORMAT
66
67#define PPM_FORMAT_TYPE(f) \
68 ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f))
69
70#define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL
71#define PNM_GET1(x) PPM_GETB(x)
72
73#define PNM_FORMAT_TYPE(f) PPM_FORMAT_TYPE(f)
74
75typedef unsigned char bit;
76#define PBM_WHITE 0
77#define PBM_BLACK 1
78
79#define pbm_allocarray(cols, rows) \
80 ((bit**) pm_allocarray(cols, rows, sizeof(bit)))
81#define pbm_allocrow(cols) ((bit*) pm_allocrow(cols, sizeof(bit)))
82#define pbm_freearray(bits, rows) pm_freearray((char**) bits, rows)
83#define pbm_freerow(bitrow) pm_freerow((char*) bitrow)
84#define pbm_packed_bytes(cols) (((cols)+7)/8)
85#define pbm_allocrow_packed(cols) \
86 ((unsigned char *) pm_allocrow(pbm_packed_bytes(cols), \
87 sizeof(unsigned char)))
88#define pbm_freerow_packed(packed_bits) \
89 pm_freerow((char *) packed_bits)
90#define pbm_allocarray_packed(cols, rows) ((unsigned char **) \
91 pm_allocarray(pbm_packed_bytes(cols), rows, sizeof(unsigned char)))
92#define pbm_freearray_packed(packed_bits, rows) \
93 pm_freearray((char **) packed_bits, rows)
94
95#define pgm_allocarray( cols, rows ) ((gray**) pm_allocarray( cols, rows, sizeof(gray) ))
96#define pgm_allocrow( cols ) ((gray*) pm_allocrow( cols, sizeof(gray) ))
97#define pgm_freearray( grays, rows ) pm_freearray( (char**) grays, rows )
98#define pgm_freerow( grayrow ) pm_freerow( (char*) grayrow )
99
100#define ppm_allocarray( cols, rows ) ((pixel**) pm_allocarray( cols, rows, sizeof(pixel) ))
101#define ppm_allocrow( cols ) ((pixel*) pm_allocrow( cols, sizeof(pixel) ))
102#define ppm_freearray( pixels, rows ) pm_freearray( (char**) pixels, rows )
103#define ppm_freerow( pixelrow ) pm_freerow( (char*) pixelrow )
104
105static const bool pm_plain_output = false;
106
107// Some functions lifted from Netpbm and adapted to use C++ iostreams.
108
109
110char**
111pm_allocarray(int const cols, int const rows, int const size ) {
112 /*----------------------------------------------------------------------------
113 Allocate an array of 'rows' rows of 'cols' columns each, with each
114 element 'size' bytes.
115
116 We use a special format where we tack on an extra element to the row
117 index to indicate the format of the array.
118
119 We have two ways of allocating the space: fragmented and
120 unfragmented. In both, the row index (plus the extra element) is
121 in one block of memory. In the fragmented format, each row is
122 also in an independent memory block, and the extra row pointer is
123 NULL. In the unfragmented format, all the rows are in a single
124 block of memory called the row heap and the extra row pointer is
125 the address of that block.
126
127 We use unfragmented format if possible, but if the allocation of the
128 row heap fails, we fall back to fragmented.
129 -----------------------------------------------------------------------------*/
130 char** rowIndex;
131 char * rowheap;
132
133 rowIndex = (char **)PANDA_MALLOC_ARRAY((rows + 1) * sizeof(char *));
134 if ( rowIndex == nullptr )
135 pm_error("out of memory allocating row index (%u rows) for an array",
136 rows);
137 rowheap = (char *)PANDA_MALLOC_ARRAY( rows * cols * size );
138 if ( rowheap == nullptr ) {
139 /* We couldn't get the whole heap in one block, so try fragmented
140 format.
141 */
142 int row;
143
144 rowIndex[rows] = nullptr; /* Declare it fragmented format */
145
146 for (row = 0; row < rows; ++row) {
147 rowIndex[row] = pm_allocrow(cols, size);
148 if (rowIndex[row] == nullptr)
149 pm_error("out of memory allocating Row %u "
150 "(%u columns, %u bytes per tuple) "
151 "of an array", row, cols, size);
152 }
153 } else {
154 /* It's unfragmented format */
155 int row;
156 rowIndex[rows] = rowheap; /* Declare it unfragmented format */
157
158 for (row = 0; row < rows; ++row)
159 rowIndex[row] = &(rowheap[row * cols * size]);
160 }
161 return rowIndex;
162}
163
164void
165pm_freearray(char ** const rowIndex,
166 int const rows) {
167
168 void * const rowheap = rowIndex[rows];
169
170 if (rowheap != nullptr) {
171 PANDA_FREE_ARRAY(rowheap);
172 } else {
173 int row;
174 for (row = 0; row < rows; ++row) {
175 pm_freerow(rowIndex[row]);
176 }
177 }
178 PANDA_FREE_ARRAY(rowIndex);
179}
180
181static unsigned int
182pm_getuint(istream * const ifP) {
183 /*----------------------------------------------------------------------------
184 Read an unsigned integer in ASCII decimal from the file stream
185 represented by 'ifP' and return its value.
186
187 If there is nothing at the current position in the file stream that
188 can be interpreted as an unsigned integer, issue an error message
189 to stderr and abort the program.
190
191 If the number at the current position in the file stream is too
192 great to be represented by an 'int' (Yes, I said 'int', not
193 'unsigned int'), issue an error message to stderr and abort the
194 program.
195 -----------------------------------------------------------------------------*/
196 int ch;
197 unsigned int i;
198
199 // skip whitespace
200 do {
201 ch = ifP->get();
202
203 if (ch == '#') {
204 // Skip a comment
205 do {
206 ch = ifP->get();
207 } while (ch != EOF && ch != '\n');
208 }
209 } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
210
211 if (ch < '0' || ch > '9')
212 pm_error("junk in file where an unsigned integer should be");
213
214 i = 0;
215 do {
216 unsigned int const digitVal = ch - '0';
217
218 if (i > INT_MAX/10 - digitVal)
219 pm_error("ASCII decimal integer in file is "
220 "too large to be processed. ");
221 i = i * 10 + digitVal;
222 ch = ifP->get();
223 } while (ch >= '0' && ch <= '9');
224
225 return i;
226}
227
228static void
229ppm_readppminitrest(istream * const file,
230 int * const colsP,
231 int * const rowsP,
232 pixval * const maxvalP) {
233 unsigned int maxval;
234
235 /* Read size. */
236 *colsP = (int)pm_getuint(file);
237 *rowsP = (int)pm_getuint(file);
238
239 /* Read maxval. */
240 maxval = pm_getuint(file);
241 if (maxval > PPM_OVERALLMAXVAL)
242 pm_error("maxval of input image (%u) is too large. "
243 "The maximum allowed by the PPM is %u.",
244 maxval, PPM_OVERALLMAXVAL);
245 if (maxval == 0)
246 pm_error("maxval of input image is zero.");
247
248 *maxvalP = maxval;
249}
250
251static void
252pgm_readpgminitrest(istream * const file,
253 int * const colsP,
254 int * const rowsP,
255 gray * const maxvalP) {
256
257 gray maxval;
258
259 /* Read size. */
260 *colsP = (int)pm_getuint(file);
261 *rowsP = (int)pm_getuint(file);
262
263 /* Read maxval. */
264 maxval = pm_getuint(file);
265 if (maxval > PGM_OVERALLMAXVAL)
266 pm_error("maxval of input image (%u) is too large. "
267 "The maximum allowed by PGM is %u.",
268 maxval, PGM_OVERALLMAXVAL);
269 if (maxval == 0)
270 pm_error("maxval of input image is zero.");
271
272 *maxvalP = maxval;
273}
274
275static void
276pbm_readpbminitrest(istream* file,
277 int* colsP,
278 int* rowsP) {
279 /* Read size. */
280 *colsP = (int)pm_getuint( file );
281 *rowsP = (int)pm_getuint( file );
282
283 /* *colsP and *rowsP really should be unsigned int, but they come
284 from the time before unsigned ints (or at least from a person
285 trained in that tradition), so they are int. We could simply
286 consider negative numbers to mean values > INT_MAX/2 and much
287 code would just automatically work. But some code would fail
288 miserably. So we consider values that won't fit in an int to
289 be unprocessable.
290 */
291 if (*colsP < 0)
292 pm_error("Number of columns in header is too large.");
293 if (*rowsP < 0)
294 pm_error("Number of columns in header is too large.");
295}
296
297static unsigned char
298pm_getrawbyte(istream * const file) {
299 int iby;
300
301 iby = file->get();
302 if (iby == EOF)
303 pm_error("EOF / read error reading a one-byte sample");
304 return (unsigned char) iby;
305}
306
307static bit
308getbit (istream * const file) {
309 char ch;
310
311 do {
312 ch = file->get();
313 } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' );
314
315 if ( ch != '0' && ch != '1' )
316 pm_error( "junk in file where bits should be" );
317
318 return ( ch == '1' ) ? 1 : 0;
319}
320
321static void
322pbm_readpbmrow( istream *file, bit *bitrow, int cols, int format ) {
323 int col, bitshift;
324 bit* bP;
325
326 switch ( format )
327 {
328 case PBM_FORMAT:
329 for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
330 *bP = getbit( file );
331 break;
332
333 case RPBM_FORMAT: {
334 unsigned char item;
335 bitshift = -1; item = 0; /* item's value is meaningless here */
336 for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
337 {
338 if ( bitshift == -1 )
339 {
340 item = pm_getrawbyte( file );
341 bitshift = 7;
342 }
343 *bP = ( item >> bitshift ) & 1;
344 --bitshift;
345 }
346 }
347 break;
348
349 default:
350 pm_error( "can't happen" );
351 }
352}
353
354static gray
355pgm_getrawsample(istream * const file, gray const maxval) {
356
357 if (maxval < 256) {
358 /* The sample is just one byte. Read it. */
359 return(pm_getrawbyte(file));
360 } else {
361 /* The sample is two bytes. Read both. */
362 unsigned char byte_pair[2];
363 size_t pairs_read;
364
365 file->read((char *)byte_pair, 2);
366 pairs_read = file->gcount();
367 if (pairs_read == 0)
368 pm_error("EOF /read error while reading a long sample");
369 /* This could be a few instructions faster if exploited the internal
370 format (i.e. endianness) of a pixval. Then we might be able to
371 skip the shifting and oring.
372 */
373 return((byte_pair[0]<<8) | byte_pair[1]);
374 }
375}
376
377static void
378pgm_readpgmrow(istream* const file, gray* const grayrow,
379 int const cols, gray const maxval, int const format) {
380
381 switch (format) {
382 case PGM_FORMAT: {
383 int col;
384 for (col = 0; col < cols; ++col) {
385 grayrow[col] = pm_getuint(file);
386#ifdef DEBUG
387 if (grayrow[col] > maxval)
388 pm_error( "value out of bounds (%u > %u)",
389 grayrow[col], maxval );
390#endif /*DEBUG*/
391 }
392 }
393 break;
394
395 case RPGM_FORMAT: {
396 int col;
397 for (col = 0; col < cols; ++col) {
398 grayrow[col] = pgm_getrawsample( file, maxval );
399#ifdef DEBUG
400 if ( grayrow[col] > maxval )
401 pm_error( "value out of bounds (%u > %u)",
402 grayrow[col], maxval );
403#endif /*DEBUG*/
404 }
405 }
406 break;
407
408 case PBM_FORMAT:
409 case RPBM_FORMAT:
410 {
411 bit * bitrow;
412 int col;
413
414 bitrow = pbm_allocrow(cols);
415 pbm_readpbmrow( file, bitrow, cols, format );
416 for (col = 0; col < cols; ++col)
417 grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0;
418 pbm_freerow(bitrow);
419 }
420 break;
421
422 default:
423 pm_error( "can't happen" );
424 }
425}
426
427static void
428ppm_readppmrow(istream* const fileP,
429 pixel* const pixelrow,
430 int const cols,
431 pixval const maxval,
432 int const format) {
433
434 switch (format) {
435 case PPM_FORMAT: {
436 int col;
437 for (col = 0; col < cols; ++col) {
438 pixval const r = pm_getuint(fileP);
439 pixval const g = pm_getuint(fileP);
440 pixval const b = pm_getuint(fileP);
441 PPM_ASSIGN(pixelrow[col], r, g, b);
442 }
443 }
444 break;
445
446 case RPPM_FORMAT: {
447 int col;
448 for (col = 0; col < cols; ++col) {
449 pixval const r = pgm_getrawsample(fileP, maxval);
450 pixval const g = pgm_getrawsample(fileP, maxval);
451 pixval const b = pgm_getrawsample(fileP, maxval);
452 PPM_ASSIGN(pixelrow[col], r, g, b);
453 }
454 }
455 break;
456
457 case PGM_FORMAT:
458 case RPGM_FORMAT: {
459 gray * const grayrow = pgm_allocrow(cols);
460 int col;
461
462 pgm_readpgmrow(fileP, grayrow, cols, maxval, format);
463 for (col = 0; col < cols; ++col) {
464 pixval const g = grayrow[col];
465 PPM_ASSIGN(pixelrow[col], g, g, g);
466 }
467 pgm_freerow(grayrow);
468 }
469 break;
470
471 case PBM_FORMAT:
472 case RPBM_FORMAT: {
473 bit * const bitrow = pbm_allocrow(cols);
474 int col;
475
476 pbm_readpbmrow(fileP, bitrow, cols, format);
477 for (col = 0; col < cols; ++col) {
478 pixval const g = (bitrow[col] == PBM_WHITE) ? maxval : 0;
479 PPM_ASSIGN(pixelrow[col], g, g, g);
480 }
481 pbm_freerow(bitrow);
482 }
483 break;
484
485 default:
486 pm_error("Invalid format code");
487 }
488}
489
490static void
491pnm_readpnmrow( istream* file, xel* xelrow, int cols, xelval maxval, int format ) {
492 int col;
493 xel* xP;
494 gray* grayrow;
495 gray* gP;
496 bit* bitrow;
497 bit* bP;
498
499 switch ( PNM_FORMAT_TYPE(format) )
500 {
501 case PPM_TYPE:
502 ppm_readppmrow( file, (pixel*) xelrow, cols, (pixval) maxval, format );
503 break;
504
505 case PGM_TYPE:
506 grayrow = pgm_allocrow( cols );
507 pgm_readpgmrow( file, grayrow, cols, (gray) maxval, format );
508 for ( col = 0, xP = xelrow, gP = grayrow; col < cols; ++col, ++xP, ++gP )
509 PNM_ASSIGN1( *xP, *gP );
510 pgm_freerow( grayrow );
511 break;
512
513 case PBM_TYPE:
514 bitrow = pbm_allocrow( cols );
515 pbm_readpbmrow( file, bitrow, cols, format );
516 for ( col = 0, xP = xelrow, bP = bitrow; col < cols; ++col, ++xP, ++bP )
517 PNM_ASSIGN1( *xP, *bP == PBM_BLACK ? 0: maxval );
518 pbm_freerow( bitrow );
519 break;
520
521 default:
522 pm_error( "can't happen" );
523 }
524}
525
526static void
527pbm_writepbminit(ostream * const fileP,
528 int const cols,
529 int const rows,
530 int const forceplain) {
531
532 if (!forceplain && !pm_plain_output) {
533 (*fileP)
534 << (char)PBM_MAGIC1
535 << (char)RPBM_MAGIC2
536 << '\n'
537 << cols << ' ' << rows << '\n';
538 } else {
539 (*fileP)
540 << (char)PBM_MAGIC1
541 << (char)PBM_MAGIC2
542 << '\n'
543 << cols << ' ' << rows << '\n';
544 }
545}
546
547static void
548pgm_writepgminit(ostream * const fileP,
549 int const cols,
550 int const rows,
551 gray const maxval,
552 int const forceplain) {
553
554 bool const plainFormat = forceplain || pm_plain_output;
555
556 if (maxval > PGM_OVERALLMAXVAL && !plainFormat)
557 pm_error("too-large maxval passed to ppm_writepgminit(): %d.\n"
558 "Maximum allowed by the PGM format is %d.",
559 maxval, PGM_OVERALLMAXVAL);
560
561 (*fileP)
562 << (char)PGM_MAGIC1
563 << (char)(plainFormat /*|| maxval >= 1<<16*/ ? PGM_MAGIC2 : RPGM_MAGIC2)
564 << '\n'
565 << cols << ' ' << rows << '\n' << maxval << '\n';
566}
567
568static void
569ppm_writeppminit(ostream* const fileP,
570 int const cols,
571 int const rows,
572 pixval const maxval,
573 int const forceplain) {
574
575 bool const plainFormat = forceplain || pm_plain_output;
576
577 if (maxval > PPM_OVERALLMAXVAL && !plainFormat)
578 pm_error("too-large maxval passed to ppm_writeppminit(): %d."
579 "Maximum allowed by the PPM format is %d.",
580 maxval, PPM_OVERALLMAXVAL);
581
582 (*fileP)
583 << (char)PPM_MAGIC1
584 << (char)(plainFormat /*|| maxval >= 1<<16*/ ? PPM_MAGIC2 : RPPM_MAGIC2)
585 << '\n'
586 << cols << ' ' << rows << '\n' << maxval << '\n';
587}
588
589static void
590pnm_writepnminit(ostream * const fileP,
591 int const cols,
592 int const rows,
593 xelval const maxval,
594 int const format,
595 int const forceplain) {
596
597 bool const plainFormat = forceplain || pm_plain_output;
598
599 switch (PNM_FORMAT_TYPE(format)) {
600 case PPM_TYPE:
601 ppm_writeppminit(fileP, cols, rows, (pixval) maxval, plainFormat);
602 break;
603
604 case PGM_TYPE:
605 pgm_writepgminit(fileP, cols, rows, (gray) maxval, plainFormat);
606 break;
607
608 case PBM_TYPE:
609 pbm_writepbminit(fileP, cols, rows, plainFormat);
610 break;
611
612 default:
613 pm_error("invalid format argument received by pnm_writepnminit(): %d"
614 "PNM_FORMAT_TYPE(format) must be %d, %d, or %d",
615 format, PBM_TYPE, PGM_TYPE, PPM_TYPE);
616 }
617}
618
619static void
620packBitsGeneric(ostream * const fileP,
621 const bit * const bitrow,
622 unsigned char * const packedBits,
623 int const cols,
624 int * const nextColP) {
625 /*----------------------------------------------------------------------------
626 Pack the bits of bitrow[] into byts at 'packedBits'. Going left to right,
627 stop when there aren't enough bits left to fill a whole byte. Return
628 as *nextColP the number of the next column after the rightmost one we
629 packed.
630
631 Don't use any special CPU facilities to do the packing.
632 -----------------------------------------------------------------------------*/
633 int col;
634
635#define iszero(x) ((x) == 0 ? 0 : 1)
636
637 for (col = 0; col + 7 < cols; col += 8)
638 packedBits[col/8] = (
639 iszero(bitrow[col+0]) << 7 |
640 iszero(bitrow[col+1]) << 6 |
641 iszero(bitrow[col+2]) << 5 |
642 iszero(bitrow[col+3]) << 4 |
643 iszero(bitrow[col+4]) << 3 |
644 iszero(bitrow[col+5]) << 2 |
645 iszero(bitrow[col+6]) << 1 |
646 iszero(bitrow[col+7]) << 0
647 );
648 *nextColP = col;
649}
650
651static void
652writePackedRawRow(ostream * const fileP,
653 const unsigned char * const packed_bits,
654 int const cols) {
655
656 fileP->write((const char *)packed_bits, pbm_packed_bytes(cols));
657 if (fileP->fail()) {
658 pm_error("I/O error writing packed row to raw PBM file.");
659 }
660}
661
662static void
663writePbmRowRaw(ostream * const fileP,
664 const bit * const bitrow,
665 int const cols) {
666
667 int nextCol;
668
669 unsigned char * const packedBits = pbm_allocrow_packed(cols);
670
671 packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol);
672
673 /* routine for partial byte at the end of packed_bits[]
674 Prior to addition of the above enhancement,
675 this method was used for the entire process
676 */
677
678 if (cols % 8 > 0) {
679 int col;
680 int bitshift;
681 unsigned char item;
682
683 bitshift = 7; /* initial value */
684 item = 0; /* initial value */
685 for (col = nextCol; col < cols; ++col, --bitshift )
686 if (bitrow[col] !=0)
687 item |= 1 << bitshift
688 ;
689
690 packedBits[col/8] = item;
691 }
692
693 writePackedRawRow(fileP, packedBits, cols);
694
695 pbm_freerow_packed(packedBits);
696}
697
698
699
700static void
701writePbmRowPlain(ostream * const fileP,
702 bit * const bitrow,
703 int const cols) {
704
705 int col, charcount;
706
707 charcount = 0;
708 for (col = 0; col < cols; ++col) {
709 if (charcount >= 70) {
710 fileP->put('\n');
711 charcount = 0;
712 }
713 fileP->put(bitrow[col] ? '1' : '0');
714 ++charcount;
715 }
716 fileP->put('\n');
717}
718
719static void
720pbm_writepbmrow(ostream * const fileP,
721 bit * const bitrow,
722 int const cols,
723 int const forceplain) {
724
725 if (!forceplain && !pm_plain_output)
726 writePbmRowRaw(fileP, bitrow, cols);
727 else
728 writePbmRowPlain(fileP, bitrow, cols);
729}
730
731static void
732pgm_writerawsample(ostream *file, const gray val, const gray maxval) {
733
734 if (maxval < 256) {
735 /* Samples fit in one byte, so write just one byte */
736 file->put(val);
737 if (file->fail())
738 pm_error("Error writing single byte sample to file");
739 } else {
740 /* Samples are too big for one byte, so write two */
741 unsigned char outval[2];
742 /* We could save a few instructions if we exploited the internal
743 format of a gray, i.e. its endianness. Then we might be able
744 to skip the shifting and anding.
745 */
746 outval[0] = val >> 8;
747 outval[1] = val & 0xFF;
748 file->write((const char *)outval, 2);
749 if (file->fail())
750 pm_error("Error writing double byte sample to file");
751 }
752}
753
754static void
755pgm_writepgmrowraw(ostream *file, gray *grayrow, int cols, gray maxval ) {
756 int col;
757
758 for (col = 0; col < cols; ++col) {
759#ifdef DEBUG
760 if (grayrow[col] > maxval)
761 pm_error( "value out of bounds (%u > %u)", grayrow[col], maxval);
762#endif /*DEBUG*/
763 pgm_writerawsample(file, grayrow[col], maxval);
764 }
765}
766
767static void
768putus(unsigned short const n,
769 ostream * const fileP) {
770
771 if (n >= 10)
772 putus(n / 10, fileP);
773 fileP->put(n % 10 + '0');
774}
775
776
777static void
778pgm_writepgmrowplain(ostream * const fileP,
779 gray * const grayrow,
780 int const cols,
781 gray const maxval) {
782
783 int col, charcount;
784 gray* gP;
785
786 charcount = 0;
787 for (col = 0, gP = grayrow; col < cols; ++col, ++gP) {
788 if (charcount >= 65) {
789 fileP->put('\n');
790 charcount = 0;
791 } else if (charcount > 0) {
792 fileP->put(' ');
793 ++charcount;
794 }
795#ifdef DEBUG
796 if (*gP > maxval)
797 pm_error("value out of bounds (%u > %u)", *gP, maxval);
798#endif /*DEBUG*/
799 putus((unsigned short)*gP, fileP);
800 charcount += 3;
801 }
802 if (charcount > 0)
803 fileP->put('\n');
804}
805
806static void
807pgm_writepgmrow(ostream* const fileP,
808 gray* const grayrow,
809 int const cols,
810 gray const maxval,
811 int const forceplain) {
812
813 if (forceplain || pm_plain_output /*|| maxval >= 1<<16*/)
814 pgm_writepgmrowplain(fileP, grayrow, cols, maxval);
815 else
816 pgm_writepgmrowraw(fileP, grayrow, cols, maxval);
817}
818
819static void
820ppm_writeppmrowraw(ostream *file, pixel *pixelrow, int cols, pixval maxval ) {
821 int col;
822 pixval val;
823
824 for ( col = 0; col < cols; ++col )
825 {
826 val = PPM_GETR( pixelrow[col] );
827#ifdef DEBUG
828 if ( val > maxval )
829 pm_error( "r value out of bounds (%u > %u)", val, maxval );
830#endif /*DEBUG*/
831 pgm_writerawsample( file, val, maxval );
832 val = PPM_GETG( pixelrow[col] );
833#ifdef DEBUG
834 if ( val > maxval )
835 pm_error( "g value out of bounds (%u > %u)", val, maxval );
836#endif /*DEBUG*/
837 pgm_writerawsample( file, val, maxval );
838 val = PPM_GETB( pixelrow[col] );
839#ifdef DEBUG
840 if ( val > maxval )
841 pm_error( "b value out of bounds (%u > %u)", val, maxval );
842#endif /*DEBUG*/
843 pgm_writerawsample( file, val, maxval );
844 }
845}
846
847static void
848ppm_writeppmrowplain(ostream *file, pixel *pixelrow, int cols, pixval maxval ) {
849 int col, charcount;
850 pixel* pP;
851 pixval val;
852
853 charcount = 0;
854 for ( col = 0, pP = pixelrow; col < cols; ++col, ++pP )
855 {
856 if ( charcount >= 65 )
857 {
858 (void) file->put( '\n' );
859 charcount = 0;
860 }
861 else if ( charcount > 0 )
862 {
863 (void) file->put( ' ' );
864 (void) file->put( ' ' );
865 charcount += 2;
866 }
867 val = PPM_GETR( *pP );
868#ifdef DEBUG
869 if ( val > maxval )
870 pm_error( "r value out of bounds (%u > %u)", val, maxval );
871#endif /*DEBUG*/
872 putus( val, file );
873 (void) file->put( ' ' );
874 val = PPM_GETG( *pP );
875#ifdef DEBUG
876 if ( val > maxval )
877 pm_error( "g value out of bounds (%u > %u)", val, maxval );
878#endif /*DEBUG*/
879 putus( val, file );
880 (void) file->put( ' ' );
881 val = PPM_GETB( *pP );
882#ifdef DEBUG
883 if ( val > maxval )
884 pm_error( "b value out of bounds (%u > %u)", val, maxval );
885#endif /*DEBUG*/
886 putus( val, file );
887 charcount += 11;
888 }
889 if ( charcount > 0 )
890 (void) file->put( '\n' );
891}
892
893static void
894ppm_writeppmrow(ostream * const fileP,
895 pixel * const pixelrow,
896 int const cols,
897 pixval const maxval,
898 int const forceplain) {
899
900 if (forceplain || pm_plain_output /*|| maxval >= 1<<16*/)
901 ppm_writeppmrowplain(fileP, pixelrow, cols, maxval);
902 else
903 ppm_writeppmrowraw(fileP, pixelrow, cols, maxval);
904}
905
906static void
907pnm_writepnmrow(ostream * const fileP,
908 xel * const xelrow,
909 int const cols,
910 xelval const maxval,
911 int const format,
912 int const forceplain) {
913
914 bool const plainFormat = forceplain || pm_plain_output;
915
916 switch (PNM_FORMAT_TYPE(format)) {
917 case PPM_TYPE:
918 ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval,
919 plainFormat);
920 break;
921
922 case PGM_TYPE: {
923 gray* grayrow;
924 int col;
925
926 grayrow = pgm_allocrow(cols);
927
928 for (col = 0; col < cols; ++col)
929 grayrow[col] = PNM_GET1(xelrow[col]);
930
931 pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat);
932
933 pgm_freerow( grayrow );
934 }
935 break;
936
937 case PBM_TYPE: {
938 bit* bitrow;
939 int col;
940
941 bitrow = pbm_allocrow(cols);
942
943 for (col = 0; col < cols; ++col)
944 bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE;
945
946 pbm_writepbmrow(fileP, bitrow, cols, plainFormat);
947
948 pbm_freerow(bitrow);
949 }
950 break;
951
952 default:
953 pm_error("invalid format argument received by pnm_writepnmrow(): %d"
954 "PNM_FORMAT_TYPE(format) must be %d, %d, or %d",
955 format, PBM_TYPE, PGM_TYPE, PPM_TYPE);
956 }
957}
958
959/**
960 *
961 */
962PNMFileTypePNM::
963PNMFileTypePNM() {
964}
965
966/**
967 * Returns a few words describing the file type.
968 */
969string PNMFileTypePNM::
970get_name() const {
971 return "NetPBM-style PBM/PGM/PPM/PNM";
972}
973
974/**
975 * Returns the number of different possible filename extensions_PNM associated
976 * with this particular file type.
977 */
978int PNMFileTypePNM::
979get_num_extensions() const {
980 return num_extensions_PNM;
981}
982
983/**
984 * Returns the nth possible filename extension associated with this particular
985 * file type, without a leading dot.
986 */
987string PNMFileTypePNM::
988get_extension(int n) const {
989 nassertr(n >= 0 && n < num_extensions_PNM, string());
990 return extensions_PNM[n];
991}
992
993/**
994 * Returns a suitable filename extension (without a leading dot) to suggest
995 * for files of this type, or empty string if no suggestions are available.
996 */
997string PNMFileTypePNM::
998get_suggested_extension() const {
999 return "ppm";
1000}
1001
1002/**
1003 * Returns true if this particular file type uses a magic number to identify
1004 * it, false otherwise.
1005 */
1006bool PNMFileTypePNM::
1007has_magic_number() const {
1008 return true;
1009}
1010
1011/**
1012 * Returns true if the indicated "magic number" byte stream (the initial few
1013 * bytes read from the file) matches this particular file type, false
1014 * otherwise.
1015 */
1016bool PNMFileTypePNM::
1017matches_magic_number(const string &magic_number) const {
1018 return (magic_number.size() >= 2) &&
1019 magic_number[0] == 'P' &&
1020 (magic_number[1] >= '1' && magic_number[1] <= '6');
1021}
1022
1023/**
1024 * Allocates and returns a new PNMReader suitable for reading from this file
1025 * type, if possible. If reading from this file type is not supported,
1026 * returns NULL.
1027 */
1028PNMReader *PNMFileTypePNM::
1029make_reader(istream *file, bool owns_file, const string &magic_number) {
1030 init_pnm();
1031 return new Reader(this, file, owns_file, magic_number);
1032}
1033
1034/**
1035 * Allocates and returns a new PNMWriter suitable for reading from this file
1036 * type, if possible. If writing files of this type is not supported, returns
1037 * NULL.
1038 */
1039PNMWriter *PNMFileTypePNM::
1040make_writer(ostream *file, bool owns_file) {
1041 init_pnm();
1042 return new Writer(this, file, owns_file);
1043}
1044
1045
1046/**
1047 *
1048 */
1049PNMFileTypePNM::Reader::
1050Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
1051 PNMReader(type, file, owns_file)
1052{
1053 if (!read_magic_number(_file, magic_number, 2)) {
1054 // No magic number. No image.
1055 if (pnmimage_pnm_cat.is_debug()) {
1056 pnmimage_pnm_cat.debug()
1057 << "PNM file appears to be empty.\n";
1058 }
1059 _is_valid = false;
1060 return;
1061 }
1062
1063 _ftype =
1064 ((unsigned char)magic_number[0] << 8) |
1065 (unsigned char)magic_number[1];
1066
1067 switch ( PNM_FORMAT_TYPE(_ftype) ) {
1068 case PPM_TYPE:
1069 ppm_readppminitrest( file, &_x_size, &_y_size, &_maxval );
1070 _num_channels = 3;
1071 break;
1072
1073 case PGM_TYPE:
1074 pgm_readpgminitrest( file, &_x_size, &_y_size, &_maxval );
1075 _num_channels = 1;
1076 break;
1077
1078 case PBM_TYPE:
1079 pbm_readpbminitrest( file, &_x_size, &_y_size );
1080 _num_channels = 1;
1081 _maxval = 1;
1082 break;
1083
1084 default:
1085 _is_valid = false;
1086 }
1087
1088 if (pnmimage_pnm_cat.is_debug()) {
1089 if (is_valid()) {
1090 pnmimage_pnm_cat.debug()
1091 << "Reading ";
1092 switch (PNM_FORMAT_TYPE(_ftype)) {
1093 case PPM_TYPE:
1094 pnmimage_pnm_cat.debug(false) << "PPM";
1095 break;
1096 case PGM_TYPE:
1097 pnmimage_pnm_cat.debug(false) << "PGM";
1098 break;
1099 case PBM_TYPE:
1100 pnmimage_pnm_cat.debug(false) << "PBM";
1101 break;
1102 }
1103 pnmimage_pnm_cat.debug(false)
1104 << " " << *this << "\n";
1105 } else {
1106 pnmimage_pnm_cat.debug()
1107 << "File is not a valid PNM image.\n";
1108 }
1109 }
1110}
1111
1112/**
1113 * Returns true if this particular PNMReader supports a streaming interface to
1114 * reading the data: that is, it is capable of returning the data one row at a
1115 * time, via repeated calls to read_row(). Returns false if the only way to
1116 * read from this file is all at once, via read_data().
1117 */
1118bool PNMFileTypePNM::Reader::
1119supports_read_row() const {
1120 return true;
1121}
1122
1123/**
1124 * If supports_read_row(), above, returns true, this function may be called
1125 * repeatedly to read the image, one horizontal row at a time, beginning from
1126 * the top. Returns true if the row is successfully read, false if there is
1127 * an error or end of file.
1128 *
1129 * The x_size and y_size parameters are the value of _x_size and _y_size as
1130 * originally filled in by the constructor; it is the actual number of pixels
1131 * in the image. (The _x_size and _y_size members may have been automatically
1132 * modified by the time this method is called if we are scaling on load, so
1133 * should not be used.)
1134 */
1135bool PNMFileTypePNM::Reader::
1136read_row(xel *array, xelval *, int x_size, int y_size) {
1137 if (!is_valid()) {
1138 return false;
1139 }
1140 pnm_readpnmrow(_file, array, x_size, _maxval, _ftype);
1141 return true;
1142}
1143
1144
1145/**
1146 *
1147 */
1148PNMFileTypePNM::Writer::
1149Writer(PNMFileType *type, ostream *file, bool owns_file) :
1150 PNMWriter(type, file, owns_file)
1151{
1152}
1153
1154/**
1155 * Returns true if this particular PNMWriter supports a streaming interface to
1156 * writing the data: that is, it is capable of writing the image one row at a
1157 * time, via repeated calls to write_row(). Returns false if the only way to
1158 * write from this file is all at once, via write_data().
1159 */
1160bool PNMFileTypePNM::Writer::
1161supports_write_row() const {
1162 return true;
1163}
1164
1165/**
1166 * If supports_write_row(), above, returns true, this function may be called
1167 * to write out the image header in preparation to writing out the image data
1168 * one row at a time. Returns true if the header is successfully written,
1169 * false if there is an error.
1170 *
1171 * It is the user's responsibility to fill in the header data via calls to
1172 * set_x_size(), set_num_channels(), etc., or copy_header_from(), before
1173 * calling write_header().
1174 */
1175bool PNMFileTypePNM::Writer::
1176write_header() {
1177 switch (get_color_type()) {
1178 case PNMImageHeader::CT_grayscale:
1179 case PNMImageHeader::CT_two_channel:
1180 if (_maxval == 1) {
1181 _pnm_format = PBM_TYPE;
1182 } else {
1183 _pnm_format = PGM_TYPE;
1184 }
1185 break;
1186
1187 case PNMImageHeader::CT_color:
1188 case PNMImageHeader::CT_four_channel:
1189 _pnm_format = PPM_TYPE;
1190 break;
1191
1192 default:
1193 break;
1194 }
1195
1196 pnm_writepnminit(_file, _x_size, _y_size, _maxval, _pnm_format, 0);
1197 return true;
1198}
1199
1200/**
1201 * If supports_write_row(), above, returns true, this function may be called
1202 * repeatedly to write the image, one horizontal row at a time, beginning from
1203 * the top. Returns true if the row is successfully written, false if there
1204 * is an error.
1205 *
1206 * You must first call write_header() before writing the individual rows. It
1207 * is also important to delete the PNMWriter class after successfully writing
1208 * the last row. Failing to do this may result in some data not getting
1209 * flushed!
1210 */
1211bool PNMFileTypePNM::Writer::
1212write_row(xel *row_data, xelval *) {
1213 pnm_writepnmrow(_file, row_data, _x_size, _maxval, _pnm_format, 0);
1214
1215 return true;
1216}
1217
1218/**
1219 * Registers the current object as something that can be read from a Bam file.
1220 */
1221void PNMFileTypePNM::
1222register_with_read_factory() {
1224 register_factory(get_class_type(), make_PNMFileTypePNM);
1225}
1226
1227/**
1228 * This method is called by the BamReader when an object of this type is
1229 * encountered in a Bam file; it should allocate and return a new object with
1230 * all the data read.
1231 *
1232 * In the case of the PNMFileType objects, since these objects are all shared,
1233 * we just pull the object from the registry.
1234 */
1235TypedWritable *PNMFileTypePNM::
1236make_PNMFileTypePNM(const FactoryParams &params) {
1237 return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
1238}
1239
1240#endif // HAVE_PNM
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
An instance of this class is passed to the Factory when requesting it to do its business and construc...
PNMFileType * get_type_by_handle(TypeHandle handle) const
Returns the PNMFileType instance stored in the registry for the given TypeHandle, e....
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition pnmFileType.h:32
This is an abstract base class that defines the interface for reading image files of various types.
Definition pnmReader.h:27
This is an abstract base class that defines the interface for writing image files of various types.
Definition pnmWriter.h:27
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
char * pm_allocrow(int cols, int size)
Allocates a row of cols * size bytes.
void pm_freerow(char *itrow)
Frees the row previously allocated withm pm_allocrow().
void pm_error(const char *format,...)
Outputs the given printf-style message to the user and terminates messily.