diff --git a/rtdata/languages/default b/rtdata/languages/default index 4ac12a093..963659884 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -751,6 +751,7 @@ HISTORY_MSG_RAWCACORR_AUTOIT;Raw CA Correction - Iterations HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw CA Correction - Avoid color shift HISTORY_MSG_RAW_BORDER;Raw border HISTORY_MSG_RESIZE_ALLOWUPSCALING;Resize - Allow upscaling +HISTORY_MSG_SH_COLORSPACE;S/H - Colorspace HISTORY_MSG_SHARPENING_CONTRAST;Sharpening - Contrast threshold HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength diff --git a/rtengine/ipshadowshighlights.cc b/rtengine/ipshadowshighlights.cc index 073be47bd..ff56c84ac 100644 --- a/rtengine/ipshadowshighlights.cc +++ b/rtengine/ipshadowshighlights.cc @@ -34,11 +34,12 @@ void ImProcFunctions::shadowsHighlights(LabImage *lab) const int width = lab->W; const int height = lab->H; + const bool lab_mode = params->sh.lab; array2D mask(width, height); array2D L(width, height); - const float radius = float(params->sh.radius) * 10 / scale; - LUTf f(65536); + const float radius = float(params->sh.radius) * 10 / scale; + LUTf f(lab_mode ? 32768 : 65536); TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); TMatrix iws = ICCStore::getInstance()->workingSpaceInverseMatrix(params->icm.workingProfile); @@ -98,32 +99,54 @@ void ImProcFunctions::shadowsHighlights(LabImage *lab) }); if(!hl) { + if (lab_mode) { #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel for if (multiThread) #endif - for (int c = 0; c < 65536; ++c) { - float l, a, b; - float R = c, G = c, B = c; - rgb2lab(R, G, B, l, a, b); - auto base = pow_F(l / 32768.f, gamma); - // get a bit more contrast in the shadows - base = sh_contrast.getVal(base); - l = base * 32768.f; - lab2rgb(l, a, b, R, G, B); - f[c] = G; + for (int l = 0; l < 32768; ++l) { + auto base = pow_F(l / 32768.f, gamma); + // get a bit more contrast in the shadows + base = sh_contrast.getVal(base); + f[l] = base * 32768.f; + } + } else { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int c = 0; c < 65536; ++c) { + float l, a, b; + float R = c, G = c, B = c; + rgb2lab(R, G, B, l, a, b); + auto base = pow_F(l / 32768.f, gamma); + // get a bit more contrast in the shadows + base = sh_contrast.getVal(base); + l = base * 32768.f; + lab2rgb(l, a, b, R, G, B); + f[c] = G; + } } } else { + if (lab_mode) { #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel for if (multiThread) #endif - for (int c = 0; c < 65536; ++c) { - float l, a, b; - float R = c, G = c, B = c; - rgb2lab(R, G, B, l, a, b); - auto base = pow_F(l / 32768.f, gamma); - l = base * 32768.f; - lab2rgb(l, a, b, R, G, B); - f[c] = G; + for (int l = 0; l < 32768; ++l) { + auto base = pow_F(l / 32768.f, gamma); + f[l] = base * 32768.f; + } + } else { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int c = 0; c < 65536; ++c) { + float l, a, b; + float R = c, G = c, B = c; + rgb2lab(R, G, B, l, a, b); + auto base = pow_F(l / 32768.f, gamma); + l = base * 32768.f; + lab2rgb(l, a, b, R, G, B); + f[c] = G; + } } } @@ -132,15 +155,28 @@ void ImProcFunctions::shadowsHighlights(LabImage *lab) #endif for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { + float l = lab->L[y][x]; float blend = LIM01(mask[y][x]); float orig = 1.f - blend; - if (lab->L[y][x] >= 0.f && lab->L[y][x] < 32768.f) { - float rgb[3]; - lab2rgb(lab->L[y][x], lab->a[y][x], lab->b[y][x], rgb[0], rgb[1], rgb[2]); - for (int i = 0; i < 3; ++i) { - rgb[i] = f[rgb[i]] * blend + rgb[i] * orig; + if (l >= 0.f && l < 32768.f) { + if (lab_mode) { + lab->L[y][x] = f[l] * blend + l * orig; + if (!hl && l > 1.f) { + // when pushing shadows, scale also the chromaticity + float s = max(lab->L[y][x] / l * 0.5f, 1.f) * blend; + float a = lab->a[y][x]; + float b = lab->b[y][x]; + lab->a[y][x] = a * s + a * orig; + lab->b[y][x] = b * s + b * orig; + } + } else { + float rgb[3]; + lab2rgb(l, lab->a[y][x], lab->b[y][x], rgb[0], rgb[1], rgb[2]); + for (int i = 0; i < 3; ++i) { + rgb[i] = f[rgb[i]] * blend + rgb[i] * orig; + } + rgb2lab(rgb[0], rgb[1], rgb[2], lab->L[y][x], lab->a[y][x], lab->b[y][x]); } - rgb2lab(rgb[0], rgb[1], rgb[2], lab->L[y][x], lab->a[y][x], lab->b[y][x]); } } } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index aebc8c047..4d0c7aed0 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1501,7 +1501,8 @@ SHParams::SHParams() : htonalwidth(70), shadows(0), stonalwidth(30), - radius(40) + radius(40), + lab(false) { } @@ -1513,7 +1514,8 @@ bool SHParams::operator ==(const SHParams& other) const && htonalwidth == other.htonalwidth && shadows == other.shadows && stonalwidth == other.stonalwidth - && radius == other.radius; + && radius == other.radius + && lab == other.lab; } bool SHParams::operator !=(const SHParams& other) const @@ -3088,6 +3090,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->sh.shadows, "Shadows & Highlights", "Shadows", sh.shadows, keyFile); saveToKeyfile(!pedited || pedited->sh.stonalwidth, "Shadows & Highlights", "ShadowTonalWidth", sh.stonalwidth, keyFile); saveToKeyfile(!pedited || pedited->sh.radius, "Shadows & Highlights", "Radius", sh.radius, keyFile); + saveToKeyfile(!pedited || pedited->sh.lab, "Shadows & Highlights", "Lab", sh.lab, keyFile); // Crop saveToKeyfile(!pedited || pedited->crop.enabled, "Crop", "Enabled", crop.enabled, keyFile); @@ -4020,6 +4023,11 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Shadows & Highlights", "Shadows", pedited, sh.shadows, pedited->sh.shadows); assignFromKeyfile(keyFile, "Shadows & Highlights", "ShadowTonalWidth", pedited, sh.stonalwidth, pedited->sh.stonalwidth); assignFromKeyfile(keyFile, "Shadows & Highlights", "Radius", pedited, sh.radius, pedited->sh.radius); + if (ppVersion >= 344) { + assignFromKeyfile(keyFile, "Shadows & Highlights", "Lab", pedited, sh.lab, pedited->sh.lab); + } else { + sh.lab = true; + } if (keyFile.has_key("Shadows & Highlights", "LocalContrast") && ppVersion < 329) { int lc = keyFile.get_integer("Shadows & Highlights", "LocalContrast"); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 0b8b5ba56..700c6271c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -749,6 +749,7 @@ struct SHParams { int shadows; int stonalwidth; int radius; + bool lab; SHParams(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 2ccb62f65..342d9adf9 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -292,6 +292,7 @@ void ParamsEdited::set(bool v) sh.shadows = v; sh.stonalwidth = v; sh.radius = v; + sh.lab = v; crop.enabled = v; crop.x = v; crop.y = v; @@ -851,6 +852,7 @@ void ParamsEdited::initFrom(const std::vector& sh.shadows = sh.shadows && p.sh.shadows == other.sh.shadows; sh.stonalwidth = sh.stonalwidth && p.sh.stonalwidth == other.sh.stonalwidth; sh.radius = sh.radius && p.sh.radius == other.sh.radius; + sh.lab = sh.lab && p.sh.lab == other.sh.lab; crop.enabled = crop.enabled && p.crop.enabled == other.crop.enabled; crop.x = crop.x && p.crop.x == other.crop.x; crop.y = crop.y && p.crop.y == other.crop.y; @@ -2116,6 +2118,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.sh.radius = mods.sh.radius; } + if (sh.lab) { + toEdit.sh.lab = mods.sh.lab; + } + if (crop.enabled) { toEdit.crop.enabled = mods.crop.enabled; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 0a795696c..177b05d85 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -402,12 +402,12 @@ class SHParamsEdited public: bool enabled; - bool hq; bool highlights; bool htonalwidth; bool shadows; bool stonalwidth; bool radius; + bool lab; }; class CropParamsEdited diff --git a/rtgui/ppversion.h b/rtgui/ppversion.h index 050c4c653..19e8c995d 100644 --- a/rtgui/ppversion.h +++ b/rtgui/ppversion.h @@ -1,11 +1,13 @@ #pragma once // This number has to be incremented whenever the PP3 file format is modified or the behaviour of a tool changes -#define PPVERSION 343 +#define PPVERSION 344 #define PPVERSION_AEXP 301 //value of PPVERSION when auto exposure algorithm was modified /* Log of version changes + 344 2018-10-04 + added Lab/RGB color space selection for shadows/highlights 343 2018-09-06 raw auto ca correction avoid colour shift 342 2018-09-05 diff --git a/rtgui/shadowshighlights.cc b/rtgui/shadowshighlights.cc index f1d8dbaf2..526ee9aae 100644 --- a/rtgui/shadowshighlights.cc +++ b/rtgui/shadowshighlights.cc @@ -17,12 +17,26 @@ * along with RawTherapee. If not, see . */ #include "shadowshighlights.h" +#include "eventmapper.h" using namespace rtengine; using namespace rtengine::procparams; ShadowsHighlights::ShadowsHighlights () : FoldableToolPanel(this, "shadowshighlights", M("TP_SHADOWSHLIGHTS_LABEL"), false, true) { + auto m = ProcEventMapper::getInstance(); + EvSHColorspace = m->newEvent(RGBCURVE, "HISTORY_MSG_SH_COLORSPACE"); + + Gtk::HBox* hb = Gtk::manage (new Gtk::HBox ()); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("TP_DIRPYRDENOISE_MAIN_COLORSPACE") + ": ")), Gtk::PACK_SHRINK); + colorspace = Gtk::manage(new MyComboBoxText()); + colorspace->append(M("TP_DIRPYRDENOISE_MAIN_COLORSPACE_RGB")); + colorspace->append(M("TP_DIRPYRDENOISE_MAIN_COLORSPACE_LAB")); + hb->pack_start(*colorspace); + pack_start(*hb); + + pack_start (*Gtk::manage (new Gtk::HSeparator())); + highlights = Gtk::manage (new Adjuster (M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), 0, 100, 1, 0)); h_tonalwidth = Gtk::manage (new Adjuster (M("TP_SHADOWSHLIGHTS_HLTONALW"), 10, 100, 1, 70)); pack_start (*highlights); @@ -46,6 +60,8 @@ ShadowsHighlights::ShadowsHighlights () : FoldableToolPanel(this, "shadowshighli shadows->setAdjusterListener (this); s_tonalwidth->setAdjusterListener (this); + colorspace->signal_changed().connect(sigc::mem_fun(*this, &ShadowsHighlights::colorspaceChanged)); + show_all_children (); } @@ -61,6 +77,7 @@ void ShadowsHighlights::read (const ProcParams* pp, const ParamsEdited* pedited) shadows->setEditedState (pedited->sh.shadows ? Edited : UnEdited); s_tonalwidth->setEditedState (pedited->sh.stonalwidth ? Edited : UnEdited); set_inconsistent (multiImage && !pedited->sh.enabled); + } setEnabled (pp->sh.enabled); @@ -71,6 +88,14 @@ void ShadowsHighlights::read (const ProcParams* pp, const ParamsEdited* pedited) shadows->setValue (pp->sh.shadows); s_tonalwidth->setValue (pp->sh.stonalwidth); + if (pedited && !pedited->sh.lab) { + colorspace->set_active(2); + } else if (pp->sh.lab) { + colorspace->set_active(1); + } else { + colorspace->set_active(0); + } + enableListener (); } @@ -84,6 +109,12 @@ void ShadowsHighlights::write (ProcParams* pp, ParamsEdited* pedited) pp->sh.stonalwidth = (int)s_tonalwidth->getValue (); pp->sh.enabled = getEnabled(); + if (colorspace->get_active_row_number() == 0) { + pp->sh.lab = false; + } else if (colorspace->get_active_row_number() == 1) { + pp->sh.lab = true; + } + if (pedited) { pedited->sh.radius = radius->getEditedState (); pedited->sh.highlights = highlights->getEditedState (); @@ -91,6 +122,7 @@ void ShadowsHighlights::write (ProcParams* pp, ParamsEdited* pedited) pedited->sh.shadows = shadows->getEditedState (); pedited->sh.stonalwidth = s_tonalwidth->getEditedState (); pedited->sh.enabled = !get_inconsistent(); + pedited->sh.lab = colorspace->get_active_row_number() != 2; } } @@ -153,6 +185,13 @@ void ShadowsHighlights::enabledChanged () } } +void ShadowsHighlights::colorspaceChanged() +{ + if (listener && (multiImage || getEnabled()) ) { + listener->panelChanged(EvSHColorspace, colorspace->get_active_text()); + } +} + void ShadowsHighlights::setBatchMode (bool batchMode) { @@ -162,6 +201,7 @@ void ShadowsHighlights::setBatchMode (bool batchMode) h_tonalwidth->showEditedCB (); shadows->showEditedCB (); s_tonalwidth->showEditedCB (); + colorspace->append(M("GENERAL_UNCHANGED")); } void ShadowsHighlights::setAdjusterBehavior (bool hadd, bool sadd) diff --git a/rtgui/shadowshighlights.h b/rtgui/shadowshighlights.h index 96642bfc0..598eece68 100644 --- a/rtgui/shadowshighlights.h +++ b/rtgui/shadowshighlights.h @@ -32,6 +32,9 @@ protected: Adjuster* shadows; Adjuster* s_tonalwidth; Adjuster* radius; + MyComboBoxText *colorspace; + + rtengine::ProcEvent EvSHColorspace; public: @@ -47,6 +50,8 @@ public: void setAdjusterBehavior (bool hadd, bool sadd); void trimValues (rtengine::procparams::ProcParams* pp); + + void colorspaceChanged(); }; #endif