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