From 46327259e224ca838420448497e0460b28bff029 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 5 Apr 2018 11:22:05 +0200 Subject: [PATCH] added checkbox to let the user decide whether to clamp or not --- rtdata/languages/default | 3 ++ rtengine/improcfun.cc | 60 ++++++++++++++++++++-------------------- rtengine/procparams.cc | 8 ++++-- rtengine/procparams.h | 1 + rtgui/paramsedited.cc | 6 ++++ rtgui/paramsedited.h | 1 + rtgui/tonecurve.cc | 23 +++++++++++++++ rtgui/tonecurve.h | 3 ++ 8 files changed, 73 insertions(+), 32 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 3621596f3..bc0dbe4e7 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -724,6 +724,7 @@ HISTORY_MSG_491;White Balance HISTORY_MSG_492;RGB Curves HISTORY_MSG_493;L*a*b* Adjustments HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction +HISTORY_MSG_CLAMPOOG;Out-of-gamut colours clipping HISTORY_MSG_HISTMATCHING;Auto-matched Tone Curve HISTORY_MSG_LOCALCONTRAST_AMOUNT;Local Contrast - Amount HISTORY_MSG_LOCALCONTRAST_DARKNESS;Local Contrast - Darkness @@ -1552,6 +1553,8 @@ TP_EXPOSURE_AUTOLEVELS;Auto Levels TP_EXPOSURE_AUTOLEVELS_TIP;Toggles execution of Auto Levels to automatically set Exposure slider values based on an image analysis.\nEnables Highlight Reconstruction if necessary. TP_EXPOSURE_BLACKLEVEL;Black TP_EXPOSURE_BRIGHTNESS;Lightness +TP_EXPOSURE_CLAMPOOG;Clip out-of-gamut colours +TP_EXPOSURE_CLAMPOOG_TOOLTIP;Clip coulours that are outside the gamut of the current working space early in the pipeline. Turning this off allows to produce 32-bit TIFFs with values that exceed the 0-1 range, but might lead to artifacts. Under normal circumstances, this should be left enabled. TP_EXPOSURE_CLIP;Clip % TP_EXPOSURE_CLIP_TIP;The fraction of pixels to be clipped in Auto Levels operation. TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight compression diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index a49d00bdd..f47eb8146 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -3750,22 +3750,6 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } - for (int i = istart, ti = 0; i < tH; i++, ti++) { - for (int j = jstart, tj = 0; j < tW; j++, tj++) { - // clip out of gamut colors, without distorting colour too bad - float r = std::max(rtemp[ti * TS + tj], 0.f); - float g = std::max(gtemp[ti * TS + tj], 0.f); - float b = std::max(btemp[ti * TS + tj], 0.f); - - if (OOG(r) || OOG(g) || OOG(b)) { - filmlike_clip (&r, &g, &b); - } - rtemp[ti * TS + tj] = r; - gtemp[ti * TS + tj] = g; - btemp[ti * TS + tj] = b; - } - } - highlightToneCurve(hltonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS, exp_scale, comp, hlrange); shadowToneCurve(shtonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS); @@ -3773,21 +3757,37 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer dcpProf->step2ApplyTile (rtemp, gtemp, btemp, tW - jstart, tH - istart, TS, asIn); } - // for (int i = istart, ti = 0; i < tH; i++, ti++) { - // for (int j = jstart, tj = 0; j < tW; j++, tj++) { - // // clip out of gamut colors, without distorting colour too bad - // float r = std::max(rtemp[ti * TS + tj], 0.f); - // float g = std::max(gtemp[ti * TS + tj], 0.f); - // float b = std::max(btemp[ti * TS + tj], 0.f); + if (params->toneCurve.clampOOG) { + for (int i = istart, ti = 0; i < tH; i++, ti++) { + for (int j = jstart, tj = 0; j < tW; j++, tj++) { + // clip out of gamut colors, without distorting colour too bad + float r = std::max(rtemp[ti * TS + tj], 0.f); + float g = std::max(gtemp[ti * TS + tj], 0.f); + float b = std::max(btemp[ti * TS + tj], 0.f); - // if (OOG(r) || OOG(g) || OOG(b)) { - // filmlike_clip (&r, &g, &b); - // } - // rtemp[ti * TS + tj] = r; - // gtemp[ti * TS + tj] = g; - // btemp[ti * TS + tj] = b; - // } - // } + if (OOG(r) || OOG(g) || OOG(b)) { + filmlike_clip(&r, &g, &b); + } + rtemp[ti * TS + tj] = r; + gtemp[ti * TS + tj] = g; + btemp[ti * TS + tj] = b; + } + } + } else { + for (int i = istart, ti = 0; i < tH; i++, ti++) { + for (int j = jstart, tj = 0; j < tW; j++, tj++) { + // clip out of gamut colors, without distorting colour too bad + float r = std::max(rtemp[ti * TS + tj], 0.f); + float g = std::max(gtemp[ti * TS + tj], 0.f); + float b = std::max(btemp[ti * TS + tj], 0.f); + + if (OOG(max(r, g, b)) && !OOG(min(r, g, b))) { + filmlike_clip(&r, &g, &b); + } + setUnlessOOG(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], r, g, b); + } + } + } if (histToneCurveThr) { for (int i = istart, ti = 0; i < tH; i++, ti++) { diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 0a87d9614..eb8f29d07 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -328,7 +328,8 @@ ToneCurveParams::ToneCurveParams() : shcompr(50), hlcompr(0), hlcomprthresh(33), - histmatching(false) + histmatching(false), + clampOOG(true) { } @@ -351,7 +352,8 @@ bool ToneCurveParams::operator ==(const ToneCurveParams& other) const && shcompr == other.shcompr && hlcompr == other.hlcompr && hlcomprthresh == other.hlcomprthresh - && histmatching == other.histmatching; + && histmatching == other.histmatching + && clampOOG == other.clampOOG; } bool ToneCurveParams::operator !=(const ToneCurveParams& other) const @@ -2753,6 +2755,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->toneCurve.hlcomprthresh, "Exposure", "HighlightComprThreshold", toneCurve.hlcomprthresh, keyFile); saveToKeyfile(!pedited || pedited->toneCurve.shcompr, "Exposure", "ShadowCompr", toneCurve.shcompr, keyFile); saveToKeyfile(!pedited || pedited->toneCurve.histmatching, "Exposure", "HistogramMatching", toneCurve.histmatching, keyFile); + saveToKeyfile(!pedited || pedited->toneCurve.clampOOG, "Exposure", "ClampOOG", toneCurve.clampOOG, keyFile); // Highlight recovery saveToKeyfile(!pedited || pedited->toneCurve.hrenabled, "HLRecovery", "Enabled", toneCurve.hrenabled, keyFile); @@ -3531,6 +3534,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Exposure", "Curve2", pedited, toneCurve.curve2, pedited->toneCurve.curve2); } assignFromKeyfile(keyFile, "Exposure", "HistogramMatching", pedited, toneCurve.histmatching, pedited->toneCurve.histmatching); + assignFromKeyfile(keyFile, "Exposure", "ClampOOG", pedited, toneCurve.clampOOG, pedited->toneCurve.clampOOG); } if (keyFile.has_group ("HLRecovery")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index b158d7d1f..2cfc777b8 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -281,6 +281,7 @@ struct ToneCurveParams { int hlcompr; // Highlight Recovery's compression int hlcomprthresh; // Highlight Recovery's threshold bool histmatching; // histogram matching + bool clampOOG; // clamp out of gamut colours ToneCurveParams(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 868507ad0..cf61c8aad 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -50,6 +50,7 @@ void ParamsEdited::set (bool v) toneCurve.hrenabled = v; toneCurve.method = v; toneCurve.histmatching = v; + toneCurve.clampOOG = v; retinex.cdcurve = v; retinex.mapcurve = v; retinex.cdHcurve = v; @@ -610,6 +611,7 @@ void ParamsEdited::initFrom (const std::vector toneCurve.hrenabled = toneCurve.hrenabled && p.toneCurve.hrenabled == other.toneCurve.hrenabled; toneCurve.method = toneCurve.method && p.toneCurve.method == other.toneCurve.method; toneCurve.histmatching = toneCurve.histmatching && p.toneCurve.histmatching == other.toneCurve.histmatching; + toneCurve.clampOOG = toneCurve.clampOOG && p.toneCurve.clampOOG == other.toneCurve.clampOOG; retinex.cdcurve = retinex.cdcurve && p.retinex.cdcurve == other.retinex.cdcurve; retinex.mapcurve = retinex.mapcurve && p.retinex.mapcurve == other.retinex.mapcurve; retinex.cdHcurve = retinex.cdHcurve && p.retinex.cdHcurve == other.retinex.cdHcurve; @@ -1210,6 +1212,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.toneCurve.histmatching = mods.toneCurve.histmatching; } + if (toneCurve.clampOOG) { + toEdit.toneCurve.clampOOG = mods.toneCurve.clampOOG; + } + if (retinex.enabled) { toEdit.retinex.enabled = mods.retinex.enabled; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 12c8ee86e..0b73d7daf 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -54,6 +54,7 @@ public: bool hrenabled; bool method; bool histmatching; + bool clampOOG; }; class RetinexParamsEdited diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc index 4b50c97cc..697944400 100644 --- a/rtgui/tonecurve.cc +++ b/rtgui/tonecurve.cc @@ -32,6 +32,7 @@ ToneCurve::ToneCurve () : FoldableToolPanel(this, "tonecurve", M("TP_EXPOSURE_LA auto m = ProcEventMapper::getInstance(); EvHistMatching = m->newEvent(AUTOEXP, "HISTORY_MSG_HISTMATCHING"); EvHistMatchingBatch = m->newEvent(M_VOID, "HISTORY_MSG_HISTMATCHING"); + EvClampOOG = m->newEvent(RGBCURVE, "HISTORY_MSG_CLAMPOOG"); CurveListener::setMulti(true); @@ -39,6 +40,13 @@ ToneCurve::ToneCurve () : FoldableToolPanel(this, "tonecurve", M("TP_EXPOSURE_LA bottomMilestones.push_back( GradientMilestone(0., 0., 0., 0.) ); bottomMilestones.push_back( GradientMilestone(1., 1., 1., 1.) ); +//----------- OOG clamping ---------------------------------- + clampOOG = Gtk::manage(new Gtk::CheckButton(M("TP_EXPOSURE_CLAMPOOG"))); + clampOOG->set_tooltip_markup(M("TP_EXPOSURE_CLAMPOOG_TOOLTIP")); + pack_start(*clampOOG); + pack_start (*Gtk::manage (new Gtk::HSeparator())); + clampOOG->signal_toggled().connect(sigc::mem_fun(*this, &ToneCurve::clampOOGChanged)); + //----------- Auto Levels ---------------------------------- abox = Gtk::manage (new Gtk::HBox ()); abox->set_spacing (10); @@ -236,6 +244,7 @@ void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) toneCurveMode2->set_active(rtengine::toUnderlying(pp->toneCurve.curveMode2)); histmatching->set_active(pp->toneCurve.histmatching); + clampOOG->set_active(pp->toneCurve.clampOOG); if (pedited) { expcomp->setEditedState (pedited->toneCurve.expcomp ? Edited : UnEdited); @@ -261,6 +270,7 @@ void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) } histmatching->set_inconsistent(!pedited->toneCurve.histmatching); + clampOOG->set_inconsistent(!pedited->toneCurve.clampOOG); } enaconn.block (true); @@ -357,6 +367,7 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) } pp->toneCurve.histmatching = histmatching->get_active(); + pp->toneCurve.clampOOG = clampOOG->get_active(); if (pedited) { pedited->toneCurve.expcomp = expcomp->getEditedState (); @@ -376,6 +387,7 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) pedited->toneCurve.method = method->get_active_row_number() != 4; pedited->toneCurve.hrenabled = !hrenabled->get_inconsistent(); pedited->toneCurve.histmatching = !histmatching->get_inconsistent(); + pedited->toneCurve.clampOOG = !clampOOG->get_inconsistent(); } pp->toneCurve.hrenabled = hrenabled->get_active(); @@ -443,6 +455,17 @@ void ToneCurve::methodChanged () } } } + + +void ToneCurve::clampOOGChanged() +{ + if (listener) { + listener->panelChanged(EvClampOOG, clampOOG->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + + void ToneCurve::setRaw (bool raw) { diff --git a/rtgui/tonecurve.h b/rtgui/tonecurve.h index e85fefa39..7d4440e2c 100644 --- a/rtgui/tonecurve.h +++ b/rtgui/tonecurve.h @@ -58,6 +58,7 @@ protected: MyComboBoxText* toneCurveMode; MyComboBoxText* toneCurveMode2; Gtk::ToggleButton *histmatching; + Gtk::CheckButton *clampOOG; bool clipDirty, lastAuto; sigc::connection autoconn, neutralconn, tcmodeconn, tcmode2conn; @@ -69,6 +70,7 @@ protected: rtengine::ProcEvent EvHistMatching; rtengine::ProcEvent EvHistMatchingBatch; + rtengine::ProcEvent EvClampOOG; // used temporarily in eventing double nextExpcomp; @@ -124,6 +126,7 @@ public: void hrenabledChanged (); void methodChanged (); + void clampOOGChanged(); }; #endif