|
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 |
|