diff options
Diffstat (limited to 'lib/libpam.c')
-rw-r--r-- | lib/libpam.c | 553 |
1 files changed, 436 insertions, 117 deletions
diff --git a/lib/libpam.c b/lib/libpam.c index ab75fab6..cc6368e1 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -1,9 +1,12 @@ -/*---------------------------------------------------------------------------- +/*============================================================================= libpam.c ------------------------------------------------------------------------------- +=============================================================================== These are the library functions, which belong in the libnetpbm library, that deal with the PAM (Portable Arbitrary Format) image format. ------------------------------------------------------------------------------*/ + + This file was originally written by Bryan Henderson and is contributed + to the public domain by him and subsequent authors. +=============================================================================*/ /* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. @@ -20,9 +23,10 @@ #include <math.h> -#include "pm_c_util.h" -#include "mallocvar.h" -#include "nstring.h" +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/nstring.h" + #include "pam.h" #include "ppm.h" #include "libpbm.h" @@ -70,6 +74,7 @@ pamCommentP(const struct pam * const pamP) { } + static void validateComputableSize(struct pam * const pamP) { /*---------------------------------------------------------------------------- @@ -205,21 +210,6 @@ pnm_createBlackTuple(const struct pam * const pamP, -void -createBlackTuple(const struct pam * const pamP, - tuple * const blackTupleP) { - -/* This is poorly named, because it lacks the "pnm" prefix. But for some - reason, this is how we originally named this. So to maintain backward - compatibility with binaries that refer to "createBlackTuple", we define - this. The preferred name, pnm_createBlackTuple() was new in Netpbm 10.20, - January 2004. We should eventually retire createBlackTuple(). -*/ - pnm_createBlackTuple(pamP, blackTupleP); -} - - - static tuple * allocPamRow(const struct pam * const pamP) { /*---------------------------------------------------------------------------- @@ -227,12 +217,11 @@ allocPamRow(const struct pam * const pamP) { overflow will not occur in our calculations. NOTE: pnm_readpaminit() ensures this assumption is valid. -----------------------------------------------------------------------------*/ - /* The tuple row data structure starts with 'width' pointers to - the tuples, immediately followed by the 'width' tuples - themselves. Each tuple consists of 'depth' samples. + /* The tuple row data structure starts with pointers to the tuples, + immediately followed by the tuples themselves. */ - int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample); + unsigned int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample); tuple * tuplerow; tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple)); @@ -262,9 +251,9 @@ pnm_allocpamrow(const struct pam * const pamP) { tuple * const tuplerow = allocPamRow(pamP); if (tuplerow == NULL) - pm_error("Out of memory allocating space for a tuple row of\n" - "%d tuples by %d samples per tuple by %d bytes per sample.", - pamP->width, allocationDepth(pamP), sizeof(sample)); + pm_error("Out of memory allocating space for a tuple row of " + "%d tuples by %d samples per tuple by %u bytes per sample.", + pamP->width, allocationDepth(pamP), (unsigned)sizeof(sample)); return tuplerow; } @@ -401,7 +390,7 @@ pnm_setminallocationdepth(struct pam * const pamP, pm_error("Can't set minimum allocation depth in pam structure, " "because the structure is only %u bytes long, and to " "have an allocation_depth field, it must bea at least %u", - pamP->len, PAM_STRUCT_SIZE(allocation_depth)); + pamP->len, (unsigned)PAM_STRUCT_SIZE(allocation_depth)); pamP->allocation_depth = MAX(allocationDepth, pamP->depth); @@ -430,8 +419,9 @@ pnm_setpamrow(const struct pam * const pamP, #define MAX_VALUE_LENGTH 255 static void -parse_header_line(const char buffer[], char label[MAX_LABEL_LENGTH+1], - char value[MAX_VALUE_LENGTH+1]) { +parseHeaderLine(const char buffer[], + char label[MAX_LABEL_LENGTH+1], + char value[MAX_VALUE_LENGTH+1]) { int buffer_curs; @@ -483,9 +473,69 @@ struct headerSeen { static void -process_header_line(char const buffer[], - struct pam * const pamP, - struct headerSeen * const headerSeenP) { +parseHeaderUint(const char * const valueString, + unsigned int * const valueNumP, + const char * const name) { +/*---------------------------------------------------------------------------- + Interpret 'valueString' as the number in a header such as + "WIDTH 200". + + 'name' is the header name ("WIDTH" in the example). +-----------------------------------------------------------------------------*/ + + if (strlen(valueString) == 0) + pm_error("Missing value for %s in PAM file header.", name); + else { + char * endptr; + long int valueNum; + errno = 0; /* Clear errno so we can detect strtol() failure */ + valueNum = strtol(valueString, &endptr, 10); + if (errno != 0) + pm_error("Too-large value for %s in " + "PAM file header: '%s'", name, valueString); + else if (*endptr != '\0') + pm_error("Non-numeric value for %s in " + "PAM file header: '%s'", name, valueString); + else if (valueNum < 0) + pm_error("Negative value for %s in " + "PAM file header: '%s'", name, valueString); + else if ((unsigned int)valueNum != valueNum) + pm_error("Ridiculously large value for %s in " + "PAM file header: %lu", name, valueNum); + else + *valueNumP = (unsigned int)valueNum; + } +} + + + +static void +parseHeaderInt(const char * const valueString, + int * const valueNumP, + const char * const name) { +/*---------------------------------------------------------------------------- + This is not what it seems. It is the same thing as + parseHeaderUint, except that the type of the value it returns is + "int" instead of "unsigned int". But that doesn't mean the value can + be negative. We throw an error is it is not positive. +-----------------------------------------------------------------------------*/ + unsigned int valueNum; + + parseHeaderUint(valueString, &valueNum, name); + + if ((int)valueNum != valueNum) + pm_error("Ridiculously large value for %s in " + "PAM file header: %u", name, valueNum); + else + *valueNumP = (int)valueNum; +} + + + +static void +processHeaderLine(char const buffer[], + struct pam * const pamP, + struct headerSeen * const headerSeenP) { /*---------------------------------------------------------------------------- Process a line from the PAM header. The line is buffer[], and it is not a comment or blank. @@ -497,67 +547,44 @@ process_header_line(char const buffer[], char label[MAX_LABEL_LENGTH+1]; char value[MAX_VALUE_LENGTH+1]; - parse_header_line(buffer, label, value); + parseHeaderLine(buffer, label, value); - if (strcmp(label, "ENDHDR") == 0) + if (streq(label, "ENDHDR")) headerSeenP->endhdr = TRUE; - else { - if (strcmp(label, "WIDTH") == 0 || - strcmp(label, "HEIGHT") == 0 || - strcmp(label, "DEPTH") == 0 || - strcmp(label, "MAXVAL") == 0) { - - if (strlen(value) == 0) - pm_error("Missing value for %s in PAM file header.", - label); + else if (streq(label, "WIDTH")) { + parseHeaderInt(value, &pamP->width, label); + headerSeenP->width = TRUE; + } else if (streq(label, "HEIGHT")) { + parseHeaderInt(value, &pamP->height, label); + headerSeenP->height = TRUE; + } else if (streq(label, "DEPTH")) { + parseHeaderUint(value, &pamP->depth, label); + headerSeenP->depth = TRUE; + } else if (streq(label, "MAXVAL")) { + unsigned int maxval; + parseHeaderUint(value, &maxval, label); + if (maxval >= (1<<16)) + pm_error("Maxval too large: %u. Max is 65535", maxval); + pamP->maxval = maxval; + headerSeenP->maxval = TRUE; + } else if (streq(label, "TUPLTYPE")) { + if (strlen(value) == 0) + pm_error("TUPLTYPE header does not have any tuple type text"); + else { + size_t const oldLen = strlen(pamP->tuple_type); + if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1) + pm_error("TUPLTYPE value too long in PAM header"); + if (oldLen == 0) + strcpy(pamP->tuple_type, value); else { - char *endptr; - long int numeric_value; - errno = 0; /* Clear errno so we can detect strtol() failure */ - numeric_value = strtol(value, &endptr, 10); - if (errno != 0) - pm_error("Too-large value for %s in " - "PAM file header: '%s'", label, value); - if (*endptr != '\0') - pm_error("Non-numeric value for %s in " - "PAM file header: '%s'", label, value); - else if (numeric_value < 0) - pm_error("Negative value for %s in " - "PAM file header: '%s'", label, value); + strcat(pamP->tuple_type, " "); + strcat(pamP->tuple_type, value); } + pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0'; } - - if (strcmp(label, "WIDTH") == 0) { - pamP->width = atoi(value); - headerSeenP->width = TRUE; - } else if (strcmp(label, "HEIGHT") == 0) { - pamP->height = atoi(value); - headerSeenP->height = TRUE; - } else if (strcmp(label, "DEPTH") == 0) { - pamP->depth = atoi(value); - headerSeenP->depth = TRUE; - } else if (strcmp(label, "MAXVAL") == 0) { - pamP->maxval = atoi(value); - headerSeenP->maxval = TRUE; - } else if (strcmp(label, "TUPLTYPE") == 0) { - if (strlen(value) == 0) - pm_error("TUPLTYPE header does not have any tuple type text"); - else { - size_t const oldLen = strlen(pamP->tuple_type); - if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1) - pm_error("TUPLTYPE value too long in PAM header"); - if (oldLen == 0) - strcpy(pamP->tuple_type, value); - else { - strcat(pamP->tuple_type, " "); - strcat(pamP->tuple_type, value); - } - pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0'; - } - } else - pm_error("Unrecognized header line: '%s'. " - "Possible missing ENDHDR line?", label); - } + } else + pm_error("Unrecognized header line type: '%s'. " + "Possible missing ENDHDR line?", label); } @@ -576,7 +603,7 @@ appendComment(char ** const commentsP, if (*commentsP == NULL) pm_error("Couldn't get storage for %u characters of comments from " - "the PAM header", commentLen); + "the PAM header", (unsigned)commentLen); strcat(*commentsP, commentLine); } @@ -592,7 +619,7 @@ disposeOfComments(const struct pam * const pamP, if (retP) *retP = comments; else - strfree(comments); + pm_strfree(comments); } @@ -635,10 +662,10 @@ readpaminitrest(struct pam * const pamP) { buffer[256-1-1] = '\n'; /* In case fgets() truncated */ if (buffer[0] == '#') appendComment(&comments, buffer); - else if (stripeq(buffer, "")); + else if (pm_stripeq(buffer, "")); /* Ignore it; it's a blank line */ else - process_header_line(buffer, pamP, &headerSeen); + processHeaderLine(buffer, pamP, &headerSeen); } } @@ -718,13 +745,127 @@ pnm_readpaminitrestaspnm(FILE * const fileP, unsigned int pnm_bytespersample(sample const maxval) { +/*---------------------------------------------------------------------------- + Return the number of bytes per sample in the PAM raster of a PAM image + with maxval 'maxval'. It's defined to be the minimum number of bytes + needed for that maxval, i.e. 1 for maxval < 256, 2 otherwise. +-----------------------------------------------------------------------------*/ + + /* The PAM format requires maxval to be greater than zero and less than + 1<<16, but since that is a largely arbitrary restriction, we don't want + to rely on it. + */ - assert(sizeof(maxval) * 8 <= 32); + unsigned int i; + sample a; + + for (i = 0, a = maxval; i <= sizeof(maxval); ++i) { + if (a == 0) + return i; + a >>= 8; + } + return 0; /* silence compiler warning */ +} + + + +static void +validateMinDepth(const struct pam * const pamP, + unsigned int const minDepth) { - if (maxval >> 8 == 0) return 1; - else if (maxval >> 16 == 0) return 2; - else if (maxval >> 24 == 0) return 3; - else return 4; + if (pamP->depth < minDepth) + pm_error("Depth %u is insufficient for tuple type '%s'. " + "Minimum depth is %u", + pamP->depth, pamP->tuple_type, minDepth); +} + + + +static void +interpretTupleType(struct pam * const pamP) { +/*---------------------------------------------------------------------------- + Fill in redundant convenience fields in *pamP with information the + pamP->tuple_type value implies: + + visual + colorDepth + haveOpacity + opacityPlane + + Validate the tuple type against the depth and maxval as well. +-----------------------------------------------------------------------------*/ + const char * const tupleType = + pamP->len >= PAM_STRUCT_SIZE(tuple_type) ? pamP->tuple_type : ""; + + bool visual; + unsigned int colorDepth; + bool haveOpacity; + unsigned int opacityPlane; + + assert(pamP->depth > 0); + + switch (PAM_FORMAT_TYPE(pamP->format)) { + case PAM_TYPE: { + if (streq(tupleType, "BLACKANDWHITE")) { + visual = true; + colorDepth = 1; + haveOpacity = false; + if (pamP->maxval != 1) + pm_error("maxval %u is not consistent with tuple type " + "BLACKANDWHITE (should be 1)", + (unsigned)pamP->maxval); + } else if (streq(tupleType, "GRAYSCALE")) { + visual = true; + colorDepth = 1; + haveOpacity = false; + } else if (streq(tupleType, "GRAYSCALE_ALPHA")) { + visual = true; + colorDepth = 1; + haveOpacity = true; + opacityPlane = PAM_GRAY_TRN_PLANE; + validateMinDepth(pamP, 2); + } else if (streq(tupleType, "RGB")) { + visual = true; + colorDepth = 3; + haveOpacity = false; + validateMinDepth(pamP, 3); + } else if (streq(tupleType, "RGB_ALPHA")) { + visual = true; + colorDepth = 3; + haveOpacity = true; + opacityPlane = PAM_TRN_PLANE; + validateMinDepth(pamP, 4); + } else { + visual = false; + } + } break; + case PPM_TYPE: + visual = true; + colorDepth = 3; + haveOpacity = false; + assert(pamP->depth == 3); + break; + case PGM_TYPE: + visual = true; + colorDepth = 1; + haveOpacity = false; + break; + case PBM_TYPE: + visual = true; + colorDepth = 1; + haveOpacity = false; + break; + default: + assert(false); + } + if (pamP->size >= PAM_STRUCT_SIZE(visual)) + pamP->visual = visual; + if (pamP->size >= PAM_STRUCT_SIZE(color_depth)) + pamP->color_depth = colorDepth; + if (pamP->size >= PAM_STRUCT_SIZE(have_opacity)) + pamP->have_opacity = haveOpacity; + if (pamP->size >= PAM_STRUCT_SIZE(opacity_plane)) + pamP->opacity_plane = opacityPlane; } @@ -736,9 +877,9 @@ pnm_readpaminit(FILE * const file, if (size < PAM_STRUCT_SIZE(tuple_type)) pm_error("pam object passed to pnm_readpaminit() is too small. " - "It must be large\n" + "It must be large " "enough to hold at least up to the " - "'tuple_type' member, but according\n" + "'tuple_type' member, but according " "to the 'size' argument, it is only %d bytes long.", size); @@ -794,6 +935,8 @@ pnm_readpaminit(FILE * const file, pamP->plainformat = FALSE; /* See below for complex explanation of why this is FALSE. */ + interpretTupleType(pamP); + validateComputableSize(pamP); } @@ -867,9 +1010,9 @@ pnm_writepaminit(struct pam * const pamP) { if (pamP->size < PAM_STRUCT_SIZE(bytes_per_sample)) pm_error("pam object passed to pnm_writepaminit() is too small. " - "It must be large\n" + "It must be large " "enough to hold at least up through the " - "'bytes_per_sample' member, but according\n" + "'bytes_per_sample' member, but according " "to its 'size' member, it is only %u bytes long.", pamP->size); if (pamP->len < PAM_STRUCT_SIZE(maxval)) @@ -881,27 +1024,37 @@ pnm_writepaminit(struct pam * const pamP) { pm_error("maxval (%lu) passed to pnm_writepaminit() " "is greater than %u", pamP->maxval, PAM_OVERALL_MAXVAL); - if (pamP->len < PAM_STRUCT_SIZE(tuple_type)) + if (pamP->len < PAM_STRUCT_SIZE(tuple_type)) { tupleType = ""; - else + if (pamP->size >= PAM_STRUCT_SIZE(tuple_type)) + pamP->tuple_type[0] = '\0'; + } else tupleType = pamP->tuple_type; - if (pamP->len < PAM_STRUCT_SIZE(bytes_per_sample)) - pamP->len = PAM_STRUCT_SIZE(bytes_per_sample); pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); + + if (pamP->size >= PAM_STRUCT_SIZE(comment_p) && + pamP->len < PAM_STRUCT_SIZE(comment_p)) + pamP->comment_p = NULL; + + if (pamP->size >= PAM_STRUCT_SIZE(allocation_depth) && + pamP->len < PAM_STRUCT_SIZE(allocation_depth)) + pamP->allocation_depth = 0; + + interpretTupleType(pamP); + + pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane)); switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: - if (pm_plain_output) - pm_error("There is no plain version of PAM. -plain option " - "is not allowed"); + /* See explanation below of why we ignore 'pm_plain_output' here. */ fprintf(pamP->file, "P7\n"); writeComments(pamP); fprintf(pamP->file, "WIDTH %u\n", (unsigned)pamP->width); fprintf(pamP->file, "HEIGHT %u\n", (unsigned)pamP->height); fprintf(pamP->file, "DEPTH %u\n", pamP->depth); fprintf(pamP->file, "MAXVAL %lu\n", pamP->maxval); - if (!stripeq(tupleType, "")) + if (!pm_stripeq(tupleType, "")) fprintf(pamP->file, "TUPLTYPE %s\n", pamP->tuple_type); fprintf(pamP->file, "ENDHDR\n"); break; @@ -954,6 +1107,23 @@ pnm_writepaminit(struct pam * const pamP) { +/* EFFECT OF -plain WHEN WRITING PAM FORMAT: + + Before Netpbm 10.63 (June 2013), pnm_writepaminit() did a pm_error() here + if 'pm_plain_output' was set (i.e. the user said -plain). But this isn't + really logical, because -plain is a global option for the program and here + we are just writing one image. As a global option, -plain must be defined + to have effect where it makes sense and have no effect where it doesn't. + Note that a program that generates GIF just ignores -plain. Note also that + a program could conceivably generate both a PPM image and a PAM image. + + Note also how we handle the other a user can request plain format: the + 'plainformat' member of the PAM struct. In the case of PAM, we ignore that + member. +*/ + + + void pnm_checkpam(const struct pam * const pamP, enum pm_check_type const checkType, @@ -1046,19 +1216,107 @@ pnm_makearrayrgb(const struct pam * const pamP, +void +pnm_makerowrgba(const struct pam * const pamP, + tuple * const tuplerow) { +/*---------------------------------------------------------------------------- + Make the tuples 'tuplerow' the RGBA equivalent of what they are now, + which is described by *pamP. + + This means afterward, *pamP no longer correctly describes these tuples; + Caller must be sure to update *pamP it or not use it anymore. + + We fail if Caller did not supply enough allocated space in 'tuplerow' for + the extra planes (tuple allocation depth). +-----------------------------------------------------------------------------*/ + if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) { + pm_message("struct pam length %u is too small for pnm_makerowrgba(). " + "This function requires struct pam fields through " + "'opacity_plane'", pamP->len); + abort(); + } else { + if (!pamP->visual) + pm_error("Non-visual tuples given to pnm_addopacityrow()"); + + if (pamP->color_depth >= 3 && pamP->have_opacity) { + /* It's already in RGBA format. Leave it alone. */ + } else { + unsigned int col; + + if (allocationDepth(pamP) < 4) + pm_error("allocation depth %u passed to pnm_makerowrgba(). " + "Must be at least 4.", allocationDepth(pamP)); + + for (col = 0; col < pamP->width; ++col) { + tuple const thisTuple = tuplerow[col]; + thisTuple[PAM_TRN_PLANE] = + pamP->have_opacity ? thisTuple[pamP->opacity_plane] : + pamP->maxval; + + assert(PAM_RED_PLANE == 0); + thisTuple[PAM_BLU_PLANE] = thisTuple[0]; + thisTuple[PAM_GRN_PLANE] = thisTuple[0]; + } + } + } +} + + + +void +pnm_addopacityrow(const struct pam * const pamP, + tuple * const tuplerow) { +/*---------------------------------------------------------------------------- + Add an opacity plane to the tuples in 'tuplerow', if one isn't already + there. + + This means afterward, *pamP no longer correctly describes these tuples; + Caller must be sure to update *pamP it or not use it anymore. + + We fail if Caller did not supply enough allocated space in 'tuplerow' for + the extra plane (tuple allocation depth). +-----------------------------------------------------------------------------*/ + if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) { + pm_message("struct pam length %u is too small for pnm_makerowrgba(). " + "This function requires struct pam fields through " + "'opacity_plane'", pamP->len); + abort(); + } else { + if (!pamP->visual) + pm_error("Non-visual tuples given to pnm_addopacityrow()"); + + if (pamP->have_opacity) { + /* It already has opacity. Leave it alone. */ + } else { + unsigned int const opacityPlane = pamP->color_depth; + + unsigned int col; + + if (allocationDepth(pamP) < opacityPlane + 1) + pm_error("allocation depth %u passed to pnm_addopacityrow(). " + "Must be at least %u.", + allocationDepth(pamP), opacityPlane + 1); + + for (col = 0; col < pamP->width; ++col) + tuplerow[col][opacityPlane] = pamP->maxval; + } + } +} + + + void pnm_getopacity(const struct pam * const pamP, - bool * const haveOpacityP, + int * const haveOpacityP, unsigned int * const opacityPlaneP) { - /* Design note; If use of this information proliferates, we should - probably add it to struct pam as convenience values analogous to - bytes_per_sample. + /* Usage note: this is obsolete since we added 'have_opacity', etc. + to struct pam. */ - if (strcmp(pamP->tuple_type, "RGB_ALPHA") == 0) { + if (streq(pamP->tuple_type, "RGB_ALPHA")) { *haveOpacityP = TRUE; *opacityPlaneP = PAM_TRN_PLANE; - } else if (strcmp(pamP->tuple_type, "GRAYSCALE_ALPHA") == 0) { + } else if (streq(pamP->tuple_type, "GRAYSCALE_ALPHA")) { *haveOpacityP = TRUE; *opacityPlaneP = PAM_GRAY_TRN_PLANE; } else @@ -1067,6 +1325,67 @@ pnm_getopacity(const struct pam * const pamP, +tuple +pnm_backgroundtuple(struct pam * const pamP, + tuple ** const tuples) { +/*-------------------------------------------------------------------- + This function was copied from libpnm3.c's pnm_backgroundxel() and + modified to use tuples instead of xels. +----------------------------------------------------------------------*/ + tuple tuplePtr, bgtuple, ul, ur, ll, lr; + + /* Guess a good background value. */ + ul = tuples[0][0]; + ur = tuples[0][pamP->width-1]; + ll = tuples[pamP->height-1][0]; + lr = tuples[pamP->height-1][pamP->width-1]; + bgtuple = NULL; + + /* We first recognize three corners equal. If not, we look for any + two. If not, we just average all four. + */ + if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, ll)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, ur) && + pnm_tupleequal(pamP, ur, lr)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, ll) && + pnm_tupleequal(pamP, ll, lr)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ur, ll) && + pnm_tupleequal(pamP, ll, lr)) + tuplePtr = ur; + else if (pnm_tupleequal(pamP, ul, ur)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, ll)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, lr)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ur, ll)) + tuplePtr = ur; + else if (pnm_tupleequal(pamP, ur, lr)) + tuplePtr = ur; + else if (pnm_tupleequal(pamP, ll, lr)) + tuplePtr = ll; + else { + /* Reimplement libpnm3.c's mean4() but for tuples. */ + unsigned int plane; + bgtuple = pnm_allocpamtuple(pamP); + for (plane = 0; plane < pamP->depth; ++plane) + bgtuple[plane] = (ul[plane] + ur[plane] + ll[plane] + lr[plane]) / 4; + } + if (!bgtuple) { + unsigned int plane; + bgtuple = pnm_allocpamtuple(pamP); + for (plane = 0; plane < pamP->depth; ++plane) + bgtuple[plane] = tuplePtr[plane]; + } + + return bgtuple; +} + + + /*============================================================================= pm_system() Standard Input feeder and Standard Output accepter functions. =============================================================================*/ |