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