diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2008-07-27 20:18:32 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2008-07-27 20:18:32 +0000 |
commit | 183ee46475c0d75207055d6c18f9dd87dc537389 (patch) | |
tree | d093a92bcb9214eb0c1e4cd994cad49182fd6ecb /editor | |
parent | d571531281cdf1635ae63988c58c321f01a54f74 (diff) | |
download | netpbm-mirror-183ee46475c0d75207055d6c18f9dd87dc537389.tar.gz netpbm-mirror-183ee46475c0d75207055d6c18f9dd87dc537389.tar.xz netpbm-mirror-183ee46475c0d75207055d6c18f9dd87dc537389.zip |
replace mysterious window height calculation with one that makes sense and makes the window tall enough for the actual computation
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@689 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r-- | editor/pamperspective.c | 442 |
1 files changed, 283 insertions, 159 deletions
diff --git a/editor/pamperspective.c b/editor/pamperspective.c index f18e3a03..744f123c 100644 --- a/editor/pamperspective.c +++ b/editor/pamperspective.c @@ -21,12 +21,13 @@ #define _BSD_SOURCE /* Make sure strdup is int string.h */ #include <assert.h> +#include <stdlib.h> #include <math.h> #include <string.h> -#include "pam.h" -#include "shhopt.h" #include "mallocvar.h" +#include "shhopt.h" +#include "pam.h" typedef double number; @@ -317,45 +318,53 @@ static int parse_enum (const char *const text, -static number parse_float (char *const text) +static number +parseFloat(const char * const text) { /*---------------------------------------------------------------------------- Parse an argument given to a float command line option. We cannot just call strtod, because we want to parse fractions like "5/3" -----------------------------------------------------------------------------*/ -{ - bool error; - char* end; - char* denstart; - number num,den; - - error = FALSE; - num = strtod (text, &end); /* try strtod anyway */ - switch (*end) { - case 0: /* It is a plain number */ - break; - case '/': /* It might be a fraction */ - /* (Try to) parse the numerator */ - *end = 0; - num = strtod (text, &end); - error = (*end) != 0; - if (!error) { - /* Undo the above change */ - *end = '/'; - /* (Try to) parse the denominator */ - denstart = end+1; - den = strtod (denstart, &end); - error = (fabs(den)<eps) || ((*end) != 0); - if (!error) - num /= den; + bool error; + char * end; + number num; + char * buffer; + + buffer = strdup(text); + if (!buffer) + pm_error("Out of memory"); + + error = FALSE; + num = strtod(buffer, &end); /* try strtod anyway */ + switch(*end) { + case 0: /* It is a plain number */ + break; + case '/': /* It might be a fraction */ + /* (Try to) parse the numerator */ + *end = 0; + num = strtod(text, &end); + error = (*end != '\0'); + if (!error) { + char * const denStart = end + 1; + number denominator; + + /* Undo the above change */ + *end = '/'; + /* (Try to) parse the denominator */ + denominator = strtod(denStart, &end); + error = (fabs(denominator) < eps) || (*end != '\0'); + if (!error) + num /= denominator; + }; + break; + default: /* It is no number format we know */ + error = TRUE; }; - break; - default: /* It is no number format we know */ - error = TRUE; - }; - if (error) - pm_error ("Invalid number format: %s", text); + if (error) + pm_error("Invalid number format: %s", text); - return num; + free(buffer); + + return num; } @@ -384,8 +393,8 @@ static void parse_include_point(char * specification, 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); + new_point->xi = (number) parseFloat(specification); + new_point->yi = (number) parseFloat(comma_seek+1); *comma_seek = ','; } @@ -433,9 +442,12 @@ static void parse_include_points(const char * const include_opt, } -static void parse_command_line (int argc, char* argv[], option *const options) -{ - char* float_text[num_float_options]; + +static void +parseCommandLine(int argc, const char * argv[], + option * const options) { + + const 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]; @@ -448,6 +460,8 @@ static void parse_command_line (int argc, char* argv[], option *const options) unsigned int option_def_index; optEntry* option_def; + set_command_line_defaults(options); + /* Let shhopt try its best */ option_def_index = 0; @@ -471,7 +485,7 @@ static void parse_command_line (int argc, char* argv[], option *const options) opt.opt_table = option_def; opt.short_allowed = FALSE; opt.allowNegNum = TRUE; - optParseOptions3 (&argc, argv, opt, sizeof(opt), 0); + optParseOptions3 (&argc, (char **)argv, opt, sizeof(opt), 0); /* The non-option arguments are optionally all eight coordinates and optionally the input filename @@ -504,7 +518,7 @@ static void parse_command_line (int argc, char* argv[], option *const options) for (i=0; i<num_float_options; i++) if (float_spec[i]) - options->floats[i] = parse_float (float_text[i]); + options->floats[i] = parseFloat (float_text[i]); /* Parse enum options -- shhopt retrieved them as strings */ @@ -622,7 +636,7 @@ static bool solve_3_linear_equations (number* x1, number* x2, number* x3, 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 + The return value is whether the system is solvable ----------------------------------------------------------------------------*/ { number c11,c12,d1,c21,c22,d2,e,f; @@ -717,18 +731,18 @@ static bool solve_3_linear_equations (number* x1, number* x2, number* x3, static void determine_world_parallelogram (world_data *const world, const option *const options) /*---------------------------------------------------------------------------- - constructs xw_ul,...,zw_lr from xi_ul,...,yi_lr + Construct xw_ul,...,zw_lr from xi_ul,...,yi_lr - Actually this is a solution of a linear equation system. + This is a solution of a linear equation system. - We first solve 4 variables (the 4 z-coordinates) against 4 - equations: Each z-coordinate determines the corresponding x- and - y-coordinates in a linear fashion, where the coefficients are taken - from the image coordinates. This corresponds to the fact that a - point of an image determines a line in the world. + We first solve 4 equations for 4 variables (the 4 z-coordinates): + Each z-coordinate determines the corresponding x- and y-coordinates + in a linear fashion, where the coefficients are taken from the image + coordinates. This corresponds to the fact that a point of an image + determines a line in the world. 3 equations state that the 4 points form a parallelogram. The 4th - equation is for normalization and states, that the center of the + equation is for normalization and states that the center of the parallelogram has a z-coordinate of 1. -----------------------------------------------------------------------------*/ { @@ -896,9 +910,11 @@ static void determine_world_parallelogram (world_data *const world, -static int diff (int const a, int const b) -{ - return MAX (b-a, a-b); +static unsigned int +distance(unsigned int const a, + unsigned int const b) { + + return a > b ? a - b : b - a; } @@ -1065,37 +1081,85 @@ static void determine_coefficients_pixel (world_data *const world, -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 void +outpixelToInPos(int const outCol, + int const outRow, + number * const inColP, + number * const inRowP, + const world_data * const worldP) { +/*---------------------------------------------------------------------------- + For a pixel of the output image at Column 'outCol', row 'outRow', + determine the position in the input image that corresponds to the + center of that pixel. + + This position is not a pixel position -- it's a position in + continuous space, for example Row 9.2, Column 0.1. And it isn't + necessarily within the input image, for example Column 600 even though + the input image is only 500 pixels wide, and a coordinate might even + be negative. +-----------------------------------------------------------------------------*/ + number const outColF = (number) outCol; + number const outRowF = (number) outRow; + + number const xw = worldP->ax + worldP->bx * outColF + worldP->cx * outRowF; + number const yw = worldP->ay + worldP->by * outColF + worldP->cy * outRowF; + number const zw = worldP->az + worldP->bz * outColF + worldP->cz * outRowF; + + *inColP = xw/zw; + *inRowP = 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 +outpixelToInRow(int const outCol, + int const outRow, + const world_data * const worldP) { + + number xi, yi; + + outpixelToInPos(outCol, outRow, &xi, &yi, worldP); + + return (int) yi; } -static int clean_y (int const y, const struct pam *const outpam) -{ - return MIN(MAX(0, y), outpam->height-1); + + +static int +boundedRow(int const unboundedRow, + const struct pam * const outpamP) { + + return MIN(MAX(0, unboundedRow), outpamP->height-1); } +#if 0 +/* This is the original calculation of window height. It's + mysterious, and doesn't work. It looks like it basically wants to + take the greater of vertical displacement of the top edge of the + input quadrilateral and that of the bottom edge. In simple + scenarios, that is in fact what it does, and I can see how those + edges might be where the most stretching takes place. However, it + the calculation is obviously more complex than that. + + It doesn't work because the actual image generation produces rows + in the middle that are derived from lines in the input quadrilateral + with greater slope than either the top or bottom edge. I.e. to + compute one output row, it needs more rows of input than this + calculation provides. + + I don't know if that means the computation of the output is wrong + or the computation of the window height is wrong. The code is too + opaque. But just to make a viable computation, I replaced the + window height calculation with the brute force computation you + see below: it determines the vertical displacement of every line + of the input quadrilateral that is used to generate an output row + and takes the greatest of them for the window height. + + - Bryan Henderson 08.07.27. +*/ + static unsigned int windowHeight(const world_data * const worldP, @@ -1104,18 +1168,20 @@ windowHeight(const world_data * const worldP, const option * const optionsP) { unsigned int numRows; - unsigned int yul, yur, yll, ylr, y_min; + int yul, yur, yll, ylr, y_min; - yul = outpixel_to_iny(0, 0, worldP); - yur = outpixel_to_iny(outpamP->width-1, 0, worldP); - yll = outpixel_to_iny(0, outpamP->height-1, worldP); - ylr = outpixel_to_iny(outpamP->width-1, outpamP->height-1, worldP); + yul = outpixelToInRow(0, 0, worldP); + yur = outpixelToInRow(outpamP->width-1, 0, worldP); + yll = outpixelToInRow(0, outpamP->height-1, worldP); + ylr = outpixelToInRow(outpamP->width-1, outpamP->height-1, worldP); y_min = MIN(MIN(yul, yur), MIN(yll, ylr)); numRows = MAX(MAX(diff(yul, yur), diff(yll, ylr)), - MAX(diff(clean_y(yul, outpamP), clean_y(y_min, outpamP)), - diff(clean_y(yur, outpamP), clean_y(y_min, outpamP)))) + MAX(diff(boundedRow(yul, outpamP), + boundedRow(y_min, outpamP)), + diff(boundedRow(yur, outpamP), + boundedRow(y_min, outpamP)))) + 2; switch (optionsP->enums[3]) { /* --interpolation */ case interp_nearest: @@ -1129,11 +1195,43 @@ windowHeight(const world_data * const worldP, return numRows; } +#endif + + + +static unsigned int +windowHeight(const world_data * const worldP, + const struct pam * const inpamP, + const struct pam * const outpamP, + const option * const optionsP) { + + unsigned int outRow; + unsigned int maxRowWindowHeight; + + maxRowWindowHeight = 1; /* initial value */ + + for (outRow = 0; outRow < outpamP->height; ++outRow) { + unsigned int const leftCol = 0; + unsigned int const rghtCol = outpamP->width - 1; + unsigned int const leftInRow = + boundedRow(outpixelToInRow(leftCol, outRow, worldP), outpamP); + unsigned int const rghtInRow = + boundedRow(outpixelToInRow(rghtCol, outRow, worldP), outpamP); + + unsigned int const rowWindowHeight = distance(leftInRow, rghtInRow); + + maxRowWindowHeight = MAX(maxRowWindowHeight, rowWindowHeight); + } + + /* We add 2 for rounding */ + + return maxRowWindowHeight + 2; +} static void -init_buffer(buffer * const bufferP, +buffer_init(buffer * const bufferP, const world_data * const worldP, const option * const optionsP, const struct pam * const inpamP, @@ -1160,10 +1258,19 @@ init_buffer(buffer * const bufferP, -static tuple * -read_buffer(buffer * const bufferP, - unsigned int const imageRow) { +static const tuple * +buffer_getRow(buffer * const bufferP, + unsigned int const imageRow) { +/*---------------------------------------------------------------------------- + Return row 'imageRow' of an image. + The return value is a pointer into storage that belongs to *bufferP. + + *bufferP remembers only a window of the image, and the window + cannot move up, so 'imageRow' cannot be higher in the image than + the lowest row read so far through *bufferP plus *bufferP's maximum + window height. We assume that. +-----------------------------------------------------------------------------*/ unsigned int bufferRow; /* The row of the buffer that holds row 'imageRow' of the image */ unsigned int n; @@ -1201,7 +1308,7 @@ read_buffer(buffer * const bufferP, static void -term_buffer(buffer * const bufferP) { +buffer_term(buffer * const bufferP) { unsigned int i; @@ -1225,91 +1332,110 @@ term_buffer(buffer * const bufferP) { -/* The following variables are global for speed reasons. - In this way they do not have to be passed to each call of the +struct interpContext { + tuple background; + buffer* indata; + int width,height,depth; +}; + +/* The following is global for speed reasons. + In this way it does 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 struct interpContext ictx; -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 +init_interpolation_global_vars(buffer * const inbufferP, + const struct pam * const inpamP, + const struct pam * const outpamP) { + + pnm_createBlackTuple(outpamP, &ictx.background); + ictx.indata = inbufferP; + ictx.width = inpamP->width; + ictx.height = inpamP->height; + ictx.depth = outpamP->depth; } -static void clean_interpolation_global_vars (void) -{ - free (background); +static void +clean_interpolation_global_vars(void) { + + free(ictx.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 tuple +getPixel(int const col, + int const row) { +/*---------------------------------------------------------------------------- + Get the pixel at Row 'row', Column 'col' of the image which is the + context of the interpolation in which we are called. + + Consider the image to go on forever in all directions (even negative + column/row numbers), being the background color everywhere outside + the actual image. +-----------------------------------------------------------------------------*/ + if ((col < 0) || (col >= ictx.width) || (row < 0) || (row >= ictx.height)) + return ictx.background; + else + return buffer_getRow(ictx.indata, row)[col]; } -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<depth; entry++) { - dest[entry]=p[entry]; - } +static void +takeNearest(tuple const dest, + number const x, + number const y) { + + int const xx = (int)floor(x+0.5); + int const yy = (int)floor(y+0.5); + tuple const p = getPixel(xx, yy); + + unsigned int entry; + + for (entry = 0; entry < ictx.depth; ++entry) { + dest[entry] = p[entry]; + } } -static void linear_interpolation (tuple const dest, - number const x, number const y) -{ - int xx,yy,entry; - number xf,yf,a,b,c,d; - tuple p1,p2,p3,p4; - - xx = (int)floor(x); - yy = (int)floor(y); - xf = x-(number)xx; - yf = y-(number)yy; - p1 = attempt_read (xx, yy); - p2 = attempt_read (xx+1, yy); - p3 = attempt_read (xx, yy+1); - p4 = attempt_read (xx+1, yy+1); - a = (1.0-xf)*(1.0-yf); - b = xf*(1.0-yf); - c = (1.0-xf)*yf; - d = xf*yf; - for (entry=0; entry<depth; entry++) { - dest[entry]=(sample) floor( - a*((number) p1[entry]) + - b*((number) p2[entry]) + - c*((number) p3[entry]) + - d*((number) p4[entry]) + - 0.5); - } +static void +linearInterpolation(tuple const dest, + number const x, + number const y) { + + int const xx = (int)floor(x); + int const yy = (int)floor(y); + number const xf = x - (number)xx; + number const yf = y - (number)yy; + tuple const p1 = getPixel(xx, yy); + tuple const p2 = getPixel(xx+1, yy); + tuple const p3 = getPixel(xx, yy+1); + tuple const p4 = getPixel(xx+1, yy+1); + number const a = (1.0-xf) * (1.0-yf); + number const b = xf * (1.0-yf); + number const c = (1.0-xf) * yf; + number const d = xf * yf; + + unsigned int entry; + + for (entry=0; entry < ictx.depth; ++entry) { + dest[entry] = floor( + a * (number) p1[entry] + + b * (number) p2[entry] + + c * (number) p3[entry] + + d * (number) p4[entry] + + 0.5); + } } @@ -1328,8 +1454,8 @@ perspective(struct pam * const outpamP, unsigned int col; for (col = 0; col < outpamP->width; ++col) { - number xi,yi; - outpixel_to_inpixel(col, row, &xi, &yi, worldP); + number xi, yi; + outpixelToInPos(col, row, &xi, &yi, worldP); interpolater(outrow[col], xi, yi); } pnm_writepamrow(outpamP, outrow); @@ -1340,7 +1466,7 @@ perspective(struct pam * const outpamP, int -main(int argc, char* argv[]) { +main(int argc, const char * argv[]) { FILE * ifP; struct pam inpam; @@ -1350,11 +1476,9 @@ main(int argc, char* argv[]) { world_data world; interpolateFn * interpolater; - pnm_init(&argc, argv); - - set_command_line_defaults(&options); + pm_proginit(&argc, argv); - parse_command_line(argc, argv, &options); + parseCommandLine(argc, argv, &options); ifP = pm_openr(options.infilename); @@ -1388,21 +1512,21 @@ main(int argc, char* argv[]) { /* Initialize the actual calculation */ - init_buffer(&inbuffer, &world, &options, &inpam, &outpam); + buffer_init(&inbuffer, &world, &options, &inpam, &outpam); init_interpolation_global_vars(&inbuffer, &inpam, &outpam); switch (options.enums[3]) { /* --interpolation */ case interp_nearest: - interpolater = take_nearest; + interpolater = takeNearest; break; case interp_linear: - interpolater = linear_interpolation; + interpolater = linearInterpolation; break; }; perspective(&outpam, &world, interpolater); clean_interpolation_global_vars(); - term_buffer(&inbuffer); + buffer_term(&inbuffer); free_option(&options); pm_close(ifP); pm_close(stdout); |