static sample composeComponents(sample const compA, sample const compB, float const distrib, float const aFactor, float const composedFactor, sample const maxval, enum sampleScale const sampleScale) { /*---------------------------------------------------------------------------- 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). 'aFactor' is a factor in [0,1] to apply to 'compA' first. 'composedFactor' is a factor to apply to the result. See above for explanation of why 'aFactor' and 'composedFactor' are useful. The inputs and result are based on a maxval of 'maxval'. Note that while 'distrib' in the straightforward case is always in [0,1], it can in fact be negative or greater than 1. We clip the result as required to return a legal sample value. -----------------------------------------------------------------------------*/ sample retval; if (fabs(distrib) > .999 && aFactor > .999 && composedFactor > .999) /* Fast path for common case */ retval = compA; else { if (sampleScale == INTENSITY_SAMPLE) { sample const mix = ROUNDU(compA * aFactor * distrib + compB * (1.0 - distrib)); retval = MIN(maxval, MAX(0, mix)); } else { float const compANormalized = (float)compA/maxval; float const compBNormalized = (float)compB/maxval; float const compALinear = pm_ungamma709(compANormalized); float const compBLinear = pm_ungamma709(compBNormalized); float const compALinearAdj = compALinear * aFactor; float const mix = compALinearAdj * distrib + compBLinear * (1.0 - distrib) * composedFactor; sample const sampleValue = ROUNDU(pm_gamma709(mix) * maxval); retval = MIN(maxval, MAX(0, sampleValue)); } } return retval; }
static sample composeComponents(sample const compA, sample const compB, float const distrib, sample const maxval, enum sampleScale const sampleScale) { /*---------------------------------------------------------------------------- Compose a single component of each of two pixels, with 'distrib' being the fraction of 'compA' in the result, 1-distrib the fraction of 'compB'. The inputs and result are based on a maxval of 'maxval'. Note that while 'distrib' in the straightforward case is always in [0,1], it can in fact be negative or greater than 1. We clip the result as required to return a legal sample value. -----------------------------------------------------------------------------*/ sample retval; if (fabs(1.0-distrib) < .001) /* Fast path for common case */ retval = compA; else { if (sampleScale == INTENSITY_SAMPLE) { sample const mix = ROUNDU(compA * distrib + compB * (1.0 - distrib)); retval = MIN(maxval, MAX(0, mix)); } else { float const compANormalized = (float)compA/maxval; float const compBNormalized = (float)compB/maxval; float const compALinear = pm_ungamma709(compANormalized); float const compBLinear = pm_ungamma709(compBNormalized); float const mix = compALinear * distrib + compBLinear * (1.0 - distrib); sample const sampleValue = ROUNDU(pm_gamma709(mix) * maxval); retval = MIN(maxval, MAX(0, sampleValue)); } } return retval; }
static struct converter createClusterConverter(struct pam * const graypamP, enum ditherType const ditherType, unsigned int const radius) { /* TODO: We create a floating point normalized, gamma-adjusted dither matrix from the old integer dither matrices that were developed for use with integer arithmetic. We really should just change the literal values in dither.h instead of computing the matrix from the integer literal values here. */ int const clusterNormalizer = radius * radius * 2; unsigned int const diameter = 2 * radius; struct converter converter; struct clusterState * stateP; unsigned int row; converter.cols = graypamP->width; converter.convertRow = &clusterConvertRow; converter.destroy = &clusterDestroy; MALLOCVAR_NOFAIL(stateP); stateP->radius = radius; MALLOCARRAY_NOFAIL(stateP->clusterMatrix, diameter); for (row = 0; row < diameter; ++row) { unsigned int col; MALLOCARRAY_NOFAIL(stateP->clusterMatrix[row], diameter); for (col = 0; col < diameter; ++col) { switch (ditherType) { case DT_REGULAR: switch (radius) { case 8: stateP->clusterMatrix[row][col] = pm_gamma709((float)dither8[row][col] / 256); break; default: pm_error("INTERNAL ERROR: invalid radius"); } break; case DT_CLUSTER: { int val; switch (radius) { case 3: val = cluster3[row][col]; break; case 4: val = cluster4[row][col]; break; case 8: val = cluster8[row][col]; break; default: pm_error("INTERNAL ERROR: invalid radius"); } stateP->clusterMatrix[row][col] = pm_gamma709((float)val / clusterNormalizer); } break; } } } converter.stateP = stateP; return converter; }