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 void computeMap(const unsigned int * const lumahist, xelval const maxval, unsigned int const pixelCount, gray * const lumamap) { /* Calculate initial histogram equalization curve. */ unsigned int i; unsigned int pixsum; xelval maxluma; for (i = 0, pixsum = 0; i <= maxval; ++i) { /* With 16 bit grays, the following calculation can overflow a 32 bit long. So, we do it in floating point. */ lumamap[i] = ROUNDU((((double) pixsum * maxval)) / pixelCount); pixsum += lumahist[i]; } findMaxLuma(lumahist, maxval, &maxluma); { double const lscale = (double)maxval / ((lumahist[maxluma] > 0) ? (double) lumamap[maxluma] : (double) maxval); unsigned int i; /* Normalize so that the brightest pixels are set to maxval. */ for (i = 0; i <= maxval; ++i) lumamap[i] = MIN(maxval, ROUNDU(lumamap[i] * lscale)); } }
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 void remap(xel ** const xels, unsigned int const cols, unsigned int const rows, xelval const maxval, int const format, bool const monoOnly, const gray * const lumamap) { /*---------------------------------------------------------------------------- Update the array 'xels' to have the new intensities. -----------------------------------------------------------------------------*/ switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: { unsigned int row; for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { xel const thisXel = xels[row][col]; if (monoOnly && PPM_ISGRAY(thisXel)) { /* Leave this pixel alone */ } else { struct hsv hsv; xelval iv; hsv = ppm_hsv_from_color(thisXel, maxval); iv = MIN(maxval, ROUNDU(hsv.v * maxval)); hsv.v = MIN(1.0, ((double) lumamap[iv]) / ((double) maxval)); xels[row][col] = ppm_color_from_hsv(hsv, maxval); } } } } break; case PBM_TYPE: case PGM_TYPE: { unsigned int row; for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) PNM_ASSIGN1(xels[row][col], lumamap[PNM_GET1(xels[row][col])]); } } break; } }
static void countComp(xelval const value, xelval const startval, xelval const endval, unsigned int const histWidth, unsigned int * const hist) { double const hscale = (float)(histWidth-1) / (endval - startval - 1); if (value >= startval && value < endval) { unsigned int const bin = ROUNDU((value-startval) * hscale); assert(bin < histWidth); ++hist[bin]; } }
pixel ppm_color_from_hsv(struct hsv const hsv, pixval const maxval) { pixel retval; double R, G, B; if (hsv.s == 0) { R = hsv.v; G = hsv.v; B = hsv.v; } else { unsigned int const sectorSize = 60; /* Color wheel is divided into six 60 degree sectors. */ unsigned int const sector = (hsv.h/sectorSize); /* The sector in which our color resides. Value is in 0..5 */ double const f = (hsv.h - sector*sectorSize)/60; /* The fraction of the way the color is from one side of our sector to the other side, going clockwise. Value is in [0, 1). */ double const m = (hsv.v * (1 - hsv.s)); double const n = (hsv.v * (1 - (hsv.s * f))); double const k = (hsv.v * (1 - (hsv.s * (1 - f)))); switch (sector) { case 0: R = hsv.v; G = k; B = m; break; case 1: R = n; G = hsv.v; B = m; break; case 2: R = m; G = hsv.v; B = k; break; case 3: R = m; G = n; B = hsv.v; break; case 4: R = k; G = m; B = hsv.v; break; case 5: R = hsv.v; G = m; B = n; break; default: pm_error("Invalid H value passed to color_from_HSV: %f", hsv.h); } } PPM_ASSIGN(retval, ROUNDU(R * maxval), ROUNDU(G * maxval), ROUNDU(B * maxval)); return retval; }