# HG changeset patch # User alex@thinkpad # Date 1386872498 -7200 # Node ID 518491f4fa3046733ec1f095dff8ce9b12cd3904 # Parent 2003390ed062ad333e0d15e4984fdc4e2de459bf Enhancement: softer "soft film" highlight handling 1) new curve formula, with higher slope near white point: (1 - 1/(1+a*x)) / (1 - 1/(1+a)) => little or no blown highlights at large EV 2) soft film is applied starting from -2 EV instead of 0 (the picture is developed linearly underexposed by 2 EV, then it's brightened back by the soft film curve) 3) a little refactoring in develop_linear needed for fixing a small bug in highlight recovery (without it you may get some pink highlights) diff -r 2003390ed062 -r 518491f4fa30 ufraw_developer.c --- a/ufraw_developer.c Sat Apr 20 00:36:54 2013 +0300 +++ b/ufraw_developer.c Thu Dec 12 20:21:38 2013 +0200 @@ -302,24 +302,6 @@ return hICC; } -/* Find a for which (1-exp(-a x)/(1-exp(-a)) has derivative b at x=0 */ -/* In other words, solve a/(1-exp(-a))==b */ -static double findExpCoeff(double b) -{ - double a, bg; - int try; - if (b <= 1) return 0; - if (b < 2) a = (b - 1) / 2; - else a = b; - bg = a / (1 - exp(-a)); - /* The limit on try is just to be sure there is no infinite loop. */ - for (try = 0; abs(bg - b) > 0.001 || try < 100; try++) { - a = a + (b - bg); - bg = a / (1 - exp(-a)); - } - return a; -} - static void developer_create_transform(developer_data *d, DeveloperMode mode) { if (!d->updateTransform) @@ -474,8 +456,17 @@ /* Handle the exposure normalization for Canon EOS cameras. */ if (conf->ExposureNorm > 0) exposure = (guint64)exposure * d->rgbMax / conf->ExposureNorm; - if (exposure >= 0x10000) d->restoreDetails = clip_details; - if (exposure <= 0x10000) clipHighlights = digital_highlights; + + /* film exposures are developed linearly at -2 EV and brightened by +2 EV using the film curve */ + /* at 0 EV (that is, -2 on the GUI), the film curve is linear, and below the formula doesn't make sense */ + unsigned exposure_limit_for_film = 0x10000 / 4; + if (exposure <= exposure_limit_for_film) clipHighlights = digital_highlights; + + /* highlight recovery for negative EV is only done when exposure is... well... negative */ + /* note that film exposures are developed linearly at -2 EV, so this threhold on the GUI becomes +2 EV */ + unsigned exposure_limit_for_negative_restore = (clipHighlights == film_highlights ? 0x10000 * 4 : 0x10000); + if (exposure >= exposure_limit_for_negative_restore) d->restoreDetails = clip_details; + /* Check if gamma curve data has changed. */ if (in->gamma != d->gamma || in->linear != d->linear || exposure != d->exposure || clipHighlights != d->clipHighlights || @@ -498,10 +489,12 @@ guint16 FilmCurve[0x10000]; if (d->clipHighlights == film_highlights) { /* Exposure is set by FilmCurve[]. - * Set initial slope to d->exposuse/0x10000 */ - double a = findExpCoeff((double)d->exposure / 0x10000); - for (i = 0; i < 0x10000; i++) FilmCurve[i] = - (1 - exp(-a * i / 0x10000)) / (1 - exp(-a)) * 0xFFFF; + * Set initial slope to d->exposure/0x10000 */ + double a = MAX((double)d->exposure * 4 / 0x10000 - 1, 1e-5); + for (i = 0; i < 0x10000; i++) { + double x = (double) i / 0x10000; + FilmCurve[i] = (1 - 1/(1+a*x)) / (1 - 1/(1+a)) * 0xFFFF; + } } else { /* digital highlights */ for (i = 0; i < 0x10000; i++) FilmCurve[i] = i; } @@ -861,6 +854,14 @@ unsigned c; gint64 tmppix[4]; gboolean clipped = FALSE; + unsigned exposure = d->exposure; + + if (d->clipHighlights == film_highlights) { + /* develop the image at -2 EV to recover highlights blown at 0 and small negative EV */ + /* the film curve will brighten the image back */ + exposure = 0x10000 / 4; + } + for (c = 0; c < d->colors; c++) { /* Set WB, normalizing tmppix[c]<0x10000 */ tmppix[c] = in[c]; @@ -872,12 +873,7 @@ } else { tmppix[c] = MIN(tmppix[c], d->max); } - /* We are counting on the fact that film_highlights - * and !clip_highlights cannot be set simultaneously. */ - if (d->clipHighlights == film_highlights) - tmppix[c] = tmppix[c] * 0x10000 / d->max; - else - tmppix[c] = tmppix[c] * d->exposure / d->max; + tmppix[c] = tmppix[c] * exposure / d->max; } if (clipped) { /* At this point a value of d->exposure in tmppix[c] corresponds @@ -885,7 +881,7 @@ * d->exposure * 0x10000 / d->max */ gint64 unclippedPix[3], clippedPix[3]; cond_apply_matrix(d, tmppix, unclippedPix); - for (c = 0; c < 3; c++) tmppix[c] = MIN(tmppix[c], d->exposure); + for (c = 0; c < 3; c++) tmppix[c] = MIN(tmppix[c], exposure); cond_apply_matrix(d, tmppix, clippedPix); if (d->restoreDetails == restore_lch_details) { float lch[3], clippedLch[3], unclippedLch[3];