27 static const char *
const extensions_PNM[] = {
28 "pbm",
"pgm",
"ppm",
"pnm"
30 static const int num_extensions_PNM =
sizeof(extensions_PNM) /
sizeof(
const char *);
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
43 #define PBM_FORMAT_TYPE(f) \
44 ((f) == PBM_FORMAT || (f) == RPBM_FORMAT ? PBM_TYPE : -1)
46 #define PGM_OVERALLMAXVAL 65535
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
55 #define PGM_FORMAT_TYPE(f) ((f) == PGM_FORMAT || (f) == RPGM_FORMAT ? PGM_TYPE : PBM_FORMAT_TYPE(f))
57 #define PPM_OVERALLMAXVAL PGM_OVERALLMAXVAL
58 #define PPM_MAXMAXVAL PGM_MAXMAXVAL
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
67 #define PPM_FORMAT_TYPE(f) \
68 ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f))
70 #define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL
71 #define PNM_GET1(x) PPM_GETB(x)
73 #define PNM_FORMAT_TYPE(f) PPM_FORMAT_TYPE(f)
75 typedef unsigned char bit;
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)
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 )
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 )
105 static const bool pm_plain_output =
false;
111 pm_allocarray(
int const cols,
int const rows,
int const size ) {
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",
137 rowheap = (
char *)PANDA_MALLOC_ARRAY( rows * cols * size );
138 if ( rowheap ==
nullptr ) {
144 rowIndex[rows] =
nullptr;
146 for (row = 0; row < rows; ++row) {
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);
156 rowIndex[rows] = rowheap;
158 for (row = 0; row < rows; ++row)
159 rowIndex[row] = &(rowheap[row * cols * size]);
165 pm_freearray(
char **
const rowIndex,
168 void *
const rowheap = rowIndex[rows];
170 if (rowheap !=
nullptr) {
171 PANDA_FREE_ARRAY(rowheap);
174 for (row = 0; row < rows; ++row) {
178 PANDA_FREE_ARRAY(rowIndex);
182 pm_getuint(istream *
const ifP) {
207 }
while (ch != EOF && ch !=
'\n');
209 }
while (ch ==
' ' || ch ==
'\t' || ch ==
'\n' || ch ==
'\r');
211 if (ch < '0' || ch >
'9')
212 pm_error(
"junk in file where an unsigned integer should be");
216 unsigned int const digitVal = ch -
'0';
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;
223 }
while (ch >=
'0' && ch <=
'9');
229 ppm_readppminitrest(istream *
const file,
232 pixval *
const maxvalP) {
236 *colsP = (int)pm_getuint(file);
237 *rowsP = (int)pm_getuint(file);
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);
246 pm_error(
"maxval of input image is zero.");
252 pgm_readpgminitrest(istream *
const file,
255 gray *
const maxvalP) {
260 *colsP = (int)pm_getuint(file);
261 *rowsP = (int)pm_getuint(file);
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);
270 pm_error(
"maxval of input image is zero.");
276 pbm_readpbminitrest(istream* file,
280 *colsP = (int)pm_getuint( file );
281 *rowsP = (int)pm_getuint( file );
292 pm_error(
"Number of columns in header is too large.");
294 pm_error(
"Number of columns in header is too large.");
298 pm_getrawbyte(istream *
const file) {
303 pm_error(
"EOF / read error reading a one-byte sample");
304 return (
unsigned char) iby;
308 getbit (istream *
const file) {
313 }
while ( ch ==
' ' || ch ==
'\t' || ch ==
'\n' || ch ==
'\r' );
315 if ( ch !=
'0' && ch !=
'1' )
316 pm_error(
"junk in file where bits should be" );
318 return ( ch ==
'1' ) ? 1 : 0;
322 pbm_readpbmrow( istream *file, bit *bitrow,
int cols,
int format ) {
329 for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
330 *bP = getbit( file );
335 bitshift = -1; item = 0;
336 for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
338 if ( bitshift == -1 )
340 item = pm_getrawbyte( file );
343 *bP = ( item >> bitshift ) & 1;
355 pgm_getrawsample(istream *
const file, gray
const maxval) {
359 return(pm_getrawbyte(file));
362 unsigned char byte_pair[2];
365 file->read((
char *)byte_pair, 2);
366 pairs_read = file->gcount();
368 pm_error(
"EOF /read error while reading a long sample");
373 return((byte_pair[0]<<8) | byte_pair[1]);
378 pgm_readpgmrow(istream*
const file, gray*
const grayrow,
379 int const cols, gray
const maxval,
int const format) {
384 for (col = 0; col < cols; ++col) {
385 grayrow[col] = pm_getuint(file);
387 if (grayrow[col] > maxval)
388 pm_error(
"value out of bounds (%u > %u)",
389 grayrow[col], maxval );
397 for (col = 0; col < cols; ++col) {
398 grayrow[col] = pgm_getrawsample( file, maxval );
400 if ( grayrow[col] > maxval )
401 pm_error(
"value out of bounds (%u > %u)",
402 grayrow[col], maxval );
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;
428 ppm_readppmrow(istream*
const fileP,
429 pixel*
const pixelrow,
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);
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);
459 gray *
const grayrow = pgm_allocrow(cols);
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);
467 pgm_freerow(grayrow);
473 bit *
const bitrow = pbm_allocrow(cols);
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);
491 pnm_readpnmrow( istream* file,
xel* xelrow,
int cols, xelval maxval,
int format ) {
499 switch ( PNM_FORMAT_TYPE(format) )
502 ppm_readppmrow( file, (
pixel*) xelrow, cols, (pixval) maxval, format );
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 );
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 );
527 pbm_writepbminit(ostream *
const fileP,
530 int const forceplain) {
532 if (!forceplain && !pm_plain_output) {
537 << cols <<
' ' << rows <<
'\n';
543 << cols <<
' ' << rows <<
'\n';
548 pgm_writepgminit(ostream *
const fileP,
552 int const forceplain) {
554 bool const plainFormat = forceplain || pm_plain_output;
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);
563 << (
char)(plainFormat ? PGM_MAGIC2 : RPGM_MAGIC2)
565 << cols <<
' ' << rows <<
'\n' << maxval <<
'\n';
569 ppm_writeppminit(ostream*
const fileP,
573 int const forceplain) {
575 bool const plainFormat = forceplain || pm_plain_output;
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);
584 << (
char)(plainFormat ? PPM_MAGIC2 : RPPM_MAGIC2)
586 << cols <<
' ' << rows <<
'\n' << maxval <<
'\n';
590 pnm_writepnminit(ostream *
const fileP,
595 int const forceplain) {
597 bool const plainFormat = forceplain || pm_plain_output;
599 switch (PNM_FORMAT_TYPE(format)) {
601 ppm_writeppminit(fileP, cols, rows, (pixval) maxval, plainFormat);
605 pgm_writepgminit(fileP, cols, rows, (gray) maxval, plainFormat);
609 pbm_writepbminit(fileP, cols, rows, plainFormat);
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);
620 packBitsGeneric(ostream *
const fileP,
621 const bit *
const bitrow,
622 unsigned char *
const packedBits,
624 int *
const nextColP) {
635 #define iszero(x) ((x) == 0 ? 0 : 1)
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
652 writePackedRawRow(ostream *
const fileP,
653 const unsigned char *
const packed_bits,
656 fileP->write((
const char *)packed_bits, pbm_packed_bytes(cols));
658 pm_error(
"I/O error writing packed row to raw PBM file.");
663 writePbmRowRaw(ostream *
const fileP,
664 const bit *
const bitrow,
669 unsigned char *
const packedBits = pbm_allocrow_packed(cols);
671 packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol);
685 for (col = nextCol; col < cols; ++col, --bitshift )
687 item |= 1 << bitshift
690 packedBits[col/8] = item;
693 writePackedRawRow(fileP, packedBits, cols);
695 pbm_freerow_packed(packedBits);
701 writePbmRowPlain(ostream *
const fileP,
708 for (col = 0; col < cols; ++col) {
709 if (charcount >= 70) {
713 fileP->put(bitrow[col] ?
'1' :
'0');
720 pbm_writepbmrow(ostream *
const fileP,
723 int const forceplain) {
725 if (!forceplain && !pm_plain_output)
726 writePbmRowRaw(fileP, bitrow, cols);
728 writePbmRowPlain(fileP, bitrow, cols);
732 pgm_writerawsample(ostream *file,
const gray val,
const gray maxval) {
738 pm_error(
"Error writing single byte sample to file");
741 unsigned char outval[2];
746 outval[0] = val >> 8;
747 outval[1] = val & 0xFF;
748 file->write((
const char *)outval, 2);
750 pm_error(
"Error writing double byte sample to file");
755 pgm_writepgmrowraw(ostream *file, gray *grayrow,
int cols, gray maxval ) {
758 for (col = 0; col < cols; ++col) {
760 if (grayrow[col] > maxval)
761 pm_error(
"value out of bounds (%u > %u)", grayrow[col], maxval);
763 pgm_writerawsample(file, grayrow[col], maxval);
768 putus(
unsigned short const n,
769 ostream *
const fileP) {
772 putus(n / 10, fileP);
773 fileP->put(n % 10 +
'0');
778 pgm_writepgmrowplain(ostream *
const fileP,
779 gray *
const grayrow,
787 for (col = 0, gP = grayrow; col < cols; ++col, ++gP) {
788 if (charcount >= 65) {
791 }
else if (charcount > 0) {
797 pm_error(
"value out of bounds (%u > %u)", *gP, maxval);
799 putus((
unsigned short)*gP, fileP);
807 pgm_writepgmrow(ostream*
const fileP,
811 int const forceplain) {
813 if (forceplain || pm_plain_output )
814 pgm_writepgmrowplain(fileP, grayrow, cols, maxval);
816 pgm_writepgmrowraw(fileP, grayrow, cols, maxval);
820 ppm_writeppmrowraw(ostream *file,
pixel *pixelrow,
int cols, pixval maxval ) {
824 for ( col = 0; col < cols; ++col )
826 val = PPM_GETR( pixelrow[col] );
829 pm_error(
"r value out of bounds (%u > %u)", val, maxval );
831 pgm_writerawsample( file, val, maxval );
832 val = PPM_GETG( pixelrow[col] );
835 pm_error(
"g value out of bounds (%u > %u)", val, maxval );
837 pgm_writerawsample( file, val, maxval );
838 val = PPM_GETB( pixelrow[col] );
841 pm_error(
"b value out of bounds (%u > %u)", val, maxval );
843 pgm_writerawsample( file, val, maxval );
848 ppm_writeppmrowplain(ostream *file,
pixel *pixelrow,
int cols, pixval maxval ) {
854 for ( col = 0, pP = pixelrow; col < cols; ++col, ++pP )
856 if ( charcount >= 65 )
858 (void) file->put(
'\n' );
861 else if ( charcount > 0 )
863 (void) file->put(
' ' );
864 (void) file->put(
' ' );
867 val = PPM_GETR( *pP );
870 pm_error(
"r value out of bounds (%u > %u)", val, maxval );
873 (void) file->put(
' ' );
874 val = PPM_GETG( *pP );
877 pm_error(
"g value out of bounds (%u > %u)", val, maxval );
880 (void) file->put(
' ' );
881 val = PPM_GETB( *pP );
884 pm_error(
"b value out of bounds (%u > %u)", val, maxval );
890 (void) file->put(
'\n' );
894 ppm_writeppmrow(ostream *
const fileP,
895 pixel *
const pixelrow,
898 int const forceplain) {
900 if (forceplain || pm_plain_output )
901 ppm_writeppmrowplain(fileP, pixelrow, cols, maxval);
903 ppm_writeppmrowraw(fileP, pixelrow, cols, maxval);
907 pnm_writepnmrow(ostream *
const fileP,
912 int const forceplain) {
914 bool const plainFormat = forceplain || pm_plain_output;
916 switch (PNM_FORMAT_TYPE(format)) {
918 ppm_writeppmrow(fileP, (
pixel*) xelrow, cols, (pixval) maxval,
926 grayrow = pgm_allocrow(cols);
928 for (col = 0; col < cols; ++col)
929 grayrow[col] = PNM_GET1(xelrow[col]);
931 pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat);
933 pgm_freerow( grayrow );
941 bitrow = pbm_allocrow(cols);
943 for (col = 0; col < cols; ++col)
944 bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE;
946 pbm_writepbmrow(fileP, bitrow, cols, plainFormat);
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);
969 string PNMFileTypePNM::
971 return "NetPBM-style PBM/PGM/PPM/PNM";
979 get_num_extensions()
const {
980 return num_extensions_PNM;
987 string PNMFileTypePNM::
988 get_extension(
int n)
const {
989 nassertr(n >= 0 && n < num_extensions_PNM,
string());
990 return extensions_PNM[n];
997 string PNMFileTypePNM::
998 get_suggested_extension()
const {
1006 bool PNMFileTypePNM::
1007 has_magic_number()
const {
1016 bool PNMFileTypePNM::
1017 matches_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');
1029 make_reader(istream *file,
bool owns_file,
const string &magic_number) {
1031 return new Reader(
this, file, owns_file, magic_number);
1040 make_writer(ostream *file,
bool owns_file) {
1042 return new Writer(
this, file, owns_file);
1049 PNMFileTypePNM::Reader::
1050 Reader(
PNMFileType *type, istream *file,
bool owns_file,
string magic_number) :
1055 if (pnmimage_pnm_cat.is_debug()) {
1056 pnmimage_pnm_cat.debug()
1057 <<
"PNM file appears to be empty.\n";
1064 ((
unsigned char)magic_number[0] << 8) |
1065 (
unsigned char)magic_number[1];
1067 switch ( PNM_FORMAT_TYPE(_ftype) ) {
1069 ppm_readppminitrest( file, &_x_size, &_y_size, &_maxval );
1074 pgm_readpgminitrest( file, &_x_size, &_y_size, &_maxval );
1079 pbm_readpbminitrest( file, &_x_size, &_y_size );
1088 if (pnmimage_pnm_cat.is_debug()) {
1090 pnmimage_pnm_cat.debug()
1092 switch (PNM_FORMAT_TYPE(_ftype)) {
1094 pnmimage_pnm_cat.debug(
false) <<
"PPM";
1097 pnmimage_pnm_cat.debug(
false) <<
"PGM";
1100 pnmimage_pnm_cat.debug(
false) <<
"PBM";
1103 pnmimage_pnm_cat.debug(
false)
1104 <<
" " << *
this <<
"\n";
1106 pnmimage_pnm_cat.debug()
1107 <<
"File is not a valid PNM image.\n";
1118 bool PNMFileTypePNM::Reader::
1119 supports_read_row()
const {
1135 bool PNMFileTypePNM::Reader::
1136 read_row(
xel *array, xelval *,
int x_size,
int y_size) {
1140 pnm_readpnmrow(_file, array, x_size, _maxval, _ftype);
1148 PNMFileTypePNM::Writer::
1149 Writer(
PNMFileType *type, ostream *file,
bool owns_file) :
1160 bool PNMFileTypePNM::Writer::
1161 supports_write_row()
const {
1175 bool PNMFileTypePNM::Writer::
1177 switch (get_color_type()) {
1178 case PNMImageHeader::CT_grayscale:
1179 case PNMImageHeader::CT_two_channel:
1181 _pnm_format = PBM_TYPE;
1183 _pnm_format = PGM_TYPE;
1187 case PNMImageHeader::CT_color:
1188 case PNMImageHeader::CT_four_channel:
1189 _pnm_format = PPM_TYPE;
1196 pnm_writepnminit(_file, _x_size, _y_size, _maxval, _pnm_format, 0);
1211 bool PNMFileTypePNM::Writer::
1212 write_row(
xel *row_data, xelval *) {
1213 pnm_writepnmrow(_file, row_data, _x_size, _maxval, _pnm_format, 0);
1221 void PNMFileTypePNM::
1222 register_with_read_factory() {
1224 register_factory(get_class_type(), make_PNMFileTypePNM);