Blame converter/pgm/rawtopgm.c

Packit 78deda
/* rawtopgm.c - convert raw grayscale bytes into a portable graymap
Packit 78deda
**
Packit 78deda
** Copyright (C) 1989 by Jef Poskanzer.
Packit 78deda
**
Packit 78deda
** Permission to use, copy, modify, and distribute this software and its
Packit 78deda
** documentation for any purpose and without fee is hereby granted, provided
Packit 78deda
** that the above copyright notice appear in all copies and that both that
Packit 78deda
** copyright notice and this permission notice appear in supporting
Packit 78deda
** documentation.  This software is provided "as is" without express or
Packit 78deda
** implied warranty.
Packit 78deda
*/
Packit 78deda
Packit 78deda
#include <math.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "pgm.h"
Packit 78deda
Packit 78deda
struct cmdline_info {
Packit 78deda
    /* All the information the user supplied in the command line,
Packit 78deda
       in a form easy for the program to use.
Packit 78deda
    */
Packit 78deda
    const char * inputFileName;
Packit 78deda
    unsigned int headerskip;
Packit 78deda
    float rowskip;
Packit 78deda
    int bottomfirst;  /* the -bottomfirst/-bt option */
Packit 78deda
    int autosize;  /* User wants us to figure out the size */
Packit 78deda
    unsigned int width;
Packit 78deda
    unsigned int height;
Packit 78deda
    int bpp;
Packit 78deda
      /* bytes per pixel in input format.  1 or 2 */
Packit 78deda
    int littleendian;
Packit 78deda
      /* logical: samples in input are least significant byte first */
Packit 78deda
    int maxval;  /* -maxval option, or -1 if none */
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parse_command_line(int argc, char ** argv,
Packit 78deda
                   struct cmdline_info *cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Note that the file spec array we return is stored in the storage that
Packit 78deda
   was passed to us as the argv array.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    optEntry * option_def;
Packit 78deda
        /* Instructions to OptParseOptions3 on how to parse our options.
Packit 78deda
         */
Packit 78deda
    optStruct3 opt;
Packit 78deda
Packit 78deda
    unsigned int option_def_index;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(option_def, 100);
Packit 78deda
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENT3 */
Packit 78deda
    OPTENT3(0,   "bottomfirst",   OPT_FLAG,   &cmdlineP->bottomfirst,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "bt",            OPT_FLAG,   &cmdlineP->bottomfirst,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "topbottom",     OPT_FLAG,   &cmdlineP->bottomfirst,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "tb",            OPT_FLAG,   &cmdlineP->bottomfirst,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "headerskip",    OPT_UINT,   &cmdlineP->headerskip,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "rowskip",       OPT_FLOAT,  &cmdlineP->rowskip,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "bpp",           OPT_INT,    &cmdlineP->bpp,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "littleendian",  OPT_FLAG,   &cmdlineP->littleendian,
Packit 78deda
            NULL,   0);
Packit 78deda
    OPTENT3(0,   "maxval",        OPT_UINT,   &cmdlineP->maxval,
Packit 78deda
            NULL,   0);
Packit 78deda
Packit 78deda
    /* Set the defaults */
Packit 78deda
    cmdlineP->bottomfirst = FALSE;
Packit 78deda
    cmdlineP->headerskip = 0;
Packit 78deda
    cmdlineP->rowskip = 0.0;
Packit 78deda
    cmdlineP->bpp = 1;
Packit 78deda
    cmdlineP->littleendian = 0;
Packit 78deda
    cmdlineP->maxval = -1;
Packit 78deda
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
Packit 78deda
    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
Packit 78deda
Packit 78deda
    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
Packit 78deda
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
Packit 78deda
Packit 78deda
    if (argc-1 == 0) {
Packit 78deda
        cmdlineP->inputFileName = "-";
Packit 78deda
        cmdlineP->autosize = TRUE;
Packit 78deda
    } else if (argc-1 == 1) {
Packit 78deda
        cmdlineP->inputFileName = argv[1];
Packit 78deda
        cmdlineP->autosize = TRUE;
Packit 78deda
    } else if (argc-1 == 2) {
Packit 78deda
        cmdlineP->inputFileName = "-";
Packit 78deda
        cmdlineP->autosize = FALSE;
Packit 78deda
        cmdlineP->width = pm_parse_width(argv[1]);
Packit 78deda
        cmdlineP->height = pm_parse_height(argv[2]);
Packit 78deda
    } else if (argc-1 == 3) {
Packit 78deda
        cmdlineP->inputFileName = argv[3];
Packit 78deda
        cmdlineP->autosize = FALSE;
Packit 78deda
        cmdlineP->width = pm_parse_width(argv[1]);
Packit 78deda
        cmdlineP->height = pm_parse_height(argv[2]);
Packit 78deda
    } else
Packit 78deda
        pm_error("Program takes zero, one, two, or three arguments.  You "
Packit 78deda
                 "specified %d", argc-1);
Packit 78deda
Packit 78deda
    if (cmdlineP->bpp != 1 && cmdlineP->bpp != 2) 
Packit 78deda
        pm_error("Bytes per pixel (-bpp) must be 1 or 2.  You specified %d.",
Packit 78deda
                 cmdlineP->bpp);
Packit 78deda
Packit 78deda
    if (cmdlineP->maxval == 0)
Packit 78deda
        pm_error("Maxval (-maxval) may not be zero.");
Packit 78deda
Packit 78deda
    if (cmdlineP->maxval > 255 && cmdlineP->bpp == 1)
Packit 78deda
        pm_error("You have specified one byte per pixel, but a maxval "
Packit 78deda
                 "too large to fit in one byte: %d", cmdlineP->maxval);
Packit 78deda
    if (cmdlineP->maxval > 65535)
Packit 78deda
        pm_error("Maxval must be less than 65536.  You specified %d.",
Packit 78deda
                 cmdlineP->maxval);
Packit 78deda
Packit 78deda
    if (cmdlineP->rowskip && cmdlineP->autosize)
Packit 78deda
        pm_error("If you specify -rowskip, you must also give the image "
Packit 78deda
                 "dimensions.");
Packit 78deda
    if (cmdlineP->rowskip && cmdlineP->bottomfirst)
Packit 78deda
        pm_error("You canot specify both -rowskip and -bottomfirst.  This is "
Packit 78deda
                 "a limitation of this program.");
Packit 78deda
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
compute_image_size(const struct cmdline_info cmdline, const long nread,
Packit 78deda
                   int * const rows_p, int * const cols_p) {
Packit 78deda
Packit 78deda
    if (cmdline.autosize) {
Packit 78deda
        int sqrt_trunc = 
Packit 78deda
            (int) sqrt((double) (nread-cmdline.headerskip));
Packit 78deda
        if (sqrt_trunc*sqrt_trunc+cmdline.headerskip != nread) 
Packit 78deda
            pm_error( "You must specify the dimensions of the image unless "
Packit 78deda
                      "it is a quadratic image.  This one is not quadratic: "
Packit 78deda
                      "The number of "
Packit 78deda
                      "pixels in the input is %ld, which is not a perfect "
Packit 78deda
                      "square.", nread-cmdline.headerskip);
Packit 78deda
        *rows_p = *cols_p = sqrt_trunc;
Packit 78deda
        pm_message( "Image size: %d cols, %d rows", *cols_p, *rows_p);
Packit 78deda
    } else {
Packit 78deda
        *rows_p = cmdline.height;
Packit 78deda
        *cols_p = cmdline.width;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
skip_header(FILE *ifp, const int headerskip) {
Packit 78deda
    int i;
Packit 78deda
Packit 78deda
    for ( i = 0; i < headerskip; ++i ) {
Packit 78deda
        /* Read a byte out of the file */
Packit 78deda
        int val;
Packit 78deda
        val = getc( ifp );
Packit 78deda
        if ( val == EOF )
Packit 78deda
            pm_error("EOF / read error reading Byte %d in the header", i );
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static gray
Packit 78deda
read_from_file(FILE *ifp, const int bpp, const int row, const int col,
Packit 78deda
               const int littleendian) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Return the next sample value from the input file 'ifp', assuming the
Packit 78deda
   input stream is 'bpp' bytes per pixel (1 or 2).  In the case of two
Packit 78deda
   bytes, if 'littleendian', assume least significant byte is first.
Packit 78deda
   Otherwise, assume MSB first.
Packit 78deda
   
Packit 78deda
   In error messages, say this is Column 'col', Row 'row'.  Exit program if
Packit 78deda
   error.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    gray retval;
Packit 78deda
Packit 78deda
    if (bpp == 1) {
Packit 78deda
        int val;
Packit 78deda
        val = getc(ifp);
Packit 78deda
        if (val == EOF)
Packit 78deda
            pm_error( "EOF / read error at Row %d Column %d",
Packit 78deda
                      row, col);
Packit 78deda
        retval = (gray) val;
Packit 78deda
    } else {
Packit 78deda
        short val;
Packit 78deda
        int rc;
Packit 78deda
        rc = littleendian ? 
Packit 78deda
            pm_readlittleshort(ifp, &val) : pm_readbigshort(ifp, &val;;
Packit 78deda
        if (rc != 0)
Packit 78deda
            pm_error( "EOF / read error at Row %d Column %d",
Packit 78deda
                      row, col);
Packit 78deda
        retval = (gray) val;
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int argc, char *argv[] ) {
Packit 78deda
Packit 78deda
    struct cmdline_info cmdline;
Packit 78deda
    FILE* ifp;
Packit 78deda
    gray* grayrow;
Packit 78deda
    int rows, cols;
Packit 78deda
    gray maxval;
Packit 78deda
    char* buf;
Packit 78deda
    /* pixels_1 and pixels_2 are the array of pixels in the input buffer
Packit 78deda
       (assuming we are using an input buffer).  pixels_1 is the array
Packit 78deda
       as if the pixels are one byte each.  pixels_2 is the array as if
Packit 78deda
       they are two bytes each.
Packit 78deda
       */
Packit 78deda
    unsigned char *pixels_1;  
Packit 78deda
    unsigned short *pixels_2;
Packit 78deda
    long nread;
Packit 78deda
    int row;
Packit 78deda
    float toskip;
Packit 78deda
Packit 78deda
    pgm_init( &argc, argv );
Packit 78deda
Packit 78deda
    parse_command_line(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifp = pm_openr(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    if (cmdline.autosize || cmdline.bottomfirst) {
Packit 78deda
        buf = pm_read_unknown_size( ifp, &nread );
Packit 78deda
        pixels_1 = (unsigned char *) buf;
Packit 78deda
        pixels_2 = (unsigned short *) buf;
Packit 78deda
    } else
Packit 78deda
        buf = NULL;
Packit 78deda
Packit 78deda
    compute_image_size(cmdline, nread, &rows, &cols;;
Packit 78deda
Packit 78deda
    if (!buf)
Packit 78deda
        skip_header(ifp, cmdline.headerskip);
Packit 78deda
Packit 78deda
    toskip = 0.00001;
Packit 78deda
Packit 78deda
    if (cmdline.maxval == -1)
Packit 78deda
        maxval = (cmdline.bpp == 1 ? (gray) 255 : (gray) 65535);
Packit 78deda
    else
Packit 78deda
        maxval = (gray) cmdline.maxval;
Packit 78deda
Packit 78deda
    pgm_writepgminit( stdout, cols, rows, maxval, 0 );
Packit 78deda
    grayrow = pgm_allocrow( cols );
Packit 78deda
Packit 78deda
    for ( row = 0; row < rows; ++row) {
Packit 78deda
        int col;
Packit 78deda
        unsigned int rowpos; /* index of this row in pixel array */
Packit 78deda
        if (cmdline.bottomfirst)
Packit 78deda
            rowpos = (rows-row-1) * cols;
Packit 78deda
        else
Packit 78deda
            rowpos = row * cols;
Packit 78deda
Packit 78deda
        for ( col = 0; col < cols; ++col )
Packit 78deda
            if (buf) {
Packit 78deda
                if (cmdline.bpp == 1)
Packit 78deda
                    grayrow[col] = pixels_1[rowpos+col];
Packit 78deda
                else
Packit 78deda
                    grayrow[col] = pixels_2[rowpos+col];
Packit 78deda
            } else {
Packit 78deda
                grayrow[col] = read_from_file(ifp, cmdline.bpp, 
Packit 78deda
                                              row, col,
Packit 78deda
                                              cmdline.littleendian);
Packit 78deda
            }
Packit 78deda
        for ( toskip += cmdline.rowskip; toskip >= 1.0; toskip -= 1.0 ) {
Packit 78deda
            /* Note that if we're using a buffer, cmdline.rowskip is zero */
Packit 78deda
            int val;
Packit 78deda
            val = getc( ifp );
Packit 78deda
            if ( val == EOF )
Packit 78deda
                pm_error( "EOF / read error skipping bytes at the end "
Packit 78deda
                          "of Row %d.", row);
Packit 78deda
        }
Packit 78deda
        pgm_writepgmrow( stdout, grayrow, cols, maxval, 0 );
Packit 78deda
    }
Packit 78deda
    
Packit 78deda
    if (buf)
Packit 78deda
        free(buf);
Packit 78deda
    pm_close( ifp );
Packit 78deda
    pm_close( stdout );
Packit 78deda
Packit 78deda
    exit( 0 );
Packit 78deda
}