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