1 /*
2  *  png2pnm.c --- conversion from PNG-file to PGM/PPM-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 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <fcntl.h>
12 
13 #ifndef BOOL
14 #define BOOL unsigned char
15 #endif
16 #ifndef TRUE
17 #define TRUE (BOOL) 1
18 #endif
19 #ifndef FALSE
20 #define FALSE (BOOL) 0
21 #endif
22 
23 /* make png2pnm verbose so we can find problems (needs to be before png.h) */
24 #ifndef PNG_DEBUG
25 #define PNG_DEBUG 0
26 #endif
27 
28 #include "png.h"
29 
30 /* function prototypes */
31 
32 int main (int argc, char *argv[]);
33 void usage ();
34 BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
35               BOOL raw, BOOL alpha);
36 
37 /*
38  *  main
39  */
40 
main(int argc,char * argv[])41 int main (int argc, char *argv[])
42 {
43   FILE *fp_rd = stdin;
44   FILE *fp_wr = stdout;
45   FILE *fp_al = NULL;
46   BOOL raw = TRUE;
47   BOOL alpha = FALSE;
48   int argi;
49 
50   for (argi = 1; argi < argc; argi++)
51   {
52     if (argv[argi][0] == '-')
53     {
54       switch (argv[argi][1])
55       {
56         case 'n':
57           raw = FALSE;
58           break;
59         case 'r':
60           raw = TRUE;
61           break;
62         case 'a':
63           alpha = TRUE;
64           argi++;
65           if ((fp_al = fopen (argv[argi], "wb")) == NULL)
66           {
67             fprintf (stderr, "PNM2PNG\n");
68             fprintf (stderr, "Error:  cannot create alpha-channel file %s\n",
69                      argv[argi]);
70             exit (1);
71           }
72           break;
73         case 'h':
74         case '?':
75           usage ();
76           exit (0);
77           break;
78         default:
79           fprintf (stderr, "PNG2PNM\n");
80           fprintf (stderr, "Error:  unknown option %s\n", argv[argi]);
81           usage ();
82           exit (1);
83           break;
84       } /* end switch */
85     }
86     else if (fp_rd == stdin)
87     {
88       if ((fp_rd = fopen (argv[argi], "rb")) == NULL)
89       {
90         fprintf (stderr, "PNG2PNM\n");
91         fprintf (stderr, "Error:  file %s does not exist\n", argv[argi]);
92         exit (1);
93       }
94     }
95     else if (fp_wr == stdout)
96     {
97       if ((fp_wr = fopen (argv[argi], "wb")) == NULL)
98       {
99         fprintf (stderr, "PNG2PNM\n");
100         fprintf (stderr, "Error:  cannot create file %s\n", argv[argi]);
101         exit (1);
102       }
103     }
104     else
105     {
106       fprintf (stderr, "PNG2PNM\n");
107       fprintf (stderr, "Error:  too many parameters\n");
108       usage ();
109       exit (1);
110     }
111   } /* end for */
112 
113 #if defined(O_BINARY) && (O_BINARY != 0)
114   /* set stdin/stdout if required to binary */
115   if (fp_rd == stdin)
116     setmode (fileno (stdin), O_BINARY);
117   if ((raw) && (fp_wr == stdout))
118     setmode (fileno (stdout), O_BINARY);
119 #endif
120 
121   /* call the conversion program itself */
122   if (png2pnm (fp_rd, fp_wr, fp_al, raw, alpha) == FALSE)
123   {
124     fprintf (stderr, "PNG2PNM\n");
125     fprintf (stderr, "Error:  unsuccessful conversion of PNG-image\n");
126     exit (1);
127   }
128 
129   /* close input file */
130   fclose (fp_rd);
131   /* close output file */
132   fclose (fp_wr);
133   /* close alpha file */
134   if (alpha)
135     fclose (fp_al);
136 
137   return 0;
138 }
139 
140 /*
141  *  usage
142  */
143 
usage()144 void usage ()
145 {
146   fprintf (stderr, "PNG2PNM\n");
147   fprintf (stderr, "   by Willem van Schaik, 1999\n");
148   fprintf (stderr, "Usage:  png2pnm [options] <file>.png [<file>.pnm]\n");
149   fprintf (stderr, "   or:  ... | png2pnm [options]\n");
150   fprintf (stderr, "Options:\n");
151   fprintf (stderr,
152       "   -r[aw]   write pnm-file in binary format (P4/P5/P6) (default)\n");
153   fprintf (stderr, "   -n[oraw] write pnm-file in ascii format (P1/P2/P3)\n");
154   fprintf (stderr,
155       "   -a[lpha] <file>.pgm write PNG alpha channel as pgm-file\n");
156   fprintf (stderr, "   -h | -?  print this help-information\n");
157 }
158 
159 /*
160  *  png2pnm
161  */
162 
png2pnm(FILE * png_file,FILE * pnm_file,FILE * alpha_file,BOOL raw,BOOL alpha)163 BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
164               BOOL raw, BOOL alpha)
165 {
166   png_struct    *png_ptr = NULL;
167   png_info      *info_ptr = NULL;
168   png_byte      buf[8];
169   png_byte      *png_pixels = NULL;
170   png_byte      **row_pointers = NULL;
171   png_byte      *pix_ptr = NULL;
172   png_uint_32   row_bytes;
173 
174   png_uint_32   width;
175   png_uint_32   height;
176   int           bit_depth;
177   int           channels;
178   int           color_type;
179   int           alpha_present;
180   int           row, col;
181   int           ret;
182   int           i;
183   long          dep_16;
184 
185   /* read and check signature in PNG file */
186   ret = fread (buf, 1, 8, png_file);
187   if (ret != 8)
188     return FALSE;
189 
190   ret = png_sig_cmp (buf, 0, 8);
191   if (ret != 0)
192     return FALSE;
193 
194   /* create png and info structures */
195 
196   png_ptr = png_create_read_struct (png_get_libpng_ver(NULL),
197                                     NULL, NULL, NULL);
198   if (!png_ptr)
199     return FALSE; /* out of memory */
200 
201   info_ptr = png_create_info_struct (png_ptr);
202   if (!info_ptr)
203   {
204     png_destroy_read_struct (&png_ptr, NULL, NULL);
205     return FALSE; /* out of memory */
206   }
207 
208   if (setjmp (png_jmpbuf (png_ptr)))
209   {
210     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
211     return FALSE;
212   }
213 
214   /* set up the input control for C streams */
215   png_init_io (png_ptr, png_file);
216   png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */
217 
218   /* read the file information */
219   png_read_info (png_ptr, info_ptr);
220 
221   /* get size and bit-depth of the PNG-image */
222   png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
223                 NULL, NULL, NULL);
224 
225   /* set-up the transformations */
226 
227   /* transform paletted images into full-color rgb */
228   if (color_type == PNG_COLOR_TYPE_PALETTE)
229     png_set_expand (png_ptr);
230   /* expand images to bit-depth 8 (only applicable for grayscale images) */
231   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
232     png_set_expand (png_ptr);
233   /* transform transparency maps into full alpha-channel */
234   if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
235     png_set_expand (png_ptr);
236 
237 #ifdef NJET
238   /* downgrade 16-bit images to 8-bit */
239   if (bit_depth == 16)
240     png_set_strip_16 (png_ptr);
241   /* transform grayscale images into full-color */
242   if (color_type == PNG_COLOR_TYPE_GRAY ||
243       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
244     png_set_gray_to_rgb (png_ptr);
245   /* only if file has a file gamma, we do a correction */
246   if (png_get_gAMA (png_ptr, info_ptr, &file_gamma))
247     png_set_gamma (png_ptr, (double) 2.2, file_gamma);
248 #endif
249 
250   /* all transformations have been registered; now update info_ptr data,
251    * get rowbytes and channels, and allocate image memory */
252 
253   png_read_update_info (png_ptr, info_ptr);
254 
255   /* get the new color-type and bit-depth (after expansion/stripping) */
256   png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
257                 NULL, NULL, NULL);
258 
259   /* check for 16-bit files */
260   if (bit_depth == 16)
261   {
262     raw = FALSE;
263 #if defined(O_BINARY) && (O_BINARY != 0)
264     setmode (fileno (pnm_file), O_BINARY);
265 #endif
266   }
267 
268   /* calculate new number of channels and store alpha-presence */
269   if (color_type == PNG_COLOR_TYPE_GRAY)
270     channels = 1;
271   else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
272     channels = 2;
273   else if (color_type == PNG_COLOR_TYPE_RGB)
274     channels = 3;
275   else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
276     channels = 4;
277   else
278     channels = 0; /* should never happen */
279   alpha_present = (channels - 1) % 2;
280 
281   /* check if alpha is expected to be present in file */
282   if (alpha && !alpha_present)
283   {
284     fprintf (stderr, "PNG2PNM\n");
285     fprintf (stderr, "Error:  PNG-file doesn't contain alpha channel\n");
286     exit (1);
287   }
288 
289   /* row_bytes is the width x number of channels x (bit-depth / 8) */
290   row_bytes = png_get_rowbytes (png_ptr, info_ptr);
291 
292   if ((row_bytes == 0) ||
293       ((size_t) height > (size_t) (-1) / (size_t) row_bytes))
294   {
295     /* too big */
296     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
297     return FALSE;
298   }
299   if ((png_pixels = (png_byte *)
300        malloc ((size_t) row_bytes * (size_t) height)) == NULL)
301   {
302     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
303     return FALSE;
304   }
305 
306   if ((row_pointers = (png_byte **)
307        malloc ((size_t) height * sizeof (png_byte *))) == NULL)
308   {
309     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
310     free (png_pixels);
311     return FALSE;
312   }
313 
314   /* set the individual row_pointers to point at the correct offsets */
315   for (i = 0; i < ((int) height); i++)
316     row_pointers[i] = png_pixels + i * row_bytes;
317 
318   /* now we can go ahead and just read the whole image */
319   png_read_image (png_ptr, row_pointers);
320 
321   /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
322   png_read_end (png_ptr, info_ptr);
323 
324   /* clean up after the read, and free any memory allocated - REQUIRED */
325   png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
326 
327   /* write header of PNM file */
328 
329   if ((color_type == PNG_COLOR_TYPE_GRAY) ||
330       (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
331   {
332     fprintf (pnm_file, "%s\n", (raw) ? "P5" : "P2");
333     fprintf (pnm_file, "%d %d\n", (int) width, (int) height);
334     fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L));
335   }
336   else if ((color_type == PNG_COLOR_TYPE_RGB) ||
337            (color_type == PNG_COLOR_TYPE_RGB_ALPHA))
338   {
339     fprintf (pnm_file, "%s\n", (raw) ? "P6" : "P3");
340     fprintf (pnm_file, "%d %d\n", (int) width, (int) height);
341     fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L));
342   }
343 
344   /* write header of PGM file with alpha channel */
345 
346   if ((alpha) &&
347       ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
348        (color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
349   {
350     fprintf (alpha_file, "%s\n", (raw) ? "P5" : "P2");
351     fprintf (alpha_file, "%d %d\n", (int) width, (int) height);
352     fprintf (alpha_file, "%ld\n", ((1L << (int) bit_depth) - 1L));
353   }
354 
355   /* write data to PNM file */
356   pix_ptr = png_pixels;
357 
358   for (row = 0; row < (int) height; row++)
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           fputc ((int) *pix_ptr++, pnm_file);
367         }
368         else
369         {
370           if (bit_depth == 16)
371           {
372             dep_16 = (long) *pix_ptr++;
373             fprintf (pnm_file, "%ld ", (dep_16 << 8) + ((long) *pix_ptr++));
374           }
375           else
376           {
377             fprintf (pnm_file, "%ld ", (long) *pix_ptr++);
378           }
379         }
380       }
381       if (alpha_present)
382       {
383         if (!alpha)
384         {
385           pix_ptr++; /* alpha */
386           if (bit_depth == 16)
387             pix_ptr++;
388         }
389         else /* output alpha-channel as pgm file */
390         {
391           if (raw)
392           {
393             fputc ((int) *pix_ptr++, alpha_file);
394           }
395           else
396           {
397             if (bit_depth == 16)
398             {
399               dep_16 = (long) *pix_ptr++;
400               fprintf (alpha_file, "%ld ", (dep_16 << 8) + (long) *pix_ptr++);
401             }
402             else
403             {
404               fprintf (alpha_file, "%ld ", (long) *pix_ptr++);
405             }
406           }
407         }
408       } /* end if alpha_present */
409 
410       if (!raw)
411         if (col % 4 == 3)
412           fprintf (pnm_file, "\n");
413     } /* end for col */
414 
415     if (!raw)
416       if (col % 4 != 0)
417         fprintf (pnm_file, "\n");
418   } /* end for row */
419 
420   if (row_pointers != NULL)
421     free (row_pointers);
422   if (png_pixels != NULL)
423     free (png_pixels);
424 
425   return TRUE;
426 
427 } /* end of source */
428