diff options
Diffstat (limited to 'editor/pnmcut.c')
-rw-r--r-- | editor/pnmcut.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/editor/pnmcut.c b/editor/pnmcut.c new file mode 100644 index 00000000..a21fcffb --- /dev/null +++ b/editor/pnmcut.c @@ -0,0 +1,427 @@ + /* pnmcut.c - cut a rectangle out of a portable anymap +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <limits.h> +#include "pnm.h" +#include "shhopt.h" + +#define UNSPEC INT_MAX + /* UNSPEC is the value we use for an argument that is not specified + by the user. Theoretically, the user could specify this value, + but we hope not. + */ + +struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filespec; /* Filespecs of input files */ + + /* The following describe the rectangle the user wants to cut out. + the value UNSPEC for any of them indicates that value was not + specified. A negative value means relative to the far edge. + 'width' and 'height' are not negative. These specifications + do not necessarily describe a valid rectangle; they are just + what the user said. + */ + int left; + int right; + int top; + int bottom; + int width; + int height; + int pad; + + int verbose; +}; + + + +static xel black_xel; /* A black xel */ + + +static void +parse_command_line(int argc, char ** argv, + struct cmdline_info *cmdline_p) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct *option_def = malloc(100*sizeof(optStruct)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct2 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENTRY(0, "left", OPT_INT, &cmdline_p->left, 0); + OPTENTRY(0, "right", OPT_INT, &cmdline_p->right, 0); + OPTENTRY(0, "top", OPT_INT, &cmdline_p->top, 0); + OPTENTRY(0, "bottom", OPT_INT, &cmdline_p->bottom, 0); + OPTENTRY(0, "width", OPT_INT, &cmdline_p->width, 0); + OPTENTRY(0, "height", OPT_INT, &cmdline_p->height, 0); + OPTENTRY(0, "pad", OPT_FLAG, &cmdline_p->pad, 0); + OPTENTRY(0, "verbose", OPT_FLAG, &cmdline_p->verbose, 0); + + /* Set the defaults */ + cmdline_p->left = UNSPEC; + cmdline_p->right = UNSPEC; + cmdline_p->top = UNSPEC; + cmdline_p->bottom = UNSPEC; + cmdline_p->width = UNSPEC; + cmdline_p->height = UNSPEC; + cmdline_p->pad = FALSE; + cmdline_p->verbose = FALSE; + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = TRUE; /* We may have parms that are negative numbers */ + + optParseOptions2(&argc, argv, opt, 0); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (cmdline_p->width < 0) + pm_error("-width may not be negative."); + if (cmdline_p->height < 0) + pm_error("-height may not be negative."); + + if ((argc-1) != 0 && (argc-1) != 1 && (argc-1) != 4 && (argc-1) != 5) + pm_error("Wrong number of arguments. " + "Must be 0, 1, 4, or 5 arguments."); + + switch (argc-1) { + case 0: + cmdline_p->input_filespec = "-"; + break; + case 1: + cmdline_p->input_filespec = argv[1]; + break; + case 4: + case 5: { + int warg, harg; /* The "width" and "height" command line arguments */ + + if (sscanf(argv[1], "%d", &cmdline_p->left) != 1) + pm_error("Invalid number for left column argument"); + if (sscanf(argv[2], "%d", &cmdline_p->top) != 1) + pm_error("Invalid number for top row argument"); + if (sscanf(argv[3], "%d", &warg) != 1) + pm_error("Invalid number for width argument"); + if (sscanf(argv[4], "%d", &harg) != 1) + pm_error("Invalid number for height argument"); + + if (warg > 0) { + cmdline_p->width = warg; + cmdline_p->right = UNSPEC; + } else { + cmdline_p->width = UNSPEC; + cmdline_p->right = warg -1; + } + if (harg > 0) { + cmdline_p->height = harg; + cmdline_p->bottom = UNSPEC; + } else { + cmdline_p->height = UNSPEC; + cmdline_p->bottom = harg - 1; + } + + if (argc-1 == 4) + cmdline_p->input_filespec = "-"; + else + cmdline_p->input_filespec = argv[5]; + break; + } + } +} + + + +static void +compute_cut_bounds(const int cols, const int rows, + const int leftarg, const int rightarg, + const int toparg, const int bottomarg, + const int widtharg, const int heightarg, + int * const leftcol_p, int * const rightcol_p, + int * const toprow_p, int * const bottomrow_p) { +/*---------------------------------------------------------------------------- + From the values given on the command line 'leftarg', 'rightarg', + 'toparg', 'bottomarg', 'widtharg', and 'heightarg', determine what + rectangle the user wants cut out. + + Any of these arguments may be UNSPEC to indicate "not specified". + Any except 'widtharg' and 'heightarg' may be negative to indicate + relative to the far edge. 'widtharg' and 'heightarg' are positive. + + Return the location of the rectangle as *leftcol_p, *rightcol_p, + *toprow_p, and *bottomrow_p. +-----------------------------------------------------------------------------*/ + + int leftcol, rightcol, toprow, bottomrow; + /* The left and right column numbers and top and bottom row numbers + specified by the user, except with negative values translated + into the actual values. + + Note that these may very well be negative themselves, such + as when the user says "column -10" and there are only 5 columns + in the image. + */ + + /* Translate negative column and row into real column and row */ + /* Exploit the fact that UNSPEC is a positive number */ + + if (leftarg >= 0) + leftcol = leftarg; + else + leftcol = cols + leftarg; + if (rightarg >= 0) + rightcol = rightarg; + else + rightcol = cols + rightarg; + if (toparg >= 0) + toprow = toparg; + else + toprow = rows + toparg; + if (bottomarg >= 0) + bottomrow = bottomarg; + else + bottomrow = rows + bottomarg; + + /* Sort out left, right, and width specifications */ + + if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) { + *leftcol_p = 0; + *rightcol_p = cols - 1; + } + if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) { + *leftcol_p = 0; + *rightcol_p = 0 + widtharg - 1; + } + if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) { + *leftcol_p = 0; + *rightcol_p = rightcol; + } + if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) { + *leftcol_p = rightcol - widtharg + 1; + *rightcol_p = rightcol; + } + if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) { + *leftcol_p = leftcol; + *rightcol_p = cols - 1; + } + if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) { + *leftcol_p = leftcol; + *rightcol_p = leftcol + widtharg - 1; + } + if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) { + *leftcol_p = leftcol; + *rightcol_p = rightcol; + } + if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) { + pm_error("You may not specify left, right, and width.\n" + "Choose at most two of these."); + } + + + /* Sort out top, bottom, and height specifications */ + + if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) { + *toprow_p = 0; + *bottomrow_p = rows - 1; + } + if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) { + *toprow_p = 0; + *bottomrow_p = 0 + heightarg - 1; + } + if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) { + *toprow_p = 0; + *bottomrow_p = bottomrow; + } + if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) { + *toprow_p = bottomrow - heightarg + 1; + *bottomrow_p = bottomrow; + } + if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) { + *toprow_p = toprow; + *bottomrow_p = rows - 1; + } + if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) { + *toprow_p = toprow; + *bottomrow_p = toprow + heightarg - 1; + } + if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) { + *toprow_p = toprow; + *bottomrow_p = bottomrow; + } + if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) { + pm_error("You may not specify top, bottom, and height.\n" + "Choose at most two of these."); + } + +} + + + +static void +reject_out_of_bounds(const int cols, const int rows, + const int leftcol, const int rightcol, + const int toprow, const int bottomrow) { + + /* Reject coordinates off the edge */ + + if (leftcol < 0) + pm_error("You have specified a left edge (%d) that is beyond\n" + "the left edge of the image (0)", leftcol); + if (rightcol > cols-1) + pm_error("You have specified a right edge (%d) that is beyond\n" + "the right edge of the image (%d)", rightcol, cols-1); + if (rightcol < 0) + pm_error("You have specified a right edge (%d) that is beyond\n" + "the left edge of the image (0)", rightcol); + if (rightcol > cols-1) + pm_error("You have specified a right edge (%d) that is beyond\n" + "the right edge of the image (%d)", rightcol, cols-1); + if (leftcol > rightcol) + pm_error("You have specified a left edge (%d) that is to the right\n" + "of the right edge you specified (%d)", + leftcol, rightcol); + + if (toprow < 0) + pm_error("You have specified a top edge (%d) that is above the top " + "edge of the image (0)", toprow); + if (bottomrow > rows-1) + pm_error("You have specified a bottom edge (%d) that is below the\n" + "bottom edge of the image (%d)", bottomrow, rows-1); + if (bottomrow < 0) + pm_error("You have specified a bottom edge (%d) that is above the\n" + "top edge of the image (0)", bottomrow); + if (bottomrow > rows-1) + pm_error("You have specified a bottom edge (%d) that is below the\n" + "bottom edge of the image (%d)", bottomrow, rows-1); + if (toprow > bottomrow) + pm_error("You have specified a top edge (%d) that is below\n" + "the bottom edge you specified (%d)", + toprow, bottomrow); +} + + + +static void +write_black_rows(FILE *outfile, const int rows, const int cols, + xel * const output_row, + const pixval maxval, const int format) { +/*---------------------------------------------------------------------------- + Write out to file 'outfile' 'rows' rows of 'cols' black xels each, + part of an image of format 'format' with maxval 'maxval'. + + Use *output_row as a buffer. It is at least 'cols' xels wide. +-----------------------------------------------------------------------------*/ + int row; + for (row = 0; row < rows; row++) { + int col; + for (col = 0; col < cols; col++) output_row[col] = black_xel; + pnm_writepnmrow(outfile, output_row, cols, maxval, format, 0); + } +} + + + +int +main(int argc, char *argv[]) { + + FILE* ifp; + xel* xelrow; /* Row from input image */ + xel* output_row; /* Row of output image */ + xelval maxval; + int rows, cols, format, row; + int leftcol, rightcol, toprow, bottomrow; + int output_cols; /* Width of output image */ + struct cmdline_info cmdline; + + pnm_init( &argc, argv ); + + parse_command_line(argc, argv, &cmdline); + + ifp = pm_openr(cmdline.input_filespec); + + pnm_readpnminit(ifp, &cols, &rows, &maxval, &format); + xelrow = pnm_allocrow(cols); + + black_xel = pnm_blackxel(maxval, format); + + compute_cut_bounds(cols, rows, + cmdline.left, cmdline.right, + cmdline.top, cmdline.bottom, + cmdline.width, cmdline.height, + &leftcol, &rightcol, &toprow, &bottomrow); + + if (!cmdline.pad) + reject_out_of_bounds(cols, rows, leftcol, rightcol, toprow, bottomrow); + + if (cmdline.verbose) { + pm_message("Image goes from Row 0, Column 0 through Row %d, Column %d", + rows-1, cols-1); + pm_message("Cutting from Row %d, Column %d through Row %d Column %d", + toprow, leftcol, bottomrow, rightcol); + } + + output_cols = rightcol-leftcol+1; + output_row = pnm_allocrow(output_cols); + + pnm_writepnminit(stdout, output_cols, bottomrow-toprow+1, + maxval, format, 0 ); + + /* Implementation note: If speed is ever an issue, we can probably + speed up significantly the non-padding case by writing a special + case loop here for the case cmdline.pad == FALSE. + */ + + /* Write out top padding */ + write_black_rows(stdout, 0 - toprow, output_cols, output_row, + maxval, format); + + /* Read input and write out rows extracted from it */ + for (row = 0; row < rows; row++) { + pnm_readpnmrow(ifp, xelrow, cols, maxval, format); + if (row >= toprow && row <= bottomrow) { + int col; + /* Put in left padding */ + for (col = leftcol; col < 0; col++) { + output_row[col-leftcol] = black_xel; + } + /* Put in extracted columns */ + for (col = MAX(leftcol, 0); col <= MIN(rightcol, cols-1); col++) { + output_row[col-leftcol] = xelrow[col]; + } + /* Put in right padding */ + for (col = MAX(cols, leftcol); col <= rightcol; col++) { + output_row[col-leftcol] = black_xel; + } + pnm_writepnmrow(stdout, output_row, output_cols, + maxval, format, 0); + } + } + /* Note that we may be tempted just to quit after reaching the bottom + of the extracted image, but that would cause a broken pipe problem + for the process that's feeding us the image. + */ + /* Write out bottom padding */ + write_black_rows(stdout, bottomrow - (rows-1), output_cols, output_row, + maxval, format); + + pnm_freerow(output_row); + pnm_freerow(xelrow); + pm_close(ifp); + pm_close(stdout); + + exit( 0 ); +} + |