From be7a0f83c56b8ebe89f522efc4f0643d4818cd4f Mon Sep 17 00:00:00 2001 From: giraffedata Date: Thu, 4 Aug 2011 02:22:57 +0000 Subject: pamcomp: retain opacity information from underlying image git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1526 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- doc/HISTORY | 2 + editor/pamcomp.c | 262 ++++++++++++++++++++++++++++++++++++++++++------------- lib/libpam.c | 210 ++++++++++++++++++++++++++++++++++++++++++-- lib/pam.h | 25 ++++++ 4 files changed, 429 insertions(+), 70 deletions(-) diff --git a/doc/HISTORY b/doc/HISTORY index c9524a93..d256e921 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -6,6 +6,8 @@ CHANGE HISTORY not yet BJH Release 10.56.00 + pamcomp: Retain opacity information from underlying image. + pnmtops: Add PBM fast path. Thanks Prophet of the Way . diff --git a/editor/pamcomp.c b/editor/pamcomp.c index afdf4451..78f30e9c 100644 --- a/editor/pamcomp.c +++ b/editor/pamcomp.c @@ -217,32 +217,65 @@ commonFormat(int const formatA, -static void +typedef enum { TT_BLACKANDWHITE, TT_GRAYSCALE, TT_RGB } BaseTupletype; + + + +static BaseTupletype commonTupletype(const char * const tupletypeA, - const char * const tupletypeB, - char * const tupletypeOut, - unsigned int const size) { + const char * const tupletypeB) { if (strneq(tupletypeA, "RGB", 3) || strneq(tupletypeB, "RGB", 3)) - strncpy(tupletypeOut, "RGB", size); + return TT_RGB; else if (strneq(tupletypeA, "GRAYSCALE", 9) || strneq(tupletypeB, "GRAYSCALE", 9)) - strncpy(tupletypeOut, "GRAYSCALE", size); + return TT_GRAYSCALE; else if (strneq(tupletypeA, "BLACKANDWHITE", 13) || strneq(tupletypeB, "BLACKANDWHITE", 13)) - strncpy(tupletypeOut, "BLACKANDWHITE", size); + return TT_BLACKANDWHITE; else /* Results are undefined for this case, so we do a hail Mary. */ - strncpy(tupletypeOut, tupletypeA, size); + return TT_RGB; } static void -determineOutputType(struct pam * const composedPamP, - struct pam * const underlayPamP, - struct pam * const overlayPamP) { +determineOutputTupleType(BaseTupletype const baseTupletype, + bool const underlayHaveOpacity, + char * const tupleType, + size_t const size) { + + char buffer[80]; + + switch (baseTupletype) { + case TT_BLACKANDWHITE: + STRSCPY(buffer, "RGB"); + break; + case TT_GRAYSCALE: + STRSCPY(buffer, "GRAYSCALE"); + break; + case TT_RGB: + STRSCPY(buffer, "RGB"); + break; + } + + if (underlayHaveOpacity) + STRSCAT(buffer, "_ALPHA"); + + strncpy(tupleType, buffer, size); +} + + + +static void +determineOutputType(const struct pam * const underlayPamP, + const struct pam * const overlayPamP, + struct pam * const composedPamP) { + + BaseTupletype const baseTupletype = + commonTupletype(underlayPamP->tuple_type, overlayPamP->tuple_type); composedPamP->height = underlayPamP->height; composedPamP->width = underlayPamP->width; @@ -250,22 +283,22 @@ determineOutputType(struct pam * const composedPamP, composedPamP->format = commonFormat(underlayPamP->format, overlayPamP->format); composedPamP->plainformat = FALSE; - commonTupletype(underlayPamP->tuple_type, overlayPamP->tuple_type, - composedPamP->tuple_type, - sizeof(composedPamP->tuple_type)); composedPamP->maxval = pm_lcm(underlayPamP->maxval, overlayPamP->maxval, 1, PNM_OVERALLMAXVAL); - if (streq(composedPamP->tuple_type, "RGB")) - composedPamP->depth = 3; - else if (streq(composedPamP->tuple_type, "GRAYSCALE")) - composedPamP->depth = 1; - else if (streq(composedPamP->tuple_type, "BLACKANDWHITE")) - composedPamP->depth = 1; - else - /* Results are undefined for this case, so we just do something safe */ - composedPamP->depth = MIN(underlayPamP->depth, overlayPamP->depth); + composedPamP->visual = true; + composedPamP->color_depth = (baseTupletype == TT_RGB ? 3 : 1); + composedPamP->have_opacity = underlayPamP->have_opacity; + composedPamP->opacity_plane = (baseTupletype == TT_RGB ? 3 : 1); + + composedPamP->depth = + (baseTupletype == TT_RGB ? 3 : 1) + + (underlayPamP->have_opacity ? 1 : 0); + + determineOutputTupleType(baseTupletype, underlayPamP->have_opacity, + composedPamP->tuple_type, + sizeof(composedPamP->tuple_type)); } @@ -381,8 +414,9 @@ composeComponents(sample const compA, sample const maxval, enum sampleScale const sampleScale) { /*---------------------------------------------------------------------------- - Compose a single component of each of two pixels, with 'distrib' being + Compose a single color component of each of two pixels, with 'distrib' being the fraction of 'compA' in the result, 1-distrib the fraction of 'compB'. + Note that this does not apply to an opacity component. 'sampleScale' tells in what domain the 'distrib' fraction applies: brightness or light intensity (gamma-adjusted or not). @@ -426,8 +460,6 @@ overlayPixel(tuple const overlayTuple, struct pam * const underlayPamP, tuplen const alphaTuplen, bool const invertAlpha, - bool const overlayHasOpacity, - unsigned int const opacityPlane, tuple const composedTuple, struct pam * const composedPamP, float const masterOpacity, @@ -446,8 +478,7 @@ overlayPixel(tuple const overlayTuple, underlay pixel shows through and 0 means the overlay pixel is invisible and the underlay pixel shows through in full force. - 'overlayHasOpacity' means that 'overlayTuple' has an opacity component, - in particular its 'opacityPlane' sample. + if *overlayPamP may indicate that 'overlayTuple' has an opacity component. 'alphaTuplen' is a normalized tuple whose first sample is the opacity factor, except that iff 'invertAlpha' is true, it is a transparency @@ -467,9 +498,9 @@ overlayPixel(tuple const overlayTuple, overlayWeight = masterOpacity; /* initial value */ - if (overlayHasOpacity) + if (overlayPamP->have_opacity) overlayWeight *= (float) - overlayTuple[opacityPlane] / overlayPamP->maxval; + overlayTuple[overlayPamP->opacity_plane] / overlayPamP->maxval; if (alphaTuplen) { float const alphaval = @@ -480,31 +511,48 @@ overlayPixel(tuple const overlayTuple, { unsigned int plane; - for (plane = 0; plane < composedPamP->depth; ++plane) + for (plane = 0; plane < composedPamP->color_depth; ++plane) composedTuple[plane] = composeComponents(overlayTuple[plane], underlayTuple[plane], overlayWeight, composedPamP->maxval, sampleScale); + + if (composedPamP->have_opacity) { + /* copy opacity from underlay to output */ + assert(underlayPamP->have_opacity); + composedTuple[composedPamP->opacity_plane] = + underlayTuple[underlayPamP->opacity_plane]; + } } } static void -adaptRowToOutputFormat(struct pam * const inpamP, - tuple * const tuplerow, - struct pam * const outpamP) { +adaptRowFormat(struct pam * const inpamP, + struct pam * const outpamP, + tuple * const tuplerow) { /*---------------------------------------------------------------------------- Convert the row in 'tuplerow', which is in a format described by *inpamP, to the format described by *outpamP. 'tuplerow' must have enough allocated depth to do this. -----------------------------------------------------------------------------*/ + assert(outpamP->visual); + assert(inpamP->visual); + pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval); - if (strneq(outpamP->tuple_type, "RGB", 3)) - pnm_makerowrgb(inpamP, tuplerow); + if (outpamP->color_depth == 3) { + if (outpamP->have_opacity) + pnm_makerowrgba(inpamP, tuplerow); + else + pnm_makerowrgb(inpamP, tuplerow); + } else { + if (outpamP->have_opacity) + pnm_addopacityrow(inpamP, tuplerow); + } } @@ -515,16 +563,21 @@ composeRow(int const originleft, struct pam * const overlayPamP, bool const invertAlpha, float const masterOpacity, - bool const overlayHasOpacity, - unsigned int const opacityPlane, struct pam * const composedPamP, enum sampleScale const sampleScale, const tuple * const underlayTuplerow, const tuple * const overlayTuplerow, const tuplen * const alphaTuplerown, tuple * const composedTuplerow) { +/*---------------------------------------------------------------------------- + Create a row of tuples ('composedTupleRow') which is the composition of + row 'overlayTupleRow' laid over row 'underlayTupleRow', starting at + column 'originLeft'. + *underlayPamP and *overlayPamP describe the respective tuple rows. +-----------------------------------------------------------------------------*/ unsigned int col; + for (col = 0; col < composedPamP->width; ++col) { int const ovlcol = col - originleft; @@ -535,7 +588,6 @@ composeRow(int const originleft, overlayPixel(overlayTuplerow[ovlcol], overlayPamP, underlayTuplerow[col], underlayPamP, alphaTuplen, invertAlpha, - overlayHasOpacity, opacityPlane, composedTuplerow[col], composedPamP, masterOpacity, sampleScale); } else @@ -547,6 +599,55 @@ composeRow(int const originleft, +static void +determineInputAdaptations(const struct pam * const underlayPamP, + const struct pam * const overlayPamP, + const struct pam * const composedPamP, + struct pam * const adaptUnderlayPamP, + struct pam * const adaptOverlayPamP) { +/*---------------------------------------------------------------------------- + For easy of computation, this program reads a tuple row from one of the + input files, then transforms it something similar to the format of the + eventual output tuple row. E.g. if the input is grayscale and the + output color, it converts the depth 1 row read from the file to a depth + 3 row for use in computations. + + This function determines what the result of that transformation should be. + It's not as simple as it sounds because of opacity. The overlay may have + an opacity plane that has to be kept for the computations, while the output + has no opacity plane. + + Our output PAMs are meaningless except in the fields that pertain to a + row of tuples. E.g. the file descriptor and image height members are + meaningless. +-----------------------------------------------------------------------------*/ + /* We make the underlay row identical to the composed (output) row, + except for its width. + */ + + *adaptUnderlayPamP = *composedPamP; + adaptUnderlayPamP->width = underlayPamP->width; + + /* Same for the overlay row, except that it retains is original + opacity. + */ + + adaptOverlayPamP->width = overlayPamP->width; + adaptOverlayPamP->tuple_type[0] = '\0'; /* a hack; this doesn't matter */ + adaptOverlayPamP->visual = true; + adaptOverlayPamP->color_depth = composedPamP->color_depth; + adaptOverlayPamP->have_opacity = overlayPamP->have_opacity; + adaptOverlayPamP->opacity_plane = composedPamP->color_depth; + adaptOverlayPamP->depth = + composedPamP->color_depth + + (overlayPamP->have_opacity ? 1 : 0); + adaptOverlayPamP->maxval = composedPamP->maxval; + adaptOverlayPamP->bytes_per_sample = composedPamP->bytes_per_sample; + adaptOverlayPamP->allocation_depth = overlayPamP->allocation_depth; +} + + + static void composite(int const originleft, int const origintop, @@ -584,12 +685,10 @@ composite(int const originleft, int overlayRow; /* NB may be negative */ tuple * composedTuplerow; tuple * underlayTuplerow; + struct pam adaptUnderlayPam; tuple * overlayTuplerow; + struct pam adaptOverlayPam; tuplen * alphaTuplerown; - bool overlayHasOpacity; - unsigned int opacityPlane; - - pnm_getopacity(overlayPamP, &overlayHasOpacity, &opacityPlane); composedTuplerow = pnm_allocpamrow(composedPamP); underlayTuplerow = pnm_allocpamrow(underlayPamP); @@ -599,6 +698,9 @@ composite(int const originleft, else alphaTuplerown = NULL; + determineInputAdaptations(underlayPamP, overlayPamP, composedPamP, + &adaptUnderlayPam, &adaptOverlayPam); + pnm_writepaminit(composedPamP); assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */ @@ -610,15 +712,13 @@ composite(int const originleft, if (overlayRow >= 0 && overlayRow < overlayPamP->height) { pnm_readpamrow(overlayPamP, overlayTuplerow); - adaptRowToOutputFormat(overlayPamP, overlayTuplerow, composedPamP); + adaptRowFormat(overlayPamP, &adaptOverlayPam, overlayTuplerow); if (alphaPamP) pnm_readpamrown(alphaPamP, alphaTuplerown); } if (underlayRow >= 0 && underlayRow < underlayPamP->height) { pnm_readpamrow(underlayPamP, underlayTuplerow); - adaptRowToOutputFormat(underlayPamP, underlayTuplerow, - composedPamP); - + adaptRowFormat(underlayPamP, &adaptUnderlayPam, underlayTuplerow); if (underlayRow < origintop || underlayRow >= origintop + overlayPamP->height) { @@ -626,9 +726,9 @@ composite(int const originleft, pnm_writepamrow(composedPamP, underlayTuplerow); } else { - composeRow(originleft, underlayPamP, overlayPamP, - invertAlpha, masterOpacity, overlayHasOpacity, - opacityPlane, composedPamP, sampleScale, + composeRow(originleft, &adaptUnderlayPam, &adaptOverlayPam, + invertAlpha, masterOpacity, + composedPamP, sampleScale, underlayTuplerow, overlayTuplerow, alphaTuplerown, composedTuplerow); @@ -645,6 +745,30 @@ composite(int const originleft, +static void +initAlphaFile(struct cmdlineInfo const cmdline, + struct pam * const overlayPamP, + FILE ** const filePP, + struct pam * const pamP) { + + FILE * fileP; + + if (cmdline.alphaFilespec) { + fileP = pm_openr(cmdline.alphaFilespec); + pamP->comment_p = NULL; + pnm_readpaminit(fileP, pamP, PAM_STRUCT_SIZE(opacity_plane)); + + if (overlayPamP->width != pamP->width || + overlayPamP->height != pamP->height) + pm_error("Opacity map and overlay image are not the same size"); + } else + fileP = NULL; + + *filePP = fileP; +} + + + int main(int argc, const char *argv[]) { @@ -663,34 +787,46 @@ main(int argc, const char *argv[]) { parseCommandLine(argc, argv, &cmdline); overlayFileP = pm_openr(cmdline.overlayFilespec); + + overlayPam.comment_p = NULL; pnm_readpaminit(overlayFileP, &overlayPam, - PAM_STRUCT_SIZE(allocation_depth)); - if (cmdline.alphaFilespec) { - alphaFileP = pm_openr(cmdline.alphaFilespec); - pnm_readpaminit(alphaFileP, &alphaPam, - PAM_STRUCT_SIZE(allocation_depth)); + PAM_STRUCT_SIZE(opacity_plane)); - if (overlayPam.width != alphaPam.width || - overlayPam.height != overlayPam.height) - pm_error("Opacity map and overlay image are not the same size"); - } else - alphaFileP = NULL; + if (overlayPam.len < PAM_STRUCT_SIZE(opacity_plane)) + pm_error("Libnetpbm is too old. This program requires libnetpbm from " + "Netpbm 10.56 (September 2011) or newer"); + + if (!overlayPam.visual) + pm_error("Overlay image has tuple type '%s', which is not a " + "standard visual type. We don't know how to compose.", + overlayPam.tuple_type); + + initAlphaFile(cmdline, &overlayPam, &alphaFileP, &alphaPam); underlayFileP = pm_openr(cmdline.underlyingFilespec); + underlayPam.comment_p = NULL; pnm_readpaminit(underlayFileP, &underlayPam, - PAM_STRUCT_SIZE(allocation_depth)); + PAM_STRUCT_SIZE(opacity_plane)); + + assert(underlayPam.len >= PAM_STRUCT_SIZE(opacity_plane)); + if (!overlayPam.visual) + pm_error("Overlay image has tuple type '%s', which is not a " + "standard visual type. We don't know how to compose.", + overlayPam.tuple_type); + computeOverlayPosition(underlayPam.width, underlayPam.height, overlayPam.width, overlayPam.height, cmdline, &originLeft, &originTop); - composedPam.size = sizeof(composedPam); + composedPam.size = PAM_STRUCT_SIZE(opacity_plane); composedPam.len = PAM_STRUCT_SIZE(allocation_depth); composedPam.allocation_depth = 0; composedPam.file = pm_openw(cmdline.outputFilespec); + composedPam.comment_p = NULL; - determineOutputType(&composedPam, &underlayPam, &overlayPam); + determineOutputType(&underlayPam, &overlayPam, &composedPam); pnm_setminallocationdepth(&underlayPam, composedPam.depth); pnm_setminallocationdepth(&overlayPam, composedPam.depth); diff --git a/lib/libpam.c b/lib/libpam.c index 8af2c04a..9ae33f08 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -70,6 +70,7 @@ pamCommentP(const struct pam * const pamP) { } + static void validateComputableSize(struct pam * const pamP) { /*---------------------------------------------------------------------------- @@ -764,6 +765,107 @@ pnm_bytespersample(sample const maxval) { +static void +validateMinDepth(const struct pam * const pamP, + unsigned int const minDepth) { + + 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 implied by + the pamP->tuple_type 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 (STRSEQ(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 (STRSEQ(tupleType, "GRAYSCALE")) { + visual = true; + colorDepth = 1; + haveOpacity = false; + } else if (STRSEQ(tupleType, "GRAYSCALE_ALPHA")) { + visual = true; + colorDepth = 1; + haveOpacity = true; + opacityPlane = PAM_GRAY_TRN_PLANE; + validateMinDepth(pamP, 2); + } else if (STRSEQ(tupleType, "RGB")) { + visual = true; + colorDepth = 3; + haveOpacity = false; + validateMinDepth(pamP, 3); + } else if (STRSEQ(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->have_opacity = haveOpacity; +} + + + void pnm_readpaminit(FILE * const file, struct pam * const pamP, @@ -829,6 +931,8 @@ pnm_readpaminit(FILE * const file, pamP->plainformat = FALSE; /* See below for complex explanation of why this is FALSE. */ + interpretTupleType(pamP); + validateComputableSize(pamP); } @@ -916,14 +1020,18 @@ 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); + + interpretTupleType(pamP); + + pamP->len = MIN(pamP->size, sizeof(struct pam)); switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: @@ -1081,14 +1189,102 @@ 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, 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 'haveOpacity', etc. + to struct pam. */ if (streq(pamP->tuple_type, "RGB_ALPHA")) { *haveOpacityP = TRUE; diff --git a/lib/pam.h b/lib/pam.h index 4fa478ea..42d1dc75 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -109,6 +109,23 @@ struct pam { libnetpbm does not return comments and does not allocate any storage. */ + int visual; /* boolean */ + /* tuple_type is one of the PAM-defined tuple types for visual + images ("GRAYSCALE", "RGB_ALPHA", etc.). + */ + unsigned int color_depth; + /* Number of color planes (i.e. 'depth', but without transparency). + The color planes are the lowest numbered ones. Meaningless if + 'visual' is false. + */ + int have_opacity; /* boolean */ + /* The tuples have an opacity (transparency, alpha) plane. + Meaningless if 'visual' is false. + */ + unsigned int opacity_plane; + /* The plane number of the opacity plane; meaningless if + 'haveOpacity' is false or 'visual' is false. + */ }; #define PAM_HAVE_ALLOCATION_DEPTH 1 @@ -274,6 +291,14 @@ void pnm_makearrayrgb(const struct pam * const pamP, tuple ** const tuples); +void +pnm_makerowrgba(const struct pam * const pamP, + tuple * const tuplerow); + +void +pnm_addopacityrow(const struct pam * const pamP, + tuple * const tuplerow); + void pnm_getopacity(const struct pam * const pamP, int * const haveOpacityP, -- cgit 1.4.1