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