pnm2png.c 16 KB

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