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