From 1fd361a1ea06e44286c213ca1f814f49306fdc43 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 19 Aug 2006 03:12:28 +0000 Subject: Create Subversion repository git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- editor/pamperspective.c | 1331 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1331 insertions(+) create mode 100644 editor/pamperspective.c (limited to 'editor/pamperspective.c') diff --git a/editor/pamperspective.c b/editor/pamperspective.c new file mode 100644 index 00000000..fdf446c7 --- /dev/null +++ b/editor/pamperspective.c @@ -0,0 +1,1331 @@ +/* + pamperspective -- a reverse scanline renderer + + Copyright (C) 2004 by Mark Weyer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define _BSD_SOURCE /* Make sure strdup is int string.h */ + +#include +#include + +#include "pam.h" +#include "shhopt.h" +#include "mallocvar.h" + +typedef double number; + +/* There was no reason for exactly this value of eps. + For compatibility it should only be decreased in future versions. +*/ + +#define eps 0.0001 + + +/* Multiple choice types for the command line */ + +typedef enum {image, pixel_u} unit; +const char *const unit_token[3] = {"image", "pixel", NULL}; + +typedef enum {lattice, pixel_s} coord_system; +const char *const system_token[3] = {"lattice", "pixel", NULL}; + +typedef enum {nearest, linear} interpolation; +const char *const interpolation_token[3] = {"nearest", "linear", NULL}; + +typedef enum {free_, fixed} proportion; +const char *const proportion_token[3] = {"free", "fixed", NULL}; + +const char *const bool_token[7] = {"yes", "true", "on", + "no", "false", "off", NULL}; +#define first_false_bool_token 3 + +/* All command line options that have float (actually number) values. + We use our own parsing technique for these, to handle width/height + ratios like 4/3 +*/ + +#define num_float_options 15 +const char *const float_option_name[num_float_options][3] = { + {"upper left x", "upper_left_x", "ulx"}, + {"upper left y", "upper_left_y", "uly"}, + {"upper right x", "upper_right_x", "urx"}, + {"upper right y", "upper_right_y", "ury"}, + {"lower left x", "lower_left_x", "llx"}, + {"lower left y", "lower_left_y", "lly"}, + {"lower right x", "lower_right_x", "lrx"}, + {"lower right y", "lower_right_y", "lry"}, + {NULL, "detail", NULL}, + {NULL, "ratio", NULL}, + {NULL, "margin", NULL}, + {NULL, "top_margin", "tmargin"}, + {NULL, "bottom_margin", "bmargin"}, + {NULL, "left_margin", "lmargin"}, + {NULL, "right_margin", "rmargin"} +}; + +/* All command line options that have multiple choice values (except bools). */ + +#define num_enum_options 5 +const char *const enum_option_name[num_enum_options] = { + "input_system", "output_system", "input_unit", "interpolation", "proportion" +}; +const char *const *const enum_option_type[num_enum_options] = { + system_token, system_token, unit_token, interpolation_token, proportion_token +}; + +/* All command line options that have bool values */ + +#define num_bool_options 1 +const char *const bool_option_name[num_bool_options] = { + "frame_include" +}; + + +/* A linked list node for --include points */ + +typedef struct include_point_tag { + + /* How the point is given on the command line (for error messages) */ + + const char* specification; + + /* coordinates */ + + number xi,yi; + + /* link */ + + struct include_point_tag* next; + +} include_point; + + +/* The collection of command line options */ + +typedef struct { + + /* float options */ + + number floats[num_float_options]; + + /* enum options */ + + int enums[num_enum_options]; + + /* bool options */ + + bool bools[num_bool_options]; + + /* flags */ + + unsigned int width_spec, height_spec, + top_margin_spec, left_margin_spec, right_margin_spec, bottom_margin_spec; + + /* Other stuff */ + + int width, height; + const char* infilename; + include_point* include_points; + +} option; + + +/* The collection of properties that correspond to the four specified + vertices +*/ + +typedef struct { + + /* 2d (image) coordinates of the 4 vertices */ + + number xi_ul, yi_ul, xi_ur, yi_ur, xi_ll, yi_ll, xi_lr, yi_lr; + + /* 3d (world) coordinates of the 4 vertices */ + + number xw_ul, yw_ul, zw_ul, xw_ur, yw_ur, zw_ur, + xw_ll, yw_ll, zw_ll, xw_lr, yw_lr, zw_lr; + + /* Originally I planned to include the possibility to move the + centre of projection, that is the pixel the camera "looks at". It + turned out, maybe surprisingly, that this does not have any + effect. So now this centre is moved to (0,0). + + Another original plan was to correct the output parameters + depending on the lengths of the paralellograms sides or its + angles. This is, however, not possible without knowing something + like the camera angle or focal length (in pixels). + */ + + /* The coefficients for the map from output to world coordinates. + + The actual mapping is + u,v -> (ax+bx*u+cx*v, ay+by*u+cy*v, az+bz*u+cz*v) + */ + number ax,bx,cx, ay,by,cy, az,bz,cz; + +} world_data; + + +/* + Internal infile buffer + + This is a cyclic in random access out buffer, just large enough + to store all input lines that are still in use. +*/ + +typedef struct { + + int num_rows, last_physical, last_logical; + tuple** rows; + const struct pam* inpam; + +} buffer; + + + +/* + The following are like MALLOCARRAY_NOFAIL and MALLOCVAR_NOFAIL, + but issue an error message instead of aborting. +*/ + +#define MALLOCARRAY_SAFE(handle,length) \ +{ \ + MALLOCARRAY(handle,length); \ + if (handle==NULL) \ + pm_error ("Out of memory."); \ +} + +#define MALLOCVAR_SAFE(handle) \ +{ \ + MALLOCVAR(handle); \ + if (handle==NULL) \ + pm_error ("Out of memory."); \ +} + + + +static void set_command_line_defaults (option *const options) +{ + options->infilename = "-"; + options->include_points = NULL; + options->floats[8] = 1.0; /* --detail */ + options->floats[9] = 1.0; /* --ratio */ + options->floats[10] = 0.0; /* --margin */ + options->floats[11] = 0.0; /* --top_margin */ + options->floats[12] = 0.0; /* --bottom_margin */ + options->floats[13] = 0.0; /* --left_margin */ + options->floats[14] = 0.0; /* --right_margin */ + options->enums[0] = lattice; /* --input_system */ + options->enums[1] = lattice; /* --output_system */ + options->enums[2] = pixel_u; /* --input_unit */ + options->enums[3] = nearest; /* --interpolation */ + options->enums[4] = free_; /* --proportion */ + options->bools[0] = TRUE; /* --frame_include */ +} + + + +static int parse_enum (const char *const text, + const char *const *const tokens, const char *const name) +/*---------------------------------------------------------------------------- + Parse an argument given to a multiple choice command line option +-----------------------------------------------------------------------------*/ +{ + bool found; + const char *const * cur_token; + char* tokenlist; + int tokenlistlen; + int value; + int num_spaces; + int i; + + /* We find out, whether ^text occurs in ^tokens */ + + found = FALSE; + value = 0; + while (tokens[value] && !found) { + if (strcmp (text, tokens[value])) + value++; + else + found = TRUE; + }; + + /* otherwise issue an error */ + + if (!found) { + /* For the error message we want to list the allowed tokens. + First we have to determine, how much memory we need for that. + */ + num_spaces = 2; + + tokenlistlen = 0; + cur_token = tokens; + while (*cur_token) { + tokenlistlen += (strlen(*cur_token) + num_spaces); + cur_token++; + }; + /* Then we create that list */ + MALLOCARRAY_SAFE(tokenlist, tokenlistlen); + *tokenlist = 0; + cur_token = tokens; + while (*cur_token) { + for (i=0; ispecification = specification; + new_point->next = *include_pointsP; + *include_pointsP = new_point; + + /* Now we parse the specification */ + + for (comma_seek = specification; (*comma_seek != ',') && (*comma_seek != 0); + comma_seek++); + if (*comma_seek == 0) + pm_error ("Invalid format for --include point: '%s'", specification); + *comma_seek = 0; /* separate the two parts for parsing purposes */ + new_point->xi = (number) parse_float(specification); + new_point->yi = (number) parse_float(comma_seek+1); + *comma_seek = ','; +} + + +static void parse_include_points(const char * const include_opt, + include_point ** const include_pointsP) +/*---------------------------------------------------------------------------- + Process the --include option value include_opt by making a linked list + of the points it describes (in reverse order). + + Return a pointer to the first element of that linked list as + *include_pointsP. +----------------------------------------------------------------------------*/ +{ + char * cursor; + char * optWork; + /* Same as include_opt, except we replace delimiters with nulls + as we work. + */ + + optWork = strdup(include_opt); + if (optWork == NULL) + pm_error("out of memory"); + + cursor = &optWork[0]; + while (*cursor != '\0') { + bool hit_end; + char * sem_seek; + + for (sem_seek = cursor; + (*sem_seek != ';') && (*sem_seek != 0); + sem_seek++); + + hit_end = (*sem_seek == '\0'); + + *sem_seek = '\0'; + parse_include_point(cursor, include_pointsP); + + if (hit_end) + cursor = sem_seek; + else + cursor = sem_seek+1; + } + free(optWork); +} + + +static void parse_command_line (int argc, char* argv[], option *const options) +{ + char* float_text[num_float_options]; + unsigned int float_spec[num_float_options]; + char* enum_text[num_enum_options]; + unsigned int enum_spec[num_enum_options]; + char* bool_text[num_bool_options]; + unsigned int bool_spec[num_bool_options]; + char * include_opt; + unsigned int include_spec; + int i,j; + optStruct3 opt; + unsigned int option_def_index; + optEntry* option_def; + + /* Let shhopt try its best */ + + option_def_index = 0; + MALLOCARRAY_SAFE(option_def, + (2*num_float_options + num_enum_options + num_bool_options + 3)); + for (i=0; iwidth), &(options->width_spec), 0); + OPTENT3(0, "height", OPT_INT, &(options->height), &(options->height_spec), + 0); + OPTENT3(0, "include", OPT_STRING, &include_opt, &include_spec, 0); + opt.opt_table = option_def; + opt.short_allowed = FALSE; + opt.allowNegNum = TRUE; + optParseOptions3 (&argc, argv, opt, sizeof(opt), 0); + + /* The non-option arguments are optionally all eight coordinates + and optionally the input filename + */ + + switch (argc-1) { + case 1: + options->infilename = argv[1]; + case 0: + for (i=0; i<8; i++) + if (!float_spec[i]) + pm_error ("The %s-coordinate was not specified", + float_option_name[i][0]); + break; + case 9: + options->infilename = argv[9]; + case 8: + for (i=0; i<8; i++) { + float_text[i] = argv[i+1]; + float_spec[i] = 1; + }; + break; + default: pm_error ("Wrong (number of) command line arguments"); + }; + + if (include_spec) + parse_include_points(include_opt, &options->include_points); + + /* Parse float options -- shhopt retrieved them as strings */ + + for (i=0; ifloats[i] = parse_float (float_text[i]); + + /* Parse enum options -- shhopt retrieved them as strings */ + + for (i=0; ienums[i] = parse_enum (enum_text[i],enum_option_type[i], + enum_option_name[i]); + + /* Parse bool options -- shhopt retrieved them as strings */ + + for (i=0; ibools[i] = (first_false_bool_token > + parse_enum (bool_text[i], bool_token, + bool_option_name[i])); + + /* Propagate values where neccessary */ + + if (float_spec[10]) /* --margin */ + for (i=11; i<15; i++) /* --top_margin through --right_margin */ + if (!(float_spec[i])) { + options->floats[i] = options->floats[10]; + float_spec[i]=1; + }; + options->top_margin_spec = float_spec[11]; + options->bottom_margin_spec = float_spec[12]; + options->left_margin_spec = float_spec[13]; + options->right_margin_spec = float_spec[14]; + + /* Clean up */ + + free(option_def); +} + + + +static void free_option (option *const options) +{ + include_point* current; + include_point* dispose; + + current = options->include_points; + while (current != NULL) { + dispose = current; + current = current->next; + free(dispose); + }; +} + + + +static void init_world (option *const options, + const struct pam *const inpam, world_data *const world) +{ + /* constructs xi_ul,...,yi_lr + + Internally we use a pixel coordinate system with pixel units + + This also translates the --include points' coordinates + into the internal system + */ + + number mult_x, mult_y, add_after; + int add_before; + include_point* current_include; + + switch (options->enums[0]) { /* --input_system */ + case lattice: + add_after = -0.5; + add_before = 0; + break; + case pixel_s: + add_after = 0.0; + add_before = -1; + break; + }; + switch (options->enums[2]) { /* --input_unit */ + case image: + mult_x = (number)((inpam->width) + add_before); + mult_y = (number)((inpam->height) + add_before); + break; + case pixel_u: + mult_x = 1.0; + mult_y = 1.0; + break; + }; + + world->xi_ul = ((number) options->floats[0]) * mult_x + add_after; + world->yi_ul = ((number) options->floats[1]) * mult_y + add_after; + world->xi_ur = ((number) options->floats[2]) * mult_x + add_after; + world->yi_ur = ((number) options->floats[3]) * mult_y + add_after; + world->xi_ll = ((number) options->floats[4]) * mult_x + add_after; + world->yi_ll = ((number) options->floats[5]) * mult_y + add_after; + world->xi_lr = ((number) options->floats[6]) * mult_x + add_after; + world->yi_lr = ((number) options->floats[7]) * mult_y + add_after; + + for (current_include = options->include_points; current_include != NULL; + current_include = current_include->next) { + current_include->xi = current_include->xi * mult_x + add_after; + current_include->yi = current_include->yi * mult_y + add_after; + }; +} + + + +static bool solve_3_linear_equations (number* x1, number* x2, number* x3, + number const a11, number const a12, + number const a13, number const b1, + number const a21, number const a22, + number const a23, number const b2, + number const a31, number const a32, + number const a33, number const b3) +/*---------------------------------------------------------------------------- + The three equations are + a11*x1 + a12*x2 + a13*x3 = b1 + a21*x1 + a22*x2 + a23*x3 = b2 + a31*x1 + a32*x2 + a33*x3 = b3 + The return value is wether the system is solvable +----------------------------------------------------------------------------*/ +{ + number c11,c12,d1,c21,c22,d2,e,f; + int pivot; + + /* We do Gaussian elimination. + Whenever we find the system to be unsolvable, we just return FALSE. + In this specific case it makes the code clearer. + */ + + if (fabs(a11)fabs(c21)) { + if (fabs(c11)xi_lr - world->xi_ul; /* d1 is the image diagonal ul -> lr */ + dy1 = world->yi_lr - world->yi_ul; + dx2 = world->xi_ur - world->xi_ll; /* d2 is the image diagonal ll -> ur */ + dy2 = world->yi_ur - world->yi_ll; + dx3 = world->xi_ur - world->xi_ul; /* d3 is the image side ul -> ur */ + dy3 = world->yi_ur - world->yi_ul; + dx4 = world->xi_ul - world->xi_ll; /* d4 is the image side ll -> ul */ + dy4 = world->yi_ul - world->yi_ll; + dx5 = world->xi_ur - world->xi_lr; /* d5 is the image side lr -> ur */ + dy5 = world->yi_ur - world->yi_lr; + + det = dx2*dy1 - dx1*dy2; + + /* A determinant of 0 is really bad: It means that that diagonals in the + image are parallel (or of zero length) + */ + + if ((-epsxi_ul * zw_ul; + yw_ul = world->yi_ul * zw_ul; + xw_ur = world->xi_ur * zw_ur; + yw_ur = world->yi_ur * zw_ur; + xw_ll = world->xi_ll * zw_ll; + yw_ll = world->yi_ll * zw_ll; + xw_lr = world->xi_lr * zw_lr; + yw_lr = world->yi_lr * zw_lr; + + /* Now we introduce the margin. There are several ways the margin can be + defined. margin_spec keeps track of wether one of them has yet been + used. As long as margin_spec==FALSE, the variables top_margin to + bottom_margin are not initialized! */ + + if (options->bools[0]) { /* --frame_include */ + top_margin = 0.0; + left_margin = 0.0; + right_margin = 0.0; + bottom_margin = 0.0; + margin_spec = TRUE; + } else + margin_spec = FALSE; + + for (current_include = options->include_points; current_include != NULL; + current_include = current_include->next) { + solvable = solve_3_linear_equations(&include_xo, &include_yo, &include_zw, + xw_ul-xw_ur, xw_ul-xw_ll, current_include->xi, xw_ul, + yw_ul-yw_ur, yw_ul-yw_ll, current_include->yi, yw_ul, + zw_ul-zw_ur, zw_ul-zw_ll, 1.0, zw_ul); + if (!solvable) + pm_error ("The --include point %s lies on the horizon.", + current_include->specification); + if (include_zw<0.0) + pm_error ("The --include point %s lies beyond the horizon.", + current_include->specification); + if (margin_spec) { + top_margin = MAX(top_margin, -include_yo); + left_margin = MAX(left_margin, -include_xo); + right_margin = MAX(right_margin, include_xo-1.0); + bottom_margin = MAX(bottom_margin, include_yo-1.0); + } else { + top_margin = -include_yo; + left_margin = -include_xo; + right_margin = include_xo-1.0; + bottom_margin = include_yo-1.0; + margin_spec = TRUE; + }; + } + + if (margin_spec) { /* the margin is there. --top_margin and such can + still enlarge it */ + if (options->top_margin_spec) + top_margin = MAX(top_margin, options->floats[11]); + if (options->left_margin_spec) + left_margin = MAX(left_margin, options->floats[13]); + if (options->right_margin_spec) + right_margin = MAX(right_margin, options->floats[14]); + if (options->bottom_margin_spec) + bottom_margin = MAX(bottom_margin, options->floats[12]); + } else /* the margin is not yet there. --top_margin and + such can remedy this only if all of them are + given */ + if ((options->top_margin_spec) && (options->left_margin_spec) && + (options->right_margin_spec) && (options->bottom_margin_spec)) { + top_margin = options->floats[11]; + left_margin = options->floats[13]; + right_margin = options->floats[14]; + bottom_margin = options->floats[12]; + } else /* the margin finally is not there */ + pm_error ("No frame specified. " + "Use --frame_include=yes or --include or --margin."); + + world->xw_ul = xw_ul + - top_margin * (xw_ll-xw_ul) + - left_margin * (xw_ur-xw_ul); + world->yw_ul = yw_ul + - top_margin * (yw_ll-yw_ul) + - left_margin * (yw_ur-yw_ul); + world->zw_ul = zw_ul + - top_margin * (zw_ll-zw_ul) + - left_margin * (zw_ur-zw_ul); + world->xw_ur = xw_ur + - top_margin * (xw_lr-xw_ur) + - right_margin * (xw_ul-xw_ur); + world->yw_ur = yw_ur + - top_margin * (yw_lr-yw_ur) + - right_margin * (yw_ul-yw_ur); + world->zw_ur = zw_ur + - top_margin * (zw_lr-zw_ur) + - right_margin * (zw_ul-zw_ur); + world->xw_ll = xw_ll + - bottom_margin * (xw_ul-xw_ll) + - left_margin * (xw_lr-xw_ll); + world->yw_ll = yw_ll + - bottom_margin * (yw_ul-yw_ll) + - left_margin * (yw_lr-yw_ll); + world->zw_ll = zw_ll + - bottom_margin * (zw_ul-zw_ll) + - left_margin * (zw_lr-zw_ll); + world->xw_lr = xw_lr + - bottom_margin * (xw_ur-xw_lr) + - right_margin * (xw_ll-xw_lr); + world->yw_lr = yw_lr + - bottom_margin * (yw_ur-yw_lr) + - right_margin * (yw_ll-yw_lr); + world->zw_lr = zw_lr + - bottom_margin * (zw_ur-zw_lr) + - right_margin * (zw_ll-zw_lr); + + /* Again we have to forbid nonpositive z */ + + if ((world->zw_ulzw_urzw_llzw_lrxw_ul, world->yw_ul, world->zw_ul, + world->xw_ur, world->yw_ur, world->zw_ur), + norm_side(world->xw_ll, world->yw_ll, world->zw_ll, + world->xw_lr, world->yw_lr, world->zw_lr)); + dv = MAX (norm_side(world->xw_ul, world->yw_ul, world->zw_ul, + world->xw_ll, world->yw_ll, world->zw_ll), + norm_side(world->xw_ur, world->yw_ur, world->zw_ur, + world->xw_lr, world->yw_lr, world->zw_lr)); + xsteps = ceil(du*options->floats[8]); /* option->floats[8] is --detail */ + ysteps = ceil(dv*options->floats[8]); + + /* Turn the numbers of steps into width and height */ + + switch (options->enums[1]) { /* --output_system */ + case lattice: + width = xsteps; + height = ysteps; + break; + case pixel_s: + width = xsteps+1; + height = ysteps+1; + break; + }; + + /* Correct the proportion of width and height by increasing one of them */ + + switch (options->enums[4]) { /* --proportion */ + case free_: /* no correction at all */ + break; + case fixed: /* correction now */ + /* options->floats[9] is --ratio */ + width = MAX (floor(0.5 + ((number)height) * options->floats[9]), + width); + height = MAX (floor(0.5 + ((number)width) / options->floats[9]), + height); + break; + }; + + /* Override anything we have by the specified width and height */ + + if (!(options->width_spec)) + options->width=width; + if (!(options->height_spec)) + options->height=height; +} + + + +static void determine_coefficients_lattice (world_data *const world, + const option *const options) +/*---------------------------------------------------------------------------- + Constructs ax,...,cz from xw_ul,...,zw_lr + + The calculations assume lattice coordinates, that is the point ul + corresponds to the upper left corner of the pixel (0,0) and the + point lr corresponds to the lower left corner of the pixel + (width-1,height-1) +-----------------------------------------------------------------------------*/ +{ + number width,height; + + width = (number) options->width; + height = (number) options->height; + + world->bx = (world->xw_ur - world->xw_ul)/width; + world->cx = (world->xw_ll - world->xw_ul)/height; + world->by = (world->yw_ur - world->yw_ul)/width; + world->cy = (world->yw_ll - world->yw_ul)/height; + world->bz = (world->zw_ur - world->zw_ul)/width; + world->cz = (world->zw_ll - world->zw_ul)/height; + + world->ax = world->xw_ul + world->bx/2.0 + world->cx/2.0; + world->ay = world->yw_ul + world->by/2.0 + world->cy/2.0; + world->az = world->zw_ul + world->bz/2.0 + world->cz/2.0; +} + + + +static void determine_coefficients_pixel (world_data *const world, + const option *const options) +/*---------------------------------------------------------------------------- + Constructs ax,...,cz from xw_ul,...,zw_lr + + The calculations assume pixel coordinates, that is the point ul + corresponds to the centre of the pixel (0,0) and the point lr + corresponds to the centre of the pixel (width-1,height-1) +-----------------------------------------------------------------------------*/ +{ + number width,height; + + if (options->width == 1) + pm_error ("You specified 'pixel' as output coordinate model " + "and a width of 1. These things don't mix."); + if (options->height == 1) + pm_error ("You specified 'pixel' as output coordinate model " + "and a height of 1. These things don't mix."); + + width = (number) (options->width-1); + height = (number) (options->height-1); + + world->bx = (world->xw_ur - world->xw_ul)/width; + world->cx = (world->xw_ll - world->xw_ul)/height; + world->by = (world->yw_ur - world->yw_ul)/width; + world->cy = (world->yw_ll - world->yw_ul)/height; + world->bz = (world->zw_ur - world->zw_ul)/width; + world->cz = (world->zw_ll - world->zw_ul)/height; + + world->ax = world->xw_ul; + world->ay = world->yw_ul; + world->az = world->zw_ul; +} + + + +static void outpixel_to_inpixel (int const xo, int const yo, + number* const xi, number* const yi, + const world_data *const world) +{ + number xof,yof,xw,yw,zw; + + xof = (number) xo; + yof = (number) yo; + xw = world->ax + world->bx*xof + world->cx*yof; + yw = world->ay + world->by*xof + world->cy*yof; + zw = world->az + world->bz*xof + world->cz*yof; + *xi = xw/zw; + *yi = yw/zw; +} + +static int outpixel_to_iny (int xo, int yo, const world_data *const world) +{ + number xi,yi; + + outpixel_to_inpixel (xo,yo,&xi,&yi,world); + + return (int) yi; +} + +static int clean_y (int const y, const struct pam *const outpam) +{ + return MIN(MAX(0, y), outpam->height-1); +} + +static void init_buffer (buffer *const b, const world_data *const world, + const option *const options, + const struct pam *const inpam, + const struct pam *const outpam) +{ + int yul, yur, yll, ylr, y_min; + int i, num_rows; + + yul = outpixel_to_iny (0,0,world); + yur = outpixel_to_iny (outpam->width-1,0,world); + yll = outpixel_to_iny (0,outpam->height-1,world); + ylr = outpixel_to_iny (outpam->width-1,outpam->height-1,world); + + y_min = MIN (MIN (yul,yur), MIN (yll,ylr)); + num_rows = MAX (MAX (diff (yul, yur), + diff (yll, ylr)), + MAX (diff (clean_y(yul,outpam), clean_y(y_min,outpam)), + diff (clean_y(yur,outpam), clean_y(y_min,outpam)))) + + 2; + switch (options->enums[3]) { /* --interpolation */ + case nearest: + break; + case linear: + num_rows += 1; + break; + }; + if (num_rows > inpam->height) + num_rows = inpam->height; + + b->num_rows = num_rows; + MALLOCARRAY_SAFE (b->rows, num_rows); + for (i=0; irows[i] = pnm_allocpamrow (inpam); + pnm_readpamrow (inpam, b->rows[i]); + }; + b->last_physical = num_rows-1; + b->last_logical = num_rows-1; + b->inpam = inpam; +} + +static tuple* read_buffer (buffer *const b, int const logical_y) +{ + int y; + + while (logical_y > b->last_logical) { + b->last_physical++; + if (b->last_physical == b->num_rows) + b->last_physical = 0; + pnm_readpamrow (b->inpam, b->rows[b->last_physical]); + b->last_logical++; + } + + y = logical_y - b->last_logical + b->last_physical; + if (y<0) + y += b->num_rows; + + return b->rows[y]; +} + +static void free_buffer (buffer *const b) +{ + int i; + + for (i=0; inum_rows; i++) + pnm_freepamrow (b->rows[i]); + free (b->rows); +} + + + + +/* The following variables are global for speed reasons. + In this way they do not have to be passed to each call of the + interpolation functions + + Think of this as Schönfinkeling (aka Currying). +*/ + +static tuple background; +static buffer* indata; +static int width,height,depth; + +static void init_interpolation_global_vars (buffer* const inbuffer, + const struct pam *const inpam, + const struct pam *const outpam) +{ + pnm_createBlackTuple (outpam, &background); + indata = inbuffer; + width = inpam->width; + height = inpam->height; + depth = outpam->depth; +} + + + +static void clean_interpolation_global_vars (void) +{ + free (background); +} + + + +/* These functions perform the interpolation */ + +static tuple attempt_read (int const x, int const y) +{ + if ((x<0) || (x>=width) || (y<0) || (y>=height)) + return background; + else + return read_buffer(indata, y)[x]; +} + + + +static void take_nearest (tuple const dest, number const x, number const y) +{ + int xx,yy,entry; + tuple p; + + xx = (int)floor(x+0.5); + yy = (int)floor(y+0.5); + p = attempt_read (xx, yy); + for (entry=0; entry