Blame editor/pnmshear.c

Packit 78deda
 /* pnmshear.c - read a portable anymap and shear it by some angle
Packit 78deda
**
Packit 78deda
** Copyright (C) 1989, 1991 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
#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
#include <math.h>
Packit 78deda
#include <string.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "ppm.h"
Packit 78deda
#include "pnm.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
Packit 78deda
#define SCALE 4096
Packit 78deda
#define HALFSCALE 2048
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct CmdlineInfo {
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;   /* Name of input file */
Packit 78deda
    double       angle;           /* requested shear angle, in radians */
Packit 78deda
    unsigned int noantialias;     /* -noantialias option */
Packit 78deda
    const char * background;      /* NULL if none */
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int argc, const char ** argv,
Packit 78deda
                 struct CmdlineInfo *cmdlineP) {
Packit 78deda
Packit 78deda
    optStruct3 opt;
Packit 78deda
    unsigned int option_def_index = 0;
Packit 78deda
    optEntry * option_def;
Packit 78deda
Packit 78deda
    unsigned int backgroundSpec;
Packit 78deda
Packit 78deda
    MALLOCARRAY(option_def, 100);
Packit 78deda
Packit 78deda
    OPTENT3(0, "noantialias",      OPT_FLAG,  NULL, &cmdlineP->noantialias, 0);
Packit 78deda
    OPTENT3(0, "background",       OPT_STRING, &cmdlineP->background,
Packit 78deda
            &backgroundSpec, 0);
Packit 78deda
    
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = FALSE;
Packit 78deda
    opt.allowNegNum = TRUE;
Packit 78deda
Packit 78deda
    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
Packit 78deda
Packit 78deda
    if (!backgroundSpec)
Packit 78deda
        cmdlineP->background = NULL;
Packit 78deda
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        pm_error("Need an argument:  the shear angle.\n");
Packit 78deda
    else {
Packit 78deda
        char *endptr;
Packit 78deda
        cmdlineP->angle = strtod(argv[1], &endptr) * M_PI / 180;
Packit 78deda
        if (*endptr != '\0' || strlen(argv[1]) == 0)
Packit 78deda
            pm_error("Angle argument is not a valid floating point number: "
Packit 78deda
                     "'%s'", argv[1]);
Packit 78deda
        if (argc-1 < 2)
Packit 78deda
            cmdlineP->inputFileName = "-";
Packit 78deda
        else {
Packit 78deda
            cmdlineP->inputFileName = argv[2];
Packit 78deda
            if (argc-1 > 2)
Packit 78deda
                pm_error("too many arguments (%d).  "
Packit 78deda
                         "The only arguments are shear angle and filespec.",
Packit 78deda
                         argc-1);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    free(option_def);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
makeNewXel(xel *  const outputXelP,
Packit 78deda
           xel    const curXel,
Packit 78deda
           xel    const prevXel,
Packit 78deda
           double const fracnew0,
Packit 78deda
           double const omfracnew0,
Packit 78deda
           int    const format) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Create an output xel as *outputXel, which is part curXel and part
Packit 78deda
   prevXel, the part given by the fractions omfracnew0 and fracnew0,
Packit 78deda
   respectively.  These fraction values are the numerator of a fraction
Packit 78deda
   whose denominator is SCALE.
Packit 78deda
Packit 78deda
   The format of the pixel is 'format'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
Packit 78deda
    switch (PNM_FORMAT_TYPE(format)) {
Packit 78deda
    case PPM_TYPE:
Packit 78deda
        PPM_ASSIGN(*outputXelP,
Packit 78deda
                   (fracnew0 * PPM_GETR(prevXel) 
Packit 78deda
                    + omfracnew0 * PPM_GETR(curXel) 
Packit 78deda
                    + HALFSCALE) / SCALE,
Packit 78deda
                   (fracnew0 * PPM_GETG(prevXel) 
Packit 78deda
                    + omfracnew0 * PPM_GETG(curXel) 
Packit 78deda
                    + HALFSCALE) / SCALE,
Packit 78deda
                   (fracnew0 * PPM_GETB(prevXel) 
Packit 78deda
                    + omfracnew0 * PPM_GETB(curXel) 
Packit 78deda
                    + HALFSCALE) / SCALE );
Packit 78deda
        break;
Packit 78deda
        
Packit 78deda
    default:
Packit 78deda
        PNM_ASSIGN1(*outputXelP,
Packit 78deda
                    (fracnew0 * PNM_GET1(prevXel) 
Packit 78deda
                     + omfracnew0 * PNM_GET1(curXel) 
Packit 78deda
                     + HALFSCALE) / SCALE );
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
shearRow(xel *        const xelrow,
Packit 78deda
         unsigned int const cols, 
Packit 78deda
         xel *        const newxelrow,
Packit 78deda
         unsigned int const newcols, 
Packit 78deda
         double       const shearCols,
Packit 78deda
         int          const format,
Packit 78deda
         xel          const bgxel,
Packit 78deda
         bool         const antialias) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Shear the row 'xelrow' by 'shearCols' columns, and return the result as
Packit 78deda
   'newxelrow'.  They are 'cols' and 'newcols' columns wide, respectively.
Packit 78deda
   
Packit 78deda
   Fill in the part of the output row that doesn't contain image data with
Packit 78deda
   'bgxel'.
Packit 78deda
Packit 78deda
   Use antialiasing iff 'antialias'.
Packit 78deda
Packit 78deda
   The format of the input xels (which implies something about the
Packit 78deda
   output xels too) is 'format'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const intShearCols = (unsigned int) shearCols;
Packit 78deda
Packit 78deda
    assert(shearCols >= 0.0);
Packit 78deda
        
Packit 78deda
    if (antialias) {
Packit 78deda
        const long fracnew0 = (shearCols - intShearCols) * SCALE;
Packit 78deda
        const long omfracnew0 = SCALE - fracnew0;
Packit 78deda
Packit 78deda
        unsigned int col;
Packit 78deda
        xel prevXel;
Packit 78deda
            
Packit 78deda
        for (col = 0; col < newcols; ++col)
Packit 78deda
            newxelrow[col] = bgxel;
Packit 78deda
Packit 78deda
        prevXel = bgxel;
Packit 78deda
        for (col = 0; col < cols; ++col) {
Packit 78deda
            makeNewXel(&newxelrow[intShearCols + col],
Packit 78deda
                       xelrow[col], prevXel, fracnew0, omfracnew0,
Packit 78deda
                       format);
Packit 78deda
            prevXel = xelrow[col];
Packit 78deda
        }
Packit 78deda
        if (fracnew0 > 0) 
Packit 78deda
            /* Need to add a column for what's left over */
Packit 78deda
            makeNewXel(&newxelrow[intShearCols + cols],
Packit 78deda
                       bgxel, prevXel, fracnew0, omfracnew0, format);
Packit 78deda
    } else {
Packit 78deda
        unsigned int col;
Packit 78deda
        for (col = 0; col < intShearCols; ++col)
Packit 78deda
            newxelrow[col] = bgxel;
Packit 78deda
        for (col = 0; col < cols; ++col)
Packit 78deda
            newxelrow[intShearCols+col] = xelrow[col];
Packit 78deda
        for (col = intShearCols + cols; col < newcols; ++col)
Packit 78deda
            newxelrow[col] = bgxel;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
static xel
Packit 78deda
backgroundColor(const char * const backgroundColorName,
Packit 78deda
                xel *        const topRow,
Packit 78deda
                int          const cols,
Packit 78deda
                xelval       const maxval,
Packit 78deda
                int          const format) {
Packit 78deda
Packit 78deda
    xel retval;
Packit 78deda
Packit 78deda
    if (backgroundColorName) {
Packit 78deda
        retval = pnm_parsecolorxel(backgroundColorName, maxval, format);
Packit 78deda
    } else 
Packit 78deda
        retval = pnm_backgroundxelrow(topRow, cols, maxval, format);
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int argc, const char * argv[]) {
Packit 78deda
Packit 78deda
    FILE * ifP;
Packit 78deda
    xel * xelrow;
Packit 78deda
    xel * newxelrow;
Packit 78deda
    xel bgxel;
Packit 78deda
    int rows, cols, format; 
Packit 78deda
    int newformat, newcols; 
Packit 78deda
    int row;
Packit 78deda
    xelval maxval, newmaxval;
Packit 78deda
    double shearfac;
Packit 78deda
Packit 78deda
    struct CmdlineInfo cmdline;
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
Packit 78deda
    xelrow = pnm_allocrow(cols);
Packit 78deda
Packit 78deda
    /* Promote PBM files to PGM. */
Packit 78deda
    if (!cmdline.noantialias && PNM_FORMAT_TYPE(format) == PBM_TYPE) {
Packit 78deda
        newformat = PGM_TYPE;
Packit 78deda
        newmaxval = PGM_MAXMAXVAL;
Packit 78deda
        pm_message("promoting from PBM to PGM - "
Packit 78deda
                   "use -noantialias to avoid this");
Packit 78deda
    } else {
Packit 78deda
        newformat = format;
Packit 78deda
        newmaxval = maxval;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    shearfac = fabs(tan(cmdline.angle));
Packit 78deda
Packit 78deda
    newcols = rows * shearfac + cols + 0.999999;
Packit 78deda
Packit 78deda
    pnm_writepnminit(stdout, newcols, rows, newmaxval, newformat, 0);
Packit 78deda
    newxelrow = pnm_allocrow(newcols);
Packit 78deda
    
Packit 78deda
    for (row = 0; row < rows; ++row) {
Packit 78deda
        double shearCols;
Packit 78deda
Packit 78deda
        pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
Packit 78deda
Packit 78deda
        if (row == 0)
Packit 78deda
            bgxel = backgroundColor(cmdline.background,
Packit 78deda
                                    xelrow, cols, newmaxval, format);
Packit 78deda
Packit 78deda
        if (cmdline.angle > 0.0)
Packit 78deda
            shearCols = row * shearfac;
Packit 78deda
        else
Packit 78deda
            shearCols = (rows - row) * shearfac;
Packit 78deda
Packit 78deda
        shearRow(xelrow, cols, newxelrow, newcols, 
Packit 78deda
                 shearCols, format, bgxel, !cmdline.noantialias);
Packit 78deda
Packit 78deda
        pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0);
Packit 78deda
    }
Packit 78deda
    
Packit 78deda
    pm_close(ifP);
Packit 78deda
    pm_close(stdout);
Packit 78deda
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda