1 /* 2 * pnm2png.c --- conversion from PBM/PGM/PPM-file to PNG-file 3 * copyright (C) 1999 by Willem van Schaik <willem (at) schaik.com> 4 * 5 * version 1.0 - 1999.10.15 - First version. 6 * version 1.1 - 2015.07.29 - Fixed leaks (Glenn Randers-Pehrson) 7 * 8 * Permission to use, copy, modify, and distribute this software and 9 * its documentation for any purpose and without fee is hereby granted, 10 * provided that the above copyright notice appear in all copies and 11 * that both that copyright notice and this permission notice appear in 12 * supporting documentation. This software is provided "as is" without 13 * express or implied warranty. 14 */ 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #ifdef __TURBOC__ 19 #include <mem.h> 20 #include <fcntl.h> 21 #endif 22 #include <zlib.h> 23 24 #ifndef BOOL 25 #define BOOL unsigned char 26 #endif 27 #ifndef TRUE 28 #define TRUE (BOOL) 1 29 #endif 30 #ifndef FALSE 31 #define FALSE (BOOL) 0 32 #endif 33 34 #define STDIN 0 35 #define STDOUT 1 36 #define STDERR 2 37 38 /* to make pnm2png verbose so we can find problems (needs to be before png.h) */ 39 #ifndef PNG_DEBUG 40 #define PNG_DEBUG 0 41 #endif 42 43 #include "png.h" 44 45 /* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ 46 #ifndef png_jmpbuf 47 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) 48 #endif 49 50 /* function prototypes */ 51 52 int main (int argc, char *argv[]); 53 void usage (); 54 BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, 55 BOOL alpha); 56 void get_token(FILE *pnm_file, char *token); 57 png_uint_32 get_data (FILE *pnm_file, int depth); 58 png_uint_32 get_value (FILE *pnm_file, int depth); 59 60 /* 61 * main 62 */ 63 64 int main(int argc, char *argv[]) 65 { 66 FILE *fp_rd = stdin; 67 FILE *fp_al = NULL; 68 FILE *fp_wr = stdout; 69 BOOL interlace = FALSE; 70 BOOL alpha = FALSE; 71 int argi; 72 73 for (argi = 1; argi < argc; argi++) 74 { 75 if (argv[argi][0] == '-') 76 { 77 switch (argv[argi][1]) 78 { 79 case 'i': 80 interlace = TRUE; 81 break; 82 case 'a': 83 alpha = TRUE; 84 argi++; 85 if ((fp_al = fopen (argv[argi], "rb")) == NULL) 86 { 87 fprintf (stderr, "PNM2PNG\n"); 88 fprintf (stderr, "Error: alpha-channel file %s does not exist\n", 89 argv[argi]); 90 exit (1); 91 } 92 break; 93 case 'h': 94 case '?': 95 usage(); 96 exit(0); 97 break; 98 default: 99 fprintf (stderr, "PNM2PNG\n"); 100 fprintf (stderr, "Error: unknown option %s\n", argv[argi]); 101 usage(); 102 exit(1); 103 break; 104 } /* end switch */ 105 } 106 else if (fp_rd == stdin) 107 { 108 if ((fp_rd = fopen (argv[argi], "rb")) == NULL) 109 { 110 fprintf (stderr, "PNM2PNG\n"); 111 fprintf (stderr, "Error: file %s does not exist\n", argv[argi]); 112 exit (1); 113 } 114 } 115 else if (fp_wr == stdout) 116 { 117 if ((fp_wr = fopen (argv[argi], "wb")) == NULL) 118 { 119 fprintf (stderr, "PNM2PNG\n"); 120 fprintf (stderr, "Error: can not create PNG-file %s\n", argv[argi]); 121 exit (1); 122 } 123 } 124 else 125 { 126 fprintf (stderr, "PNM2PNG\n"); 127 fprintf (stderr, "Error: too many parameters\n"); 128 usage(); 129 exit (1); 130 } 131 } /* end for */ 132 133 #ifdef __TURBOC__ 134 /* set stdin/stdout to binary, we're reading the PNM always! in binary format */ 135 if (fp_rd == stdin) 136 { 137 setmode (STDIN, O_BINARY); 138 } 139 if (fp_wr == stdout) 140 { 141 setmode (STDOUT, O_BINARY); 142 } 143 #endif 144 145 /* call the conversion program itself */ 146 if (pnm2png (fp_rd, fp_wr, fp_al, interlace, alpha) == FALSE) 147 { 148 fprintf (stderr, "PNM2PNG\n"); 149 fprintf (stderr, "Error: unsuccessful converting to PNG-image\n"); 150 exit (1); 151 } 152 153 /* close input file */ 154 fclose (fp_rd); 155 /* close output file */ 156 fclose (fp_wr); 157 /* close alpha file */ 158 if (alpha) 159 fclose (fp_al); 160 161 return 0; 162 } 163 164 /* 165 * usage 166 */ 167 168 void usage() 169 { 170 fprintf (stderr, "PNM2PNG\n"); 171 fprintf (stderr, " by Willem van Schaik, 1999\n"); 172 #ifdef __TURBOC__ 173 fprintf (stderr, " for Turbo-C and Borland-C compilers\n"); 174 #else 175 fprintf (stderr, " for Linux (and Unix) compilers\n"); 176 #endif 177 fprintf (stderr, "Usage: pnm2png [options] <file>.<pnm> [<file>.png]\n"); 178 fprintf (stderr, " or: ... | pnm2png [options]\n"); 179 fprintf (stderr, "Options:\n"); 180 fprintf (stderr, " -i[nterlace] write png-file with interlacing on\n"); 181 fprintf (stderr, 182 " -a[lpha] <file>.pgm read PNG alpha channel as pgm-file\n"); 183 fprintf (stderr, " -h | -? print this help-information\n"); 184 } 185 186 /* 187 * pnm2png 188 */ 189 190 BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, 191 BOOL alpha) 192 { 193 png_struct *png_ptr = NULL; 194 png_info *info_ptr = NULL; 195 png_byte *png_pixels = NULL; 196 png_byte **row_pointers = NULL; 197 png_byte *pix_ptr = NULL; 198 volatile png_uint_32 row_bytes; 199 200 char type_token[16]; 201 char width_token[16]; 202 char height_token[16]; 203 char maxval_token[16]; 204 volatile int color_type=1; 205 unsigned long ul_width=0, ul_alpha_width=0; 206 unsigned long ul_height=0, ul_alpha_height=0; 207 unsigned long ul_maxval=0; 208 volatile png_uint_32 width=0, height=0; 209 volatile png_uint_32 alpha_width=0, alpha_height=0; 210 png_uint_32 maxval; 211 volatile int bit_depth = 0; 212 int channels=0; 213 int alpha_depth = 0; 214 int alpha_present=0; 215 int row, col; 216 BOOL raw, alpha_raw = FALSE; 217 #if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) 218 BOOL packed_bitmap = FALSE; 219 #endif 220 png_uint_32 tmp16; 221 int i; 222 223 /* read header of PNM file */ 224 225 get_token(pnm_file, type_token); 226 if (type_token[0] != 'P') 227 { 228 return FALSE; 229 } 230 else if ((type_token[1] == '1') || (type_token[1] == '4')) 231 { 232 #if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) 233 raw = (type_token[1] == '4'); 234 color_type = PNG_COLOR_TYPE_GRAY; 235 get_token(pnm_file, width_token); 236 sscanf (width_token, "%lu", &ul_width); 237 width = (png_uint_32) ul_width; 238 get_token(pnm_file, height_token); 239 sscanf (height_token, "%lu", &ul_height); 240 height = (png_uint_32) ul_height; 241 bit_depth = 1; 242 packed_bitmap = TRUE; 243 #else 244 fprintf (stderr, "PNM2PNG built without PNG_WRITE_INVERT_SUPPORTED and \n"); 245 fprintf (stderr, "PNG_WRITE_PACK_SUPPORTED can't read PBM (P1,P4) files\n"); 246 #endif 247 } 248 else if ((type_token[1] == '2') || (type_token[1] == '5')) 249 { 250 raw = (type_token[1] == '5'); 251 color_type = PNG_COLOR_TYPE_GRAY; 252 get_token(pnm_file, width_token); 253 sscanf (width_token, "%lu", &ul_width); 254 width = (png_uint_32) ul_width; 255 get_token(pnm_file, height_token); 256 sscanf (height_token, "%lu", &ul_height); 257 height = (png_uint_32) ul_height; 258 get_token(pnm_file, maxval_token); 259 sscanf (maxval_token, "%lu", &ul_maxval); 260 maxval = (png_uint_32) ul_maxval; 261 262 if (maxval <= 1) 263 bit_depth = 1; 264 else if (maxval <= 3) 265 bit_depth = 2; 266 else if (maxval <= 15) 267 bit_depth = 4; 268 else if (maxval <= 255) 269 bit_depth = 8; 270 else /* if (maxval <= 65535) */ 271 bit_depth = 16; 272 } 273 else if ((type_token[1] == '3') || (type_token[1] == '6')) 274 { 275 raw = (type_token[1] == '6'); 276 color_type = PNG_COLOR_TYPE_RGB; 277 get_token(pnm_file, width_token); 278 sscanf (width_token, "%lu", &ul_width); 279 width = (png_uint_32) ul_width; 280 get_token(pnm_file, height_token); 281 sscanf (height_token, "%lu", &ul_height); 282 height = (png_uint_32) ul_height; 283 get_token(pnm_file, maxval_token); 284 sscanf (maxval_token, "%lu", &ul_maxval); 285 maxval = (png_uint_32) ul_maxval; 286 if (maxval <= 1) 287 bit_depth = 1; 288 else if (maxval <= 3) 289 bit_depth = 2; 290 else if (maxval <= 15) 291 bit_depth = 4; 292 else if (maxval <= 255) 293 bit_depth = 8; 294 else /* if (maxval <= 65535) */ 295 bit_depth = 16; 296 } 297 else 298 { 299 return FALSE; 300 } 301 302 /* read header of PGM file with alpha channel */ 303 304 if (alpha) 305 { 306 if (color_type == PNG_COLOR_TYPE_GRAY) 307 color_type = PNG_COLOR_TYPE_GRAY_ALPHA; 308 if (color_type == PNG_COLOR_TYPE_RGB) 309 color_type = PNG_COLOR_TYPE_RGB_ALPHA; 310 311 get_token(alpha_file, type_token); 312 if (type_token[0] != 'P') 313 { 314 return FALSE; 315 } 316 else if ((type_token[1] == '2') || (type_token[1] == '5')) 317 { 318 alpha_raw = (type_token[1] == '5'); 319 get_token(alpha_file, width_token); 320 sscanf (width_token, "%lu", &ul_alpha_width); 321 alpha_width=(png_uint_32) ul_alpha_width; 322 if (alpha_width != width) 323 return FALSE; 324 get_token(alpha_file, height_token); 325 sscanf (height_token, "%lu", &ul_alpha_height); 326 alpha_height = (png_uint_32) ul_alpha_height; 327 if (alpha_height != height) 328 return FALSE; 329 get_token(alpha_file, maxval_token); 330 sscanf (maxval_token, "%lu", &ul_maxval); 331 maxval = (png_uint_32) ul_maxval; 332 if (maxval <= 1) 333 alpha_depth = 1; 334 else if (maxval <= 3) 335 alpha_depth = 2; 336 else if (maxval <= 15) 337 alpha_depth = 4; 338 else if (maxval <= 255) 339 alpha_depth = 8; 340 else /* if (maxval <= 65535) */ 341 alpha_depth = 16; 342 if (alpha_depth != bit_depth) 343 return FALSE; 344 } 345 else 346 { 347 return FALSE; 348 } 349 } /* end if alpha */ 350 351 /* calculate the number of channels and store alpha-presence */ 352 if (color_type == PNG_COLOR_TYPE_GRAY) 353 channels = 1; 354 else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 355 channels = 2; 356 else if (color_type == PNG_COLOR_TYPE_RGB) 357 channels = 3; 358 else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) 359 channels = 4; 360 #if 0 361 else 362 channels = 0; /* cannot happen */ 363 #endif 364 365 alpha_present = (channels - 1) % 2; 366 367 #if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) 368 if (packed_bitmap) 369 /* row data is as many bytes as can fit width x channels x bit_depth */ 370 row_bytes = (width * channels * bit_depth + 7) / 8; 371 else 372 #endif 373 /* row_bytes is the width x number of channels x (bit-depth / 8) */ 374 row_bytes = width * channels * ((bit_depth <= 8) ? 1 : 2); 375 376 if ((png_pixels = (png_byte *) 377 malloc (row_bytes * height * sizeof (png_byte))) == NULL) 378 return FALSE; 379 380 /* read data from PNM file */ 381 pix_ptr = png_pixels; 382 383 for (row = 0; row < (int) height; row++) 384 { 385 #if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) 386 if (packed_bitmap) { 387 for (i = 0; i < (int) row_bytes; i++) 388 /* png supports this format natively so no conversion is needed */ 389 *pix_ptr++ = get_data (pnm_file, 8); 390 } else 391 #endif 392 { 393 for (col = 0; col < (int) width; col++) 394 { 395 for (i = 0; i < (channels - alpha_present); i++) 396 { 397 if (raw) 398 *pix_ptr++ = get_data (pnm_file, bit_depth); 399 else 400 if (bit_depth <= 8) 401 *pix_ptr++ = get_value (pnm_file, bit_depth); 402 else 403 { 404 tmp16 = get_value (pnm_file, bit_depth); 405 *pix_ptr = (png_byte) ((tmp16 >> 8) & 0xFF); 406 pix_ptr++; 407 *pix_ptr = (png_byte) (tmp16 & 0xFF); 408 pix_ptr++; 409 } 410 } 411 412 if (alpha) /* read alpha-channel from pgm file */ 413 { 414 if (alpha_raw) 415 *pix_ptr++ = get_data (alpha_file, alpha_depth); 416 else 417 if (alpha_depth <= 8) 418 *pix_ptr++ = get_value (alpha_file, bit_depth); 419 else 420 { 421 tmp16 = get_value (alpha_file, bit_depth); 422 *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF); 423 *pix_ptr++ = (png_byte) (tmp16 & 0xFF); 424 } 425 } /* if alpha */ 426 } /* if packed_bitmap */ 427 } /* end for col */ 428 } /* end for row */ 429 430 /* prepare the standard PNG structures */ 431 png_ptr = png_create_write_struct (png_get_libpng_ver(NULL), NULL, NULL, 432 NULL); 433 if (!png_ptr) 434 { 435 free (png_pixels); 436 png_pixels = NULL; 437 return FALSE; 438 } 439 info_ptr = png_create_info_struct (png_ptr); 440 if (!info_ptr) 441 { 442 png_destroy_write_struct (&png_ptr, (png_infopp) NULL); 443 free (png_pixels); 444 png_pixels = NULL; 445 return FALSE; 446 } 447 448 #if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) 449 if (packed_bitmap == TRUE) 450 { 451 png_set_packing (png_ptr); 452 png_set_invert_mono (png_ptr); 453 } 454 #endif 455 456 /* setjmp() must be called in every function that calls a PNG-reading libpng function */ 457 if (setjmp (png_jmpbuf(png_ptr))) 458 { 459 png_destroy_write_struct (&png_ptr, &info_ptr); 460 free (png_pixels); 461 png_pixels = NULL; 462 return FALSE; 463 } 464 465 /* initialize the png structure */ 466 png_init_io (png_ptr, png_file); 467 468 /* we're going to write more or less the same PNG as the input file */ 469 png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth, color_type, 470 (!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7, 471 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 472 473 /* write the file header information */ 474 png_write_info (png_ptr, info_ptr); 475 476 /* if needed we will allocate memory for an new array of row-pointers */ 477 if (row_pointers == (unsigned char**) NULL) 478 { 479 if ((row_pointers = (png_byte **) 480 malloc (height * sizeof (png_bytep))) == NULL) 481 { 482 png_destroy_write_struct (&png_ptr, &info_ptr); 483 free (png_pixels); 484 png_pixels = NULL; 485 return FALSE; 486 } 487 } 488 489 /* set the individual row_pointers to point at the correct offsets */ 490 for (i = 0; i < (int) height; i++) 491 row_pointers[i] = png_pixels + i * row_bytes; 492 493 /* write out the entire image data in one call */ 494 png_write_image (png_ptr, row_pointers); 495 496 /* write the additional chunks to the PNG file (not really needed) */ 497 png_write_end (png_ptr, info_ptr); 498 499 /* clean up after the write, and free any memory allocated */ 500 png_destroy_write_struct (&png_ptr, &info_ptr); 501 502 if (row_pointers != (unsigned char**) NULL) 503 free (row_pointers); 504 if (png_pixels != (unsigned char*) NULL) 505 free (png_pixels); 506 507 return TRUE; 508 } /* end of pnm2png */ 509 510 /* 511 * get_token() - gets the first string after whitespace 512 */ 513 514 void get_token(FILE *pnm_file, char *token) 515 { 516 int i = 0; 517 int ret; 518 519 /* remove white-space and comment lines */ 520 do 521 { 522 ret = fgetc(pnm_file); 523 if (ret == '#') { 524 /* the rest of this line is a comment */ 525 do 526 { 527 ret = fgetc(pnm_file); 528 } 529 while ((ret != '\n') && (ret != '\r') && (ret != EOF)); 530 } 531 if (ret == EOF) break; 532 token[i] = (unsigned char) ret; 533 } 534 while ((token[i] == '\n') || (token[i] == '\r') || (token[i] == ' ')); 535 536 /* read string */ 537 do 538 { 539 ret = fgetc(pnm_file); 540 if (ret == EOF) break; 541 i++; 542 token[i] = (unsigned char) ret; 543 } 544 while ((token[i] != '\n') && (token[i] != '\r') && (token[i] != ' ')); 545 546 token[i] = '\0'; 547 548 return; 549 } 550 551 /* 552 * get_data() - takes first byte and converts into next pixel value, 553 * taking as much bits as defined by bit-depth and 554 * using the bit-depth to fill up a byte (0Ah -> AAh) 555 */ 556 557 png_uint_32 get_data (FILE *pnm_file, int depth) 558 { 559 static int bits_left = 0; 560 static int old_value = 0; 561 static int mask = 0; 562 int i; 563 png_uint_32 ret_value; 564 565 if (mask == 0) 566 for (i = 0; i < depth; i++) 567 mask = (mask >> 1) | 0x80; 568 569 if (bits_left <= 0) 570 { 571 old_value = fgetc (pnm_file); 572 bits_left = 8; 573 } 574 575 ret_value = old_value & mask; 576 for (i = 1; i < (8 / depth); i++) 577 ret_value = ret_value || (ret_value >> depth); 578 579 old_value = (old_value << depth) & 0xFF; 580 bits_left -= depth; 581 582 return ret_value; 583 } 584 585 /* 586 * get_value() - takes first (numeric) string and converts into number, 587 * using the bit-depth to fill up a byte (0Ah -> AAh) 588 */ 589 590 png_uint_32 get_value (FILE *pnm_file, int depth) 591 { 592 static png_uint_32 mask = 0; 593 png_byte token[16]; 594 unsigned long ul_ret_value; 595 png_uint_32 ret_value; 596 int i = 0; 597 598 if (mask == 0) 599 for (i = 0; i < depth; i++) 600 mask = (mask << 1) | 0x01; 601 602 get_token (pnm_file, (char *) token); 603 sscanf ((const char *) token, "%lu", &ul_ret_value); 604 ret_value = (png_uint_32) ul_ret_value; 605 606 ret_value &= mask; 607 608 if (depth < 8) 609 for (i = 0; i < (8 / depth); i++) 610 ret_value = (ret_value << depth) || ret_value; 611 612 return ret_value; 613 } 614 615 /* end of source */ 616 617