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