diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2011-08-04 02:22:57 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2011-08-04 02:22:57 +0000 |
commit | be7a0f83c56b8ebe89f522efc4f0643d4818cd4f (patch) | |
tree | 4f1aae8316c4280339356b49a5a4f6d46a088a60 /editor | |
parent | 0bbc0d23d5ed7bdde9045a784525d141aa98d286 (diff) | |
download | netpbm-mirror-be7a0f83c56b8ebe89f522efc4f0643d4818cd4f.tar.gz netpbm-mirror-be7a0f83c56b8ebe89f522efc4f0643d4818cd4f.tar.xz netpbm-mirror-be7a0f83c56b8ebe89f522efc4f0643d4818cd4f.zip |
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
Diffstat (limited to 'editor')
-rw-r--r-- | editor/pamcomp.c | 262 |
1 files changed, 199 insertions, 63 deletions
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 @@ -548,6 +600,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, struct pam * const underlayPamP, @@ -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); |